[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
Thu Feb 20 05:07:06 PST 2025
================
@@ -0,0 +1,69 @@
+//===--- 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();
----------------
HerrCai0907 wrote:
For me, it is more clearer to cast unsigned char to unsigned int and signed char to int. This can avoid that when converting unsigned char to int, people are not sure whether the result of 255 is -1 or 255.
https://github.com/llvm/llvm-project/pull/127720
More information about the cfe-commits
mailing list