Skip to content

Commit 82cb4d0

Browse files
committed
add option
1 parent bc43f1f commit 82cb4d0

File tree

4 files changed

+76
-14
lines changed

4 files changed

+76
-14
lines changed

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ AST_MATCHER(Type, isChar) {
3333

3434
} // namespace
3535

36+
UnintendedCharOstreamOutputCheck::UnintendedCharOstreamOutputCheck(
37+
StringRef Name, ClangTidyContext *Context)
38+
: ClangTidyCheck(Name, Context), CastTypeName(Options.get("CastTypeName")) {
39+
}
40+
void UnintendedCharOstreamOutputCheck::storeOptions(
41+
ClangTidyOptions::OptionMap &Opts) {
42+
if (CastTypeName.has_value())
43+
Options.store(Opts, "CastTypeName", CastTypeName.value());
44+
}
45+
3646
void UnintendedCharOstreamOutputCheck::registerMatchers(MatchFinder *Finder) {
3747
auto BasicOstream =
3848
cxxRecordDecl(hasName("::std::basic_ostream"),
@@ -60,19 +70,17 @@ void UnintendedCharOstreamOutputCheck::check(
6070
DiagnosticBuilder Builder =
6171
diag(Call->getOperatorLoc(),
6272
"%0 passed to 'operator<<' outputs as character instead of integer. "
63-
"cast to 'unsigned' to print numeric value or cast to 'char' to "
64-
"print "
65-
"as character")
73+
"cast to 'unsigned int' to print numeric value or cast to 'char' to "
74+
"print as character")
6675
<< Value->getType() << SourceRange;
6776

6877
QualType T = Value->getType();
6978
const Type *UnqualifiedDesugaredType = T->getUnqualifiedDesugaredType();
7079

71-
llvm::StringRef CastType;
72-
if (UnqualifiedDesugaredType->isSpecificBuiltinType(BuiltinType::SChar))
73-
CastType = "int";
74-
else
75-
CastType = "unsigned int";
80+
llvm::StringRef CastType = CastTypeName.value_or(
81+
UnqualifiedDesugaredType->isSpecificBuiltinType(BuiltinType::SChar)
82+
? "int"
83+
: "unsigned int");
7684

7785
Builder << FixItHint::CreateReplacement(
7886
SourceRange, ("static_cast<" + CastType + ">(" +

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNINTENDEDCHAROSTREAMOUTPUTCHECK_H
1111

1212
#include "../ClangTidyCheck.h"
13+
#include <optional>
1314

1415
namespace clang::tidy::bugprone {
1516

@@ -20,13 +21,16 @@ namespace clang::tidy::bugprone {
2021
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/unintended-char-ostream-output.html
2122
class UnintendedCharOstreamOutputCheck : public ClangTidyCheck {
2223
public:
23-
UnintendedCharOstreamOutputCheck(StringRef Name, ClangTidyContext *Context)
24-
: ClangTidyCheck(Name, Context) {}
24+
UnintendedCharOstreamOutputCheck(StringRef Name, ClangTidyContext *Context);
25+
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
2526
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
2627
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
2728
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
2829
return LangOpts.CPlusPlus;
2930
}
31+
32+
private:
33+
const std::optional<StringRef> CastTypeName;
3034
};
3135

3236
} // namespace clang::tidy::bugprone

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,22 @@ integer values.
1414

1515
.. code-block:: c++
1616

17-
uint8_t v = 65;
18-
std::cout << v; // output 'A' instead of '65'
17+
uint8_t v = 65;
18+
std::cout << v; // output 'A' instead of '65'
1919

2020
It could be fixed as
2121

2222
.. code-block:: c++
2323

24-
std::cout << static_cast<uint32_t>(v);
24+
std::cout << static_cast<uint32_t>(v);
2525

2626
Or cast to char to explicitly indicate the intent
2727

2828
.. code-block:: c++
2929

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

0 commit comments

Comments
 (0)