clang-tools 22.0.0git
UnintendedCharOstreamOutputCheck.cpp
Go to the documentation of this file.
1//===--- UnintendedCharOstreamOutputCheck.cpp - clang-tidy ----------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "../utils/Matchers.h"
12#include "clang/AST/Type.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/ASTMatchers/ASTMatchers.h"
15#include "clang/Basic/Diagnostic.h"
16#include "clang/Tooling/FixIt.h"
17
18using namespace clang::ast_matchers;
19
20namespace clang::tidy::bugprone {
21
22namespace {
23
24// check if the type is unsigned char or signed char
25AST_MATCHER(Type, isNumericChar) {
26 return Node.isSpecificBuiltinType(BuiltinType::SChar) ||
27 Node.isSpecificBuiltinType(BuiltinType::UChar);
28}
29
30// check if the type is char
31AST_MATCHER(Type, isChar) {
32 return Node.isSpecificBuiltinType(BuiltinType::Char_S) ||
33 Node.isSpecificBuiltinType(BuiltinType::Char_U);
34}
35
36} // namespace
37
39 StringRef Name, ClangTidyContext *Context)
40 : ClangTidyCheck(Name, Context),
41 AllowedTypes(utils::options::parseStringList(
42 Options.get("AllowedTypes", "unsigned char;signed char"))),
43 CastTypeName(Options.get("CastTypeName")) {}
46 Options.store(Opts, "AllowedTypes",
48 if (CastTypeName.has_value())
49 Options.store(Opts, "CastTypeName", CastTypeName.value());
50}
51
53 auto BasicOstream =
54 cxxRecordDecl(hasName("::std::basic_ostream"),
55 // only basic_ostream<char, Traits> has overload operator<<
56 // with char / unsigned char / signed char
57 classTemplateSpecializationDecl(
58 hasTemplateArgument(0, refersToType(isChar()))));
59 auto IsDeclRefExprFromAllowedTypes = declRefExpr(to(varDecl(
60 hasType(matchers::matchesAnyListedTypeName(AllowedTypes, false)))));
61 auto IsExplicitCastExprFromAllowedTypes = explicitCastExpr(hasDestinationType(
62 matchers::matchesAnyListedTypeName(AllowedTypes, false)));
63 Finder->addMatcher(
64 cxxOperatorCallExpr(
65 hasOverloadedOperatorName("<<"),
66 hasLHS(hasType(hasUnqualifiedDesugaredType(
67 recordType(hasDeclaration(cxxRecordDecl(
68 anyOf(BasicOstream, isDerivedFrom(BasicOstream)))))))),
69 hasRHS(expr(hasType(hasUnqualifiedDesugaredType(isNumericChar())),
70 unless(ignoringParenImpCasts(
71 anyOf(IsDeclRefExprFromAllowedTypes,
72 IsExplicitCastExprFromAllowedTypes))))))
73 .bind("x"),
74 this);
75}
76
78 const MatchFinder::MatchResult &Result) {
79 const auto *Call = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("x");
80 const Expr *Value = Call->getArg(1);
81 const SourceRange SourceRange = Value->getSourceRange();
82
83 DiagnosticBuilder Builder =
84 diag(Call->getOperatorLoc(),
85 "%0 passed to 'operator<<' outputs as character instead of integer. "
86 "cast to 'unsigned int' to print numeric value or cast to 'char' to "
87 "print as character")
88 << Value->getType() << SourceRange;
89
90 QualType T = Value->getType();
91 const Type *UnqualifiedDesugaredType = T->getUnqualifiedDesugaredType();
92
93 llvm::StringRef CastType = CastTypeName.value_or(
94 UnqualifiedDesugaredType->isSpecificBuiltinType(BuiltinType::SChar)
95 ? "int"
96 : "unsigned int");
97
98 Builder << FixItHint::CreateReplacement(
99 SourceRange, ("static_cast<" + CastType + ">(" +
100 tooling::fixit::getText(*Value, *Result.Context) + ")")
101 .str());
102}
103
104} // namespace clang::tidy::bugprone
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
UnintendedCharOstreamOutputCheck(StringRef Name, ClangTidyContext *Context)
inline ::clang::ast_matchers::internal::Matcher< QualType > matchesAnyListedTypeName(llvm::ArrayRef< StringRef > NameList, bool CanonicalTypes)
AST_MATCHER(BinaryOperator, isRelationalOperator)
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
llvm::StringMap< ClangTidyValue > OptionMap