[clang-tools-extra] [clang-tidy][bugprone-unintended-char-ostream-output] add `WarnOnExplicitCast` option (PR #133639)
Congcong Cai via cfe-commits
cfe-commits at lists.llvm.org
Sun Mar 30 08:03:59 PDT 2025
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 <congcongcai0907 at 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));
+}
More information about the cfe-commits
mailing list