-
Notifications
You must be signed in to change notification settings - Fork 13.3k
[clang-tidy] treat unsigned char and signed char as char type by default in bugprone-unintended-char-ostream-output #134870
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang-tidy] treat unsigned char and signed char as char type by default in bugprone-unintended-char-ostream-output #134870
Conversation
@llvm/pr-subscribers-clang-tools-extra @llvm/pr-subscribers-clang-tidy Author: Congcong Cai (HerrCai0907) ChangesAdd Fixed: #133425 Full diff: https://github.com/llvm/llvm-project/pull/134870.diff 6 Files Affected:
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
index 7250e4ccb8c69..57e1f744fcd7d 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
@@ -7,6 +7,8 @@
//===----------------------------------------------------------------------===//
#include "UnintendedCharOstreamOutputCheck.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
#include "clang/AST/Type.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
@@ -35,10 +37,14 @@ AST_MATCHER(Type, isChar) {
UnintendedCharOstreamOutputCheck::UnintendedCharOstreamOutputCheck(
StringRef Name, ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context), CastTypeName(Options.get("CastTypeName")) {
-}
+ : ClangTidyCheck(Name, Context),
+ AllowedTypes(utils::options::parseStringList(
+ Options.get("AllowedTypes", "unsigned char;signed char"))),
+ CastTypeName(Options.get("CastTypeName")) {}
void UnintendedCharOstreamOutputCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "AllowedTypes",
+ utils::options::serializeStringList(AllowedTypes));
if (CastTypeName.has_value())
Options.store(Opts, "CastTypeName", CastTypeName.value());
}
@@ -50,13 +56,20 @@ void UnintendedCharOstreamOutputCheck::registerMatchers(MatchFinder *Finder) {
// with char / unsigned char / signed char
classTemplateSpecializationDecl(
hasTemplateArgument(0, refersToType(isChar()))));
+ auto IsDeclRefExprFromAllowedTypes = declRefExpr(to(varDecl(
+ hasType(matchers::matchesAnyListedTypeName(AllowedTypes, false)))));
+ auto IsExplicitCastExprFromAllowedTypes = explicitCastExpr(hasDestinationType(
+ matchers::matchesAnyListedTypeName(AllowedTypes, false)));
Finder->addMatcher(
cxxOperatorCallExpr(
hasOverloadedOperatorName("<<"),
hasLHS(hasType(hasUnqualifiedDesugaredType(
recordType(hasDeclaration(cxxRecordDecl(
anyOf(BasicOstream, isDerivedFrom(BasicOstream)))))))),
- hasRHS(hasType(hasUnqualifiedDesugaredType(isNumericChar()))))
+ hasRHS(expr(hasType(hasUnqualifiedDesugaredType(isNumericChar())),
+ unless(ignoringParenImpCasts(
+ anyOf(IsDeclRefExprFromAllowedTypes,
+ IsExplicitCastExprFromAllowedTypes))))))
.bind("x"),
this);
}
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.h b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.h
index 61ea623d139ea..0759e3d1eb460 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.h
@@ -30,6 +30,7 @@ class UnintendedCharOstreamOutputCheck : public ClangTidyCheck {
}
private:
+ const std::vector<StringRef> AllowedTypes;
const std::optional<StringRef> CastTypeName;
};
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/unintended-char-ostream-output.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/unintended-char-ostream-output.rst
index 95d02b3e2ddda..9ad08188d7fb2 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/unintended-char-ostream-output.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/unintended-char-ostream-output.rst
@@ -42,6 +42,14 @@ Or cast to char to explicitly indicate that output should be a character.
Options
-------
+.. option:: AllowedTypes
+
+ A semicolon-separated list of type names that will be treated as ``char``
+ type. It only contains the non canonical type names without the alias of type
+ names. Explicit casting to these types or use the variable defined with these
+ types will be ignored.
+ Default is `unsigned char;signed char`.
+
.. option:: CastTypeName
When `CastTypeName` is specified, the fix-it will use `CastTypeName` as the
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output-allowed-types.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output-allowed-types.cpp
new file mode 100644
index 0000000000000..11dc207dfb0c3
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output-allowed-types.cpp
@@ -0,0 +1,41 @@
+// RUN: %check_clang_tidy %s bugprone-unintended-char-ostream-output %t -- \
+// RUN: -config="{CheckOptions: \
+// RUN: {bugprone-unintended-char-ostream-output.AllowedTypes: \"\"}}"
+
+namespace std {
+
+template <class _CharT, class _Traits = void> class basic_ostream {
+public:
+ basic_ostream &operator<<(int);
+ basic_ostream &operator<<(unsigned int);
+};
+
+template <class CharT, class Traits>
+basic_ostream<CharT, Traits> &operator<<(basic_ostream<CharT, Traits> &, CharT);
+template <class CharT, class Traits>
+basic_ostream<CharT, Traits> &operator<<(basic_ostream<CharT, Traits> &, char);
+template <class _Traits>
+basic_ostream<char, _Traits> &operator<<(basic_ostream<char, _Traits> &, char);
+template <class _Traits>
+basic_ostream<char, _Traits> &operator<<(basic_ostream<char, _Traits> &,
+ signed char);
+template <class _Traits>
+basic_ostream<char, _Traits> &operator<<(basic_ostream<char, _Traits> &,
+ unsigned char);
+
+using ostream = basic_ostream<char>;
+
+} // namespace std
+
+void origin_ostream(std::ostream &os) {
+ unsigned char unsigned_value = 9;
+ os << unsigned_value;
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'unsigned char' passed to 'operator<<' outputs as character instead of integer
+
+ signed char signed_value = 9;
+ os << signed_value;
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'signed char' passed to 'operator<<' outputs as character instead of integer
+
+ char char_value = 9;
+ os << char_value;
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output-cast-type.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output-cast-type.cpp
index 72020d90e0369..f3c72dac654ad 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output-cast-type.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output-cast-type.cpp
@@ -27,17 +27,18 @@ using ostream = basic_ostream<char>;
} // namespace std
-class A : public std::ostream {};
+using uint8_t = unsigned char;
+using int8_t = signed char;
void origin_ostream(std::ostream &os) {
- unsigned char unsigned_value = 9;
+ uint8_t unsigned_value = 9;
os << unsigned_value;
- // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'unsigned char' passed to 'operator<<' outputs as character instead of integer
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'uint8_t' (aka 'unsigned char') passed to 'operator<<' outputs as character instead of integer
// CHECK-FIXES: os << static_cast<unsigned char>(unsigned_value);
- signed char signed_value = 9;
+ int8_t signed_value = 9;
os << signed_value;
- // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'signed char' passed to 'operator<<' outputs as character instead of integer
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'int8_t' (aka 'signed char') passed to 'operator<<' outputs as character instead of integer
// CHECK-FIXES: os << static_cast<unsigned char>(signed_value);
char char_value = 9;
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output.cpp
index 573c429bf049f..b458e55b7abc4 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output.cpp
@@ -27,41 +27,56 @@ using ostream = basic_ostream<char>;
class A : public std::ostream {};
+using uint8_t = unsigned char;
+using int8_t = signed char;
+
void origin_ostream(std::ostream &os) {
- unsigned char unsigned_value = 9;
+ uint8_t unsigned_value = 9;
os << unsigned_value;
- // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'unsigned char' passed to 'operator<<' outputs as character instead of integer
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'uint8_t' (aka 'unsigned char') passed to 'operator<<' outputs as character instead of integer
// CHECK-FIXES: os << static_cast<unsigned int>(unsigned_value);
- signed char signed_value = 9;
+ int8_t signed_value = 9;
os << signed_value;
- // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'signed char' passed to 'operator<<' outputs as character instead of integer
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'int8_t' (aka 'signed char') passed to 'operator<<' outputs as character instead of integer
// CHECK-FIXES: os << static_cast<int>(signed_value);
char char_value = 9;
os << char_value;
+ unsigned char unsigned_char_value = 9;
+ os << unsigned_char_value;
+ signed char signed_char_value = 9;
+ os << signed_char_value;
+}
+
+void explicit_cast_to_char_type(std::ostream &os) {
+ enum V : uint8_t {};
+ V e{};
+ os << static_cast<unsigned char>(e);
+ os << (unsigned char)(e);
+ os << (static_cast<unsigned char>(e));
}
void based_on_ostream(A &os) {
- unsigned char unsigned_value = 9;
+ uint8_t unsigned_value = 9;
os << unsigned_value;
- // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'unsigned char' passed to 'operator<<' outputs as character instead of integer
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'uint8_t' (aka 'unsigned char') passed to 'operator<<' outputs as character instead of integer
// CHECK-FIXES: os << static_cast<unsigned int>(unsigned_value);
- signed char signed_value = 9;
+ int8_t signed_value = 9;
os << signed_value;
- // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'signed char' passed to 'operator<<' outputs as character instead of integer
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'int8_t' (aka 'signed char') passed to 'operator<<' outputs as character instead of integer
// CHECK-FIXES: os << static_cast<int>(signed_value);
char char_value = 9;
os << char_value;
}
-void other_ostream_template_parameters(std::basic_ostream<unsigned char> &os) {
- unsigned char unsigned_value = 9;
+void other_ostream_template_parameters(std::basic_ostream<uint8_t> &os) {
+ uint8_t unsigned_value = 9;
os << unsigned_value;
- signed char signed_value = 9;
+ int8_t signed_value = 9;
os << signed_value;
char char_value = 9;
@@ -70,23 +85,22 @@ void other_ostream_template_parameters(std::basic_ostream<unsigned char> &os) {
template <class T> class B : public std::ostream {};
void template_based_on_ostream(B<int> &os) {
- unsigned char unsigned_value = 9;
+ uint8_t unsigned_value = 9;
os << unsigned_value;
- // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'unsigned char' passed to 'operator<<' outputs as character instead of integer
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'uint8_t' (aka 'unsigned char') passed to 'operator<<' outputs as character instead of integer
// CHECK-FIXES: os << static_cast<unsigned int>(unsigned_value);
}
template<class T> void template_fn_1(T &os) {
- unsigned char unsigned_value = 9;
+ uint8_t unsigned_value = 9;
os << unsigned_value;
- // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'unsigned char' passed to 'operator<<' outputs as character instead of integer
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'uint8_t' (aka 'unsigned char') passed to 'operator<<' outputs as character instead of integer
// CHECK-FIXES: os << static_cast<unsigned int>(unsigned_value);
}
template<class T> void template_fn_2(std::ostream &os) {
T unsigned_value = 9;
os << unsigned_value;
- // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'unsigned char' passed to 'operator<<' outputs as character instead of integer
- // CHECK-FIXES: os << static_cast<unsigned int>(unsigned_value);
+ // It should be detected as well. But we cannot get the sugared type name for SubstTemplateTypeParmType.
}
template<class T> void template_fn_3(std::ostream &os) {
T unsigned_value = 9;
@@ -95,26 +109,10 @@ template<class T> void template_fn_3(std::ostream &os) {
void call_template_fn() {
A a{};
template_fn_1(a);
- template_fn_2<unsigned char>(a);
+ template_fn_2<uint8_t>(a);
template_fn_3<char>(a);
}
-using U8 = unsigned char;
-void alias_unsigned_char(std::ostream &os) {
- U8 v = 9;
- os << v;
- // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'U8' (aka 'unsigned char') passed to 'operator<<' outputs as character instead of integer
- // CHECK-FIXES: os << static_cast<unsigned int>(v);
-}
-
-using I8 = signed char;
-void alias_signed_char(std::ostream &os) {
- I8 v = 9;
- os << v;
- // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'I8' (aka 'signed char') passed to 'operator<<' outputs as character instead of integer
- // CHECK-FIXES: os << static_cast<int>(v);
-}
-
using C8 = char;
void alias_char(std::ostream &os) {
C8 v = 9;
@@ -124,8 +122,8 @@ void alias_char(std::ostream &os) {
#define MACRO_VARIANT_NAME a
void macro_variant_name(std::ostream &os) {
- unsigned char MACRO_VARIANT_NAME = 9;
+ uint8_t MACRO_VARIANT_NAME = 9;
os << MACRO_VARIANT_NAME;
- // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'unsigned char' passed to 'operator<<' outputs as character instead of integer
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'uint8_t' (aka 'unsigned char') passed to 'operator<<' outputs as character instead of integer
// CHECK-FIXES: os << static_cast<unsigned int>(MACRO_VARIANT_NAME);
}
|
164409d
to
27b7028
Compare
f860128
to
9d29fd3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good to me, I have one suggestion about the documentation.
clang-tools-extra/docs/clang-tidy/checks/bugprone/unintended-char-ostream-output.rst
Outdated
Show resolved
Hide resolved
833d092
to
104d0cc
Compare
…ult in bugprone-unintended-char-ostream-output Add `AllowedTypes` options to support custom defined char like type. treat `unsigned char` and `signed char` as char like type by default. The allowed types only effect when the var decl or explicit cast to this non-canonical type names.
ef9322a
to
b9b424a
Compare
…ult in bugprone-unintended-char-ostream-output (llvm#134870) Add `AllowedTypes` options to support custom defined char like type. treat `unsigned char` and `signed char` as char like type by default. The allowed types only effect when the var decl or explicit cast to this non-canonical type names. Fixed: llvm#133425
Add
AllowedTypes
options to support custom defined char like type.treat
unsigned char
andsigned char
as char like type by default.The allowed types only effect when the var decl or explicit cast to this
non-canonical type names.
Fixed: #133425