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
9#include "UnintendedCharOstreamOutputCheck.h"
10#include "../utils/Matchers.h"
11#include "../utils/OptionsUtils.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(K: BuiltinType::SChar) ||
27 Node.isSpecificBuiltinType(K: BuiltinType::UChar);
28}
29
30// check if the type is char
31AST_MATCHER(Type, isChar) {
32 return Node.isSpecificBuiltinType(K: BuiltinType::Char_S) ||
33 Node.isSpecificBuiltinType(K: BuiltinType::Char_U);
34}
35
36} // namespace
37
38UnintendedCharOstreamOutputCheck::UnintendedCharOstreamOutputCheck(
39 StringRef Name, ClangTidyContext *Context)
40 : ClangTidyCheck(Name, Context),
41 AllowedTypes(utils::options::parseStringList(
42 Option: Options.get(LocalName: "AllowedTypes", Default: "unsigned char;signed char"))),
43 CastTypeName(Options.get(LocalName: "CastTypeName")) {}
44void UnintendedCharOstreamOutputCheck::storeOptions(
45 ClangTidyOptions::OptionMap &Opts) {
46 Options.store(Options&: Opts, LocalName: "AllowedTypes",
47 Value: utils::options::serializeStringList(Strings: AllowedTypes));
48 if (CastTypeName.has_value())
49 Options.store(Options&: Opts, LocalName: "CastTypeName", Value: CastTypeName.value());
50}
51
52void UnintendedCharOstreamOutputCheck::registerMatchers(MatchFinder *Finder) {
53 auto BasicOstream =
54 cxxRecordDecl(hasName(Name: "::std::basic_ostream"),
55 // only basic_ostream<char, Traits> has overload operator<<
56 // with char / unsigned char / signed char
57 classTemplateSpecializationDecl(
58 hasTemplateArgument(N: 0, InnerMatcher: refersToType(InnerMatcher: isChar()))));
59 auto IsDeclRefExprFromAllowedTypes = declRefExpr(to(InnerMatcher: varDecl(
60 hasType(InnerMatcher: matchers::matchesAnyListedTypeName(NameList: AllowedTypes, CanonicalTypes: false)))));
61 auto IsExplicitCastExprFromAllowedTypes = explicitCastExpr(hasDestinationType(
62 InnerMatcher: matchers::matchesAnyListedTypeName(NameList: AllowedTypes, CanonicalTypes: false)));
63 Finder->addMatcher(
64 NodeMatch: cxxOperatorCallExpr(
65 hasOverloadedOperatorName(Name: "<<"),
66 hasLHS(InnerMatcher: hasType(InnerMatcher: hasUnqualifiedDesugaredType(
67 InnerMatcher: recordType(hasDeclaration(InnerMatcher: cxxRecordDecl(
68 anyOf(BasicOstream, isDerivedFrom(Base: BasicOstream)))))))),
69 hasRHS(InnerMatcher: expr(hasType(InnerMatcher: hasUnqualifiedDesugaredType(InnerMatcher: isNumericChar())),
70 unless(ignoringParenImpCasts(
71 InnerMatcher: anyOf(IsDeclRefExprFromAllowedTypes,
72 IsExplicitCastExprFromAllowedTypes))))))
73 .bind(ID: "x"),
74 Action: this);
75}
76
77void UnintendedCharOstreamOutputCheck::check(
78 const MatchFinder::MatchResult &Result) {
79 const auto *Call = Result.Nodes.getNodeAs<CXXOperatorCallExpr>(ID: "x");
80 const Expr *Value = Call->getArg(1);
81 const SourceRange SourceRange = Value->getSourceRange();
82
83 DiagnosticBuilder Builder =
84 diag(Loc: Call->getOperatorLoc(),
85 Description: "%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 u: UnqualifiedDesugaredType->isSpecificBuiltinType(K: BuiltinType::SChar)
95 ? "int"
96 : "unsigned int");
97
98 Builder << FixItHint::CreateReplacement(
99 RemoveRange: SourceRange, Code: ("static_cast<" + CastType + ">(" +
100 tooling::fixit::getText(Node: *Value, Context: *Result.Context) + ")")
101 .str());
102}
103
104} // namespace clang::tidy::bugprone
105

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang-tools-extra/clang-tidy/bugprone/UnintendedCharOstreamOutputCheck.cpp