[clang-tools-extra] [clang-tidy] Add new check bugprone-unintended-char-ostream-output (PR #127720)
Congcong Cai via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 24 06:57:05 PST 2025
https://github.com/HerrCai0907 updated https://github.com/llvm/llvm-project/pull/127720
>From b69bb465a24f2175f2f9f91f220252d3bcb27bde Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Wed, 19 Feb 2025 07:38:37 +0800
Subject: [PATCH 01/10] [clang-tidy]add new check
bugprone-unintended-char-ostream-output
It wants to find unintended character output from and to an ostream.
e.g.
uint8_t v = 9;
std::cout << v;
---
.../bugprone/BugproneTidyModule.cpp | 3 +
.../clang-tidy/bugprone/CMakeLists.txt | 1 +
.../UnintendedCharOstreamOutputCheck.cpp | 70 +++++++++++++++++++
.../UnintendedCharOstreamOutputCheck.h | 34 +++++++++
clang-tools-extra/docs/ReleaseNotes.rst | 6 ++
.../unintended-char-ostream-output.rst | 30 ++++++++
.../docs/clang-tidy/checks/list.rst | 1 +
.../unintended-char-ostream-output.cpp | 70 +++++++++++++++++++
8 files changed, 215 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/unintended-char-ostream-output.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index c5f0b5b28418f..0a3376949b6e5 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -90,6 +90,7 @@
#include "UndelegatedConstructorCheck.h"
#include "UnhandledExceptionAtNewCheck.h"
#include "UnhandledSelfAssignmentCheck.h"
+#include "UnintendedCharOstreamOutputCheck.h"
#include "UniquePtrArrayMismatchCheck.h"
#include "UnsafeFunctionsCheck.h"
#include "UnusedLocalNonTrivialVariableCheck.h"
@@ -147,6 +148,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-incorrect-enable-if");
CheckFactories.registerCheck<IncorrectEnableSharedFromThisCheck>(
"bugprone-incorrect-enable-shared-from-this");
+ CheckFactories.registerCheck<UnintendedCharOstreamOutputCheck>(
+ "bugprone-unintended-char-ostream-output");
CheckFactories.registerCheck<ReturnConstRefFromParameterCheck>(
"bugprone-return-const-ref-from-parameter");
CheckFactories.registerCheck<SwitchMissingDefaultCaseCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index e8309c68b7fca..9758d7259bf65 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -29,6 +29,7 @@ add_clang_library(clangTidyBugproneModule STATIC
InaccurateEraseCheck.cpp
IncorrectEnableIfCheck.cpp
IncorrectEnableSharedFromThisCheck.cpp
+ UnintendedCharOstreamOutputCheck.cpp
ReturnConstRefFromParameterCheck.cpp
SuspiciousStringviewDataUsageCheck.cpp
SwitchMissingDefaultCaseCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
new file mode 100644
index 0000000000000..7c54ef1486b2f
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
@@ -0,0 +1,70 @@
+//===--- UnintendedCharOstreamOutputCheck.cpp - clang-tidy
+//---------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnintendedCharOstreamOutputCheck.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+namespace {
+
+// check if the type is unsigned char or signed char
+AST_MATCHER(Type, isNumericChar) {
+ const auto *BT = dyn_cast<BuiltinType>(&Node);
+ if (BT == nullptr)
+ return false;
+ const BuiltinType::Kind K = BT->getKind();
+ return K == BuiltinType::UChar || K == BuiltinType::SChar;
+}
+
+// check if the type is char
+AST_MATCHER(Type, isChar) {
+ const auto *BT = dyn_cast<BuiltinType>(&Node);
+ if (BT == nullptr)
+ return false;
+ const BuiltinType::Kind K = BT->getKind();
+ return K == BuiltinType::Char_U || K == BuiltinType::Char_S;
+}
+
+} // namespace
+
+void UnintendedCharOstreamOutputCheck::registerMatchers(MatchFinder *Finder) {
+ auto BasicOstream =
+ cxxRecordDecl(hasName("::std::basic_ostream"),
+ // only basic_ostream<char, Traits> has overload operator<<
+ // with char / unsigned char / signed char
+ classTemplateSpecializationDecl(
+ hasTemplateArgument(0, refersToType(isChar()))));
+ Finder->addMatcher(
+ cxxOperatorCallExpr(
+ hasOverloadedOperatorName("<<"),
+ hasLHS(hasType(hasUnqualifiedDesugaredType(
+ recordType(hasDeclaration(cxxRecordDecl(
+ anyOf(BasicOstream, isDerivedFrom(BasicOstream)))))))),
+ hasRHS(hasType(hasUnqualifiedDesugaredType(isNumericChar()))))
+ .bind("x"),
+ this);
+}
+
+void UnintendedCharOstreamOutputCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("x");
+ const Expr *Value = Call->getArg(1);
+ diag(Call->getOperatorLoc(),
+ "(%0 passed to 'operator<<' outputs as character instead of integer. "
+ "cast to 'unsigned' to print numeric value or cast to 'char' to print "
+ "as character)")
+ << Value->getType() << Value->getSourceRange();
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.h b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.h
new file mode 100644
index 0000000000000..071baac3216c0
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.h
@@ -0,0 +1,34 @@
+//===--- UnintendedCharOstreamOutputCheck.h - clang-tidy --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNINTENDEDCHAROSTREAMOUTPUTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNINTENDEDCHAROSTREAMOUTPUTCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Finds unintended character output from `unsigned char` and `signed char` to
+/// an ostream.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/unintended-char-ostream-output.html
+class UnintendedCharOstreamOutputCheck : public ClangTidyCheck {
+public:
+ UnintendedCharOstreamOutputCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus;
+ }
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNINTENDEDCHAROSTREAMOUTPUTCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 6b8fe22242417..57f37c8e02e2e 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -91,6 +91,12 @@ Improvements to clang-tidy
New checks
^^^^^^^^^^
+- New :doc:`bugprone-unintended-char-ostream-output
+ <clang-tidy/checks/bugprone/unintended-char-ostream-output>` check.
+
+ Finds unintended character output from `unsigned char` and `signed char` to an
+ ostream.
+
New check aliases
^^^^^^^^^^^^^^^^^
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
new file mode 100644
index 0000000000000..1e60698a5d445
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/unintended-char-ostream-output.rst
@@ -0,0 +1,30 @@
+.. title:: clang-tidy - bugprone-unintended-char-ostream-output
+
+bugprone-unintended-char-ostream-output
+=======================================
+
+Finds unintended character output from `unsigned char` and `signed char` to an
+``ostream``.
+
+Normally, when ``unsigned char (uint8_t)`` or ``signed char (int8_t)`` is used, it
+is more likely a number than a character. However, when it is passed directly to
+``std::ostream``'s ``operator<<``, resulting in character-based output instead
+of numeric value. This often contradicts the developer's intent to print
+integer values.
+
+.. code-block:: c++
+
+ uint8_t v = 9;
+ std::cout << v; // output '\t' instead of '9'
+
+It could be fixed as
+
+.. code-block:: c++
+
+ std::cout << (uint32_t)v;
+
+Or cast to char to explicitly indicate the intent
+
+.. code-block:: c++
+
+ std::cout << (char)v;
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 7b9b905ef7671..9306dfe95fa45 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -158,6 +158,7 @@ Clang-Tidy Checks
:doc:`bugprone-undelegated-constructor <bugprone/undelegated-constructor>`,
:doc:`bugprone-unhandled-exception-at-new <bugprone/unhandled-exception-at-new>`,
:doc:`bugprone-unhandled-self-assignment <bugprone/unhandled-self-assignment>`,
+ :doc:`bugprone-unintended-char-ostream-output <bugprone/unintended-char-ostream-output>`,
:doc:`bugprone-unique-ptr-array-mismatch <bugprone/unique-ptr-array-mismatch>`, "Yes"
:doc:`bugprone-unsafe-functions <bugprone/unsafe-functions>`,
:doc:`bugprone-unused-local-non-trivial-variable <bugprone/unused-local-non-trivial-variable>`,
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
new file mode 100644
index 0000000000000..fd2382dcd730c
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output.cpp
@@ -0,0 +1,70 @@
+// RUN: %check_clang_tidy %s bugprone-unintended-char-ostream-output %t
+
+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
+
+class A : public std::ostream {};
+
+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;
+}
+
+void based_on_ostream(A &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;
+}
+
+void based_on_ostream(std::basic_ostream<unsigned char> &os) {
+ unsigned char unsigned_value = 9;
+ os << unsigned_value;
+
+ signed char signed_value = 9;
+ os << signed_value;
+
+ char char_value = 9;
+ os << char_value;
+}
>From 22b8bd830cfaa12a86a6bbc0cab58db721de70c2 Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Wed, 19 Feb 2025 14:54:02 +0800
Subject: [PATCH 02/10] fix review
---
.../clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp | 3 +--
clang-tools-extra/docs/ReleaseNotes.rst | 2 +-
.../checks/bugprone/unintended-char-ostream-output.rst | 2 +-
.../checkers/bugprone/unintended-char-ostream-output.cpp | 1 -
4 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
index 7c54ef1486b2f..6c3ae59305af7 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
@@ -1,5 +1,4 @@
-//===--- UnintendedCharOstreamOutputCheck.cpp - clang-tidy
-//---------------------===//
+//===--- UnintendedCharOstreamOutputCheck.cpp - clang-tidy ----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 57f37c8e02e2e..662737a3802cb 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -94,7 +94,7 @@ New checks
- New :doc:`bugprone-unintended-char-ostream-output
<clang-tidy/checks/bugprone/unintended-char-ostream-output>` check.
- Finds unintended character output from `unsigned char` and `signed char` to an
+ Finds unintended character output from ``unsigned char`` and ``signed char`` to an
ostream.
New check aliases
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 1e60698a5d445..4d43fe93096bb 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
@@ -3,7 +3,7 @@
bugprone-unintended-char-ostream-output
=======================================
-Finds unintended character output from `unsigned char` and `signed char` to an
+Finds unintended character output from ``unsigned char`` and ``signed char`` to an
``ostream``.
Normally, when ``unsigned char (uint8_t)`` or ``signed char (int8_t)`` is used, it
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 fd2382dcd730c..f1c56083a42c5 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
@@ -23,7 +23,6 @@ basic_ostream<char, _Traits> &operator<<(basic_ostream<char, _Traits> &,
using ostream = basic_ostream<char>;
-
} // namespace std
class A : public std::ostream {};
>From 9033197f511fff7787ba397daa53efaefbbfd246 Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Wed, 19 Feb 2025 17:48:59 +0800
Subject: [PATCH 03/10] fix review
---
.../bugprone/UnintendedCharOstreamOutputCheck.cpp | 4 ++--
.../checks/bugprone/unintended-char-ostream-output.rst | 4 ++--
.../checkers/bugprone/unintended-char-ostream-output.cpp | 8 ++++----
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
index 6c3ae59305af7..342999597b027 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
@@ -60,9 +60,9 @@ void UnintendedCharOstreamOutputCheck::check(
const auto *Call = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("x");
const Expr *Value = Call->getArg(1);
diag(Call->getOperatorLoc(),
- "(%0 passed to 'operator<<' outputs as character instead of integer. "
+ "%0 passed to 'operator<<' outputs as character instead of integer. "
"cast to 'unsigned' to print numeric value or cast to 'char' to print "
- "as character)")
+ "as character")
<< Value->getType() << Value->getSourceRange();
}
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 4d43fe93096bb..e312f93dd4b93 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
@@ -21,10 +21,10 @@ It could be fixed as
.. code-block:: c++
- std::cout << (uint32_t)v;
+ std::cout << static_cast<uint32_t>(v);
Or cast to char to explicitly indicate the intent
.. code-block:: c++
- std::cout << (char)v;
+ std::cout << static_cast<char>(v);
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 f1c56083a42c5..d2fcdc6797628 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
@@ -30,12 +30,12 @@ class A : public std::ostream {};
void origin_ostream(std::ostream &os) {
unsigned char unsigned_value = 9;
os << unsigned_value;
- // CHECK-MESSAGES: [[@LINE-1]]:6: warning: ('unsigned char' passed to
+ // 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
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'signed char' passed to
// 'operator<<' outputs as character instead of integer
char char_value = 9;
@@ -45,12 +45,12 @@ void origin_ostream(std::ostream &os) {
void based_on_ostream(A &os) {
unsigned char unsigned_value = 9;
os << unsigned_value;
- // CHECK-MESSAGES: [[@LINE-1]]:6: warning: ('unsigned char' passed to
+ // 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
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'signed char' passed to
// 'operator<<' outputs as character instead of integer
char char_value = 9;
>From 824f287c32d9ecc889f2553f1e36d216ce9d95be Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Thu, 20 Feb 2025 08:01:49 +0800
Subject: [PATCH 04/10] Update
clang-tools-extra/docs/clang-tidy/checks/bugprone/unintended-char-ostream-output.rst
Co-authored-by: whisperity <whisperity at gmail.com>
---
.../checks/bugprone/unintended-char-ostream-output.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
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 e312f93dd4b93..4d55a2cd7cafd 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
@@ -8,8 +8,8 @@ Finds unintended character output from ``unsigned char`` and ``signed char`` to
Normally, when ``unsigned char (uint8_t)`` or ``signed char (int8_t)`` is used, it
is more likely a number than a character. However, when it is passed directly to
-``std::ostream``'s ``operator<<``, resulting in character-based output instead
-of numeric value. This often contradicts the developer's intent to print
+``std::ostream``'s ``operator<<``, the result is the character output instead
+of the numeric value. This often contradicts the developer's intent to print
integer values.
.. code-block:: c++
>From 7f6405e298f61c7e08a22c20390b12e525c102b4 Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Thu, 20 Feb 2025 08:30:51 +0800
Subject: [PATCH 05/10] fix review
---
.../UnintendedCharOstreamOutputCheck.cpp | 14 ++++---------
clang-tools-extra/docs/ReleaseNotes.rst | 2 +-
.../unintended-char-ostream-output.rst | 4 ++--
.../unintended-char-ostream-output.cpp | 20 +++++++++++--------
4 files changed, 19 insertions(+), 21 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
index 342999597b027..27d8df6cbae91 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
@@ -19,20 +19,14 @@ namespace {
// check if the type is unsigned char or signed char
AST_MATCHER(Type, isNumericChar) {
- const auto *BT = dyn_cast<BuiltinType>(&Node);
- if (BT == nullptr)
- return false;
- const BuiltinType::Kind K = BT->getKind();
- return K == BuiltinType::UChar || K == BuiltinType::SChar;
+ return Node.isSpecificBuiltinType(BuiltinType::SChar) ||
+ Node.isSpecificBuiltinType(BuiltinType::UChar);
}
// check if the type is char
AST_MATCHER(Type, isChar) {
- const auto *BT = dyn_cast<BuiltinType>(&Node);
- if (BT == nullptr)
- return false;
- const BuiltinType::Kind K = BT->getKind();
- return K == BuiltinType::Char_U || K == BuiltinType::Char_S;
+ return Node.isSpecificBuiltinType(BuiltinType::Char_S) ||
+ Node.isSpecificBuiltinType(BuiltinType::Char_U);
}
} // namespace
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 662737a3802cb..60acd6c8d7511 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -95,7 +95,7 @@ New checks
<clang-tidy/checks/bugprone/unintended-char-ostream-output>` check.
Finds unintended character output from ``unsigned char`` and ``signed char`` to an
- ostream.
+ ``ostream``.
New check aliases
^^^^^^^^^^^^^^^^^
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 4d55a2cd7cafd..670f7a615d25b 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
@@ -14,8 +14,8 @@ integer values.
.. code-block:: c++
- uint8_t v = 9;
- std::cout << v; // output '\t' instead of '9'
+ uint8_t v = 65;
+ std::cout << v; // output 'A' instead of '65'
It could be fixed as
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 d2fcdc6797628..9d0effe581807 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
@@ -30,13 +30,11 @@ class A : public std::ostream {};
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
+ // 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
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'signed char' passed to 'operator<<' outputs as character instead of integer
char char_value = 9;
os << char_value;
@@ -45,13 +43,11 @@ void origin_ostream(std::ostream &os) {
void based_on_ostream(A &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
+ // 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
+ // CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'signed char' passed to 'operator<<' outputs as character instead of integer
char char_value = 9;
os << char_value;
@@ -67,3 +63,11 @@ void based_on_ostream(std::basic_ostream<unsigned char> &os) {
char char_value = 9;
os << char_value;
}
+
+template <class T> class B : public std::ostream {};
+void template_based_on_ostream(B<int> &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
+}
+
>From 00df49d799638e90f837fea705e128856c7137d3 Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Thu, 20 Feb 2025 08:35:04 +0800
Subject: [PATCH 06/10] template test
---
.../unintended-char-ostream-output.cpp | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
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 9d0effe581807..f448b715ec9d3 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
@@ -71,3 +71,23 @@ void template_based_on_ostream(B<int> &os) {
// CHECK-MESSAGES: [[@LINE-1]]:6: warning: 'unsigned char' passed to 'operator<<' outputs as character instead of integer
}
+template<class T> void template_fn_1(T &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
+}
+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
+}
+template<class T> void template_fn_3(std::ostream &os) {
+ T unsigned_value = 9;
+ os << unsigned_value;
+}
+void call_template_fn() {
+ A a{};
+ template_fn_1(a);
+ template_fn_2<unsigned char>(a);
+ template_fn_3<char>(a);
+}
>From bc43f1ff9184002b3da497863a5679b6dc4cf542 Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Thu, 20 Feb 2025 20:46:19 +0800
Subject: [PATCH 07/10] add more test case
---
.../UnintendedCharOstreamOutputCheck.cpp | 30 ++++++++++++---
.../unintended-char-ostream-output.cpp | 38 +++++++++++++++++++
2 files changed, 63 insertions(+), 5 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
index 27d8df6cbae91..9c7e45436abe9 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
@@ -10,6 +10,8 @@
#include "clang/AST/Type.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Tooling/FixIt.h"
using namespace clang::ast_matchers;
@@ -53,11 +55,29 @@ void UnintendedCharOstreamOutputCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *Call = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("x");
const Expr *Value = Call->getArg(1);
- diag(Call->getOperatorLoc(),
- "%0 passed to 'operator<<' outputs as character instead of integer. "
- "cast to 'unsigned' to print numeric value or cast to 'char' to print "
- "as character")
- << Value->getType() << Value->getSourceRange();
+ const SourceRange SourceRange = Value->getSourceRange();
+
+ DiagnosticBuilder Builder =
+ diag(Call->getOperatorLoc(),
+ "%0 passed to 'operator<<' outputs as character instead of integer. "
+ "cast to 'unsigned' to print numeric value or cast to 'char' to "
+ "print "
+ "as character")
+ << Value->getType() << SourceRange;
+
+ QualType T = Value->getType();
+ const Type *UnqualifiedDesugaredType = T->getUnqualifiedDesugaredType();
+
+ llvm::StringRef CastType;
+ if (UnqualifiedDesugaredType->isSpecificBuiltinType(BuiltinType::SChar))
+ CastType = "int";
+ else
+ CastType = "unsigned int";
+
+ Builder << FixItHint::CreateReplacement(
+ SourceRange, ("static_cast<" + CastType + ">(" +
+ tooling::fixit::getText(*Value, *Result.Context) + ")")
+ .str());
}
} // namespace clang::tidy::bugprone
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 f448b715ec9d3..0a5cdeb21c01e 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
@@ -31,10 +31,12 @@ 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
+ // CHECK-FIXES: os << static_cast<unsigned int>(unsigned_value);
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
+ // CHECK-FIXES: os << static_cast<int>(signed_value);
char char_value = 9;
os << char_value;
@@ -44,10 +46,12 @@ void based_on_ostream(A &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
+ // CHECK-FIXES: os << static_cast<unsigned int>(unsigned_value);
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
+ // CHECK-FIXES: os << static_cast<int>(signed_value);
char char_value = 9;
os << char_value;
@@ -69,17 +73,20 @@ void template_based_on_ostream(B<int> &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
+ // CHECK-FIXES: os << static_cast<unsigned int>(unsigned_value);
}
template<class T> void template_fn_1(T &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
+ // 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);
}
template<class T> void template_fn_3(std::ostream &os) {
T unsigned_value = 9;
@@ -91,3 +98,34 @@ void call_template_fn() {
template_fn_2<unsigned char>(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;
+ os << v;
+}
+
+
+#define MACRO_VARIANT_NAME a
+void macro_variant_name(std::ostream &os) {
+ unsigned char 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-FIXES: os << static_cast<unsigned int>(MACRO_VARIANT_NAME);
+}
>From 82cb4d073e1e3ddd7157d8a37f5d1f14d2b91b8f Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Thu, 20 Feb 2025 21:02:48 +0800
Subject: [PATCH 08/10] add option
---
.../UnintendedCharOstreamOutputCheck.cpp | 24 ++++++----
.../UnintendedCharOstreamOutputCheck.h | 8 +++-
.../unintended-char-ostream-output.rst | 13 ++++--
...intended-char-ostream-output-cast-type.cpp | 45 +++++++++++++++++++
4 files changed, 76 insertions(+), 14 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output-cast-type.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
index 9c7e45436abe9..7250e4ccb8c69 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp
@@ -33,6 +33,16 @@ AST_MATCHER(Type, isChar) {
} // namespace
+UnintendedCharOstreamOutputCheck::UnintendedCharOstreamOutputCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context), CastTypeName(Options.get("CastTypeName")) {
+}
+void UnintendedCharOstreamOutputCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ if (CastTypeName.has_value())
+ Options.store(Opts, "CastTypeName", CastTypeName.value());
+}
+
void UnintendedCharOstreamOutputCheck::registerMatchers(MatchFinder *Finder) {
auto BasicOstream =
cxxRecordDecl(hasName("::std::basic_ostream"),
@@ -60,19 +70,17 @@ void UnintendedCharOstreamOutputCheck::check(
DiagnosticBuilder Builder =
diag(Call->getOperatorLoc(),
"%0 passed to 'operator<<' outputs as character instead of integer. "
- "cast to 'unsigned' to print numeric value or cast to 'char' to "
- "print "
- "as character")
+ "cast to 'unsigned int' to print numeric value or cast to 'char' to "
+ "print as character")
<< Value->getType() << SourceRange;
QualType T = Value->getType();
const Type *UnqualifiedDesugaredType = T->getUnqualifiedDesugaredType();
- llvm::StringRef CastType;
- if (UnqualifiedDesugaredType->isSpecificBuiltinType(BuiltinType::SChar))
- CastType = "int";
- else
- CastType = "unsigned int";
+ llvm::StringRef CastType = CastTypeName.value_or(
+ UnqualifiedDesugaredType->isSpecificBuiltinType(BuiltinType::SChar)
+ ? "int"
+ : "unsigned int");
Builder << FixItHint::CreateReplacement(
SourceRange, ("static_cast<" + CastType + ">(" +
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.h b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.h
index 071baac3216c0..61ea623d139ea 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.h
@@ -10,6 +10,7 @@
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNINTENDEDCHAROSTREAMOUTPUTCHECK_H
#include "../ClangTidyCheck.h"
+#include <optional>
namespace clang::tidy::bugprone {
@@ -20,13 +21,16 @@ namespace clang::tidy::bugprone {
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/unintended-char-ostream-output.html
class UnintendedCharOstreamOutputCheck : public ClangTidyCheck {
public:
- UnintendedCharOstreamOutputCheck(StringRef Name, ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context) {}
+ UnintendedCharOstreamOutputCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
+
+private:
+ const std::optional<StringRef> CastTypeName;
};
} // 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 670f7a615d25b..3235ec1a4840f 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
@@ -14,17 +14,22 @@ integer values.
.. code-block:: c++
- uint8_t v = 65;
- std::cout << v; // output 'A' instead of '65'
+ uint8_t v = 65;
+ std::cout << v; // output 'A' instead of '65'
It could be fixed as
.. code-block:: c++
- std::cout << static_cast<uint32_t>(v);
+ std::cout << static_cast<uint32_t>(v);
Or cast to char to explicitly indicate the intent
.. code-block:: c++
- std::cout << static_cast<char>(v);
+ std::cout << static_cast<char>(v);
+
+.. option:: CastTypeName
+
+ When `CastTypeName` is specified, the fix-it will use `CastTypeName` as the
+ cast target type. Otherwise, fix-it will automatically infer the type.
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
new file mode 100644
index 0000000000000..faea4127ac44a
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unintended-char-ostream-output-cast-type.cpp
@@ -0,0 +1,45 @@
+// RUN: %check_clang_tidy %s bugprone-unintended-char-ostream-output %t -- \
+// RUN: -config="{CheckOptions: \
+// RUN: {bugprone-unintended-char-ostream-output.CastTypeName: "uint8_t"}}"
+
+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
+
+class A : public std::ostream {};
+
+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
+ // CHECK-FIXES: os << static_cast<uint8_t>(unsigned_value);
+
+ 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
+ // CHECK-FIXES: os << static_cast<uint8_t>(signed_value);
+
+ char char_value = 9;
+ os << char_value;
+}
>From 553d6cd7bd4b67d5f10a02e6aa5f36a08247793b Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Mon, 24 Feb 2025 13:16:00 +0800
Subject: [PATCH 09/10] rework doc
---
.../bugprone/unintended-char-ostream-output.rst | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
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 3235ec1a4840f..7e9a366988d5e 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
@@ -17,13 +17,23 @@ integer values.
uint8_t v = 65;
std::cout << v; // output 'A' instead of '65'
-It could be fixed as
+The check will suggest casting the value to an appropriate type to indicate the
+intent, by default, it will cast to ``unsigned int`` for ``unsigned char`` and
+``int`` for ``signed char``.
.. code-block:: c++
- std::cout << static_cast<uint32_t>(v);
+ std::cout << static_cast<unsigned int>(v); // when v is unsigned char
+ std::cout << static_cast<int>(v); // when v is signed char
-Or cast to char to explicitly indicate the intent
+To avoid lengthy cast statements, add prefix ``+`` to the variable can also
+suppress warnings.
+
+.. code-block:: c++
+
+ std::cout << +v;
+
+Or cast to char to explicitly indicate that output should be a character.
.. code-block:: c++
>From 4d3916e45a28d4f197e6efa24d98b1b124e198c0 Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Mon, 24 Feb 2025 22:56:47 +0800
Subject: [PATCH 10/10] add doc
---
.../checks/bugprone/unintended-char-ostream-output.rst | 2 +-
clang-tools-extra/docs/clang-tidy/checks/list.rst | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
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 7e9a366988d5e..ea1051847129b 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
@@ -27,7 +27,7 @@ intent, by default, it will cast to ``unsigned int`` for ``unsigned char`` and
std::cout << static_cast<int>(v); // when v is signed char
To avoid lengthy cast statements, add prefix ``+`` to the variable can also
-suppress warnings.
+suppress warnings because unary expression will promote the value to an ``int``.
.. code-block:: c++
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 9306dfe95fa45..5f03ef72cc603 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -158,7 +158,7 @@ Clang-Tidy Checks
:doc:`bugprone-undelegated-constructor <bugprone/undelegated-constructor>`,
:doc:`bugprone-unhandled-exception-at-new <bugprone/unhandled-exception-at-new>`,
:doc:`bugprone-unhandled-self-assignment <bugprone/unhandled-self-assignment>`,
- :doc:`bugprone-unintended-char-ostream-output <bugprone/unintended-char-ostream-output>`,
+ :doc:`bugprone-unintended-char-ostream-output <bugprone/unintended-char-ostream-output>`, "Yes"
:doc:`bugprone-unique-ptr-array-mismatch <bugprone/unique-ptr-array-mismatch>`, "Yes"
:doc:`bugprone-unsafe-functions <bugprone/unsafe-functions>`,
:doc:`bugprone-unused-local-non-trivial-variable <bugprone/unused-local-non-trivial-variable>`,
More information about the cfe-commits
mailing list