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

Reply via email to