https://github.com/HerrCai0907 created https://github.com/llvm/llvm-project/pull/133639
Fixes: #133425. Add `WarnOnExplicitCast` to accept explicit cast to unsigned char and signed char. >From c7f63c4d221055c375d363785277c2f8a6522284 Mon Sep 17 00:00:00 2001 From: Congcong Cai <congcongcai0...@163.com> Date: Sun, 30 Mar 2025 14:57:05 +0000 Subject: [PATCH] [clang-tidy][bugprone-unintended-char-ostream-output] add `WarnOnExplicitCast` option Fixes: #133425. Add `WarnOnExplicitCast` to accept explicit cast to unsigned char and signed char. --- .../UnintendedCharOstreamOutputCheck.cpp | 11 +++-- .../UnintendedCharOstreamOutputCheck.h | 1 + .../unintended-char-ostream-output.rst | 11 +++++ ...nded-char-ostream-output-explicit-cast.cpp | 40 +++++++++++++++++++ 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output-explicit-cast.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp index 7250e4ccb8c69..b6fbb8bd0ffe4 100644 --- a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp @@ -35,12 +35,13 @@ AST_MATCHER(Type, isChar) { UnintendedCharOstreamOutputCheck::UnintendedCharOstreamOutputCheck( StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), CastTypeName(Options.get("CastTypeName")) { -} + : ClangTidyCheck(Name, Context), CastTypeName(Options.get("CastTypeName")), + WarnOnExplicitCast(Options.get("WarnOnExplicitCast", true)) {} void UnintendedCharOstreamOutputCheck::storeOptions( ClangTidyOptions::OptionMap &Opts) { if (CastTypeName.has_value()) Options.store(Opts, "CastTypeName", CastTypeName.value()); + Options.store(Opts, "WarnOnExplicitCast", WarnOnExplicitCast); } void UnintendedCharOstreamOutputCheck::registerMatchers(MatchFinder *Finder) { @@ -50,13 +51,17 @@ void UnintendedCharOstreamOutputCheck::registerMatchers(MatchFinder *Finder) { // with char / unsigned char / signed char classTemplateSpecializationDecl( hasTemplateArgument(0, refersToType(isChar())))); + auto IsNumbericCharType = + hasType(hasUnqualifiedDesugaredType(isNumericChar())); Finder->addMatcher( cxxOperatorCallExpr( hasOverloadedOperatorName("<<"), hasLHS(hasType(hasUnqualifiedDesugaredType( recordType(hasDeclaration(cxxRecordDecl( anyOf(BasicOstream, isDerivedFrom(BasicOstream)))))))), - hasRHS(hasType(hasUnqualifiedDesugaredType(isNumericChar())))) + hasRHS(WarnOnExplicitCast + ? expr(IsNumbericCharType) + : expr(IsNumbericCharType, unless(explicitCastExpr())))) .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..2e1859bbe21a7 100644 --- a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.h @@ -31,6 +31,7 @@ class UnintendedCharOstreamOutputCheck : public ClangTidyCheck { private: const std::optional<StringRef> CastTypeName; + const bool WarnOnExplicitCast; }; } // namespace clang::tidy::bugprone 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 ea1051847129b..a6196ae8c2448 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 @@ -39,6 +39,17 @@ Or cast to char to explicitly indicate that output should be a character. std::cout << static_cast<char>(v); +Options +------- + +.. option:: WarnOnExplicitCast + + When `WarnOnExplicitCast` is set to `false`, the check will not warn when + output of ostream is explicitly cast to a ``unsigned char`` or ``signed char``. + Attention: Explicit casting cannot solve the any problem if the value is not + character. + Default is `true`. + .. 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-explicit-cast.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output-explicit-cast.cpp new file mode 100644 index 0000000000000..9722fae39f129 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output-explicit-cast.cpp @@ -0,0 +1,40 @@ +// RUN: %check_clang_tidy %s bugprone-unintended-char-ostream-output %t -check-suffix=WARN-EXPLICIT-CAST +// RUN: %check_clang_tidy %s bugprone-unintended-char-ostream-output %t \ +// RUN: -config='{CheckOptions: { \ +// RUN: bugprone-unintended-char-ostream-output.WarnOnExplicitCast: false, \ +// RUN: }}' -check-suffix=IGNORE-EXPLICIT-CAST -- + +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 gh133425(std::ostream os) { + int v = 10; + os << static_cast<unsigned char>(v); + // CHECK-MESSAGES-WARN-EXPLICIT-CAST: [[@LINE-1]]:6: warning: 'unsigned char' passed to 'operator<<' outputs as character instead of integer + // CHECK-FIXES-WARN-EXPLICIT-CAST: os << static_cast<unsigned int>(static_cast<unsigned char>(v)); + os << (unsigned char)(v); + // CHECK-MESSAGES-WARN-EXPLICIT-CAST: [[@LINE-1]]:6: warning: 'unsigned char' passed to 'operator<<' outputs as character instead of integer + // CHECK-FIXES-WARN-EXPLICIT-CAST: os << static_cast<unsigned int>((unsigned char)(v)); +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits