Skip to content

Commit c7f63c4

Browse files
committed
[clang-tidy][bugprone-unintended-char-ostream-output] add WarnOnExplicitCast option
Fixes: #133425. Add `WarnOnExplicitCast` to accept explicit cast to unsigned char and signed char.
1 parent 4dbcefe commit c7f63c4

File tree

4 files changed

+60
-3
lines changed

4 files changed

+60
-3
lines changed

clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@ AST_MATCHER(Type, isChar) {
3535

3636
UnintendedCharOstreamOutputCheck::UnintendedCharOstreamOutputCheck(
3737
StringRef Name, ClangTidyContext *Context)
38-
: ClangTidyCheck(Name, Context), CastTypeName(Options.get("CastTypeName")) {
39-
}
38+
: ClangTidyCheck(Name, Context), CastTypeName(Options.get("CastTypeName")),
39+
WarnOnExplicitCast(Options.get("WarnOnExplicitCast", true)) {}
4040
void UnintendedCharOstreamOutputCheck::storeOptions(
4141
ClangTidyOptions::OptionMap &Opts) {
4242
if (CastTypeName.has_value())
4343
Options.store(Opts, "CastTypeName", CastTypeName.value());
44+
Options.store(Opts, "WarnOnExplicitCast", WarnOnExplicitCast);
4445
}
4546

4647
void UnintendedCharOstreamOutputCheck::registerMatchers(MatchFinder *Finder) {
@@ -50,13 +51,17 @@ void UnintendedCharOstreamOutputCheck::registerMatchers(MatchFinder *Finder) {
5051
// with char / unsigned char / signed char
5152
classTemplateSpecializationDecl(
5253
hasTemplateArgument(0, refersToType(isChar()))));
54+
auto IsNumbericCharType =
55+
hasType(hasUnqualifiedDesugaredType(isNumericChar()));
5356
Finder->addMatcher(
5457
cxxOperatorCallExpr(
5558
hasOverloadedOperatorName("<<"),
5659
hasLHS(hasType(hasUnqualifiedDesugaredType(
5760
recordType(hasDeclaration(cxxRecordDecl(
5861
anyOf(BasicOstream, isDerivedFrom(BasicOstream)))))))),
59-
hasRHS(hasType(hasUnqualifiedDesugaredType(isNumericChar()))))
62+
hasRHS(WarnOnExplicitCast
63+
? expr(IsNumbericCharType)
64+
: expr(IsNumbericCharType, unless(explicitCastExpr()))))
6065
.bind("x"),
6166
this);
6267
}

clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class UnintendedCharOstreamOutputCheck : public ClangTidyCheck {
3131

3232
private:
3333
const std::optional<StringRef> CastTypeName;
34+
const bool WarnOnExplicitCast;
3435
};
3536

3637
} // namespace clang::tidy::bugprone

clang-tools-extra/docs/clang-tidy/checks/bugprone/unintended-char-ostream-output.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ Or cast to char to explicitly indicate that output should be a character.
3939

4040
std::cout << static_cast<char>(v);
4141

42+
Options
43+
-------
44+
45+
.. option:: WarnOnExplicitCast
46+
47+
When `WarnOnExplicitCast` is set to `false`, the check will not warn when
48+
output of ostream is explicitly cast to a ``unsigned char`` or ``signed char``.
49+
Attention: Explicit casting cannot solve the any problem if the value is not
50+
character.
51+
Default is `true`.
52+
4253
.. option:: CastTypeName
4354

4455
When `CastTypeName` is specified, the fix-it will use `CastTypeName` as the
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %check_clang_tidy %s bugprone-unintended-char-ostream-output %t -check-suffix=WARN-EXPLICIT-CAST
2+
// RUN: %check_clang_tidy %s bugprone-unintended-char-ostream-output %t \
3+
// RUN: -config='{CheckOptions: { \
4+
// RUN: bugprone-unintended-char-ostream-output.WarnOnExplicitCast: false, \
5+
// RUN: }}' -check-suffix=IGNORE-EXPLICIT-CAST --
6+
7+
namespace std {
8+
9+
template <class _CharT, class _Traits = void> class basic_ostream {
10+
public:
11+
basic_ostream &operator<<(int);
12+
basic_ostream &operator<<(unsigned int);
13+
};
14+
15+
template <class CharT, class Traits>
16+
basic_ostream<CharT, Traits> &operator<<(basic_ostream<CharT, Traits> &, CharT);
17+
template <class CharT, class Traits>
18+
basic_ostream<CharT, Traits> &operator<<(basic_ostream<CharT, Traits> &, char);
19+
template <class _Traits>
20+
basic_ostream<char, _Traits> &operator<<(basic_ostream<char, _Traits> &, char);
21+
template <class _Traits>
22+
basic_ostream<char, _Traits> &operator<<(basic_ostream<char, _Traits> &,
23+
signed char);
24+
template <class _Traits>
25+
basic_ostream<char, _Traits> &operator<<(basic_ostream<char, _Traits> &,
26+
unsigned char);
27+
28+
using ostream = basic_ostream<char>;
29+
30+
} // namespace std
31+
32+
void gh133425(std::ostream os) {
33+
int v = 10;
34+
os << static_cast<unsigned char>(v);
35+
// CHECK-MESSAGES-WARN-EXPLICIT-CAST: [[@LINE-1]]:6: warning: 'unsigned char' passed to 'operator<<' outputs as character instead of integer
36+
// CHECK-FIXES-WARN-EXPLICIT-CAST: os << static_cast<unsigned int>(static_cast<unsigned char>(v));
37+
os << (unsigned char)(v);
38+
// CHECK-MESSAGES-WARN-EXPLICIT-CAST: [[@LINE-1]]:6: warning: 'unsigned char' passed to 'operator<<' outputs as character instead of integer
39+
// CHECK-FIXES-WARN-EXPLICIT-CAST: os << static_cast<unsigned int>((unsigned char)(v));
40+
}

0 commit comments

Comments
 (0)