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 <congcongcai0...@163.com> Date: Wed, 19 Feb 2025 07:38:37 +0800 Subject: [PATCH 1/2] [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 <congcongcai0...@163.com> Date: Wed, 19 Feb 2025 14:54:02 +0800 Subject: [PATCH 2/2] 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 {}; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits