clang-tools 10.0.0

StringIntegerAssignmentCheck.cpp
Go to the documentation of this file.
1 //===--- StringIntegerAssignmentCheck.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 "StringIntegerAssignmentCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
13
14 using namespace clang::ast_matchers;
15
16 namespace clang {
17 namespace tidy {
18 namespace bugprone {
19
20 void StringIntegerAssignmentCheck::registerMatchers(MatchFinder *Finder) {
21 if (!getLangOpts().CPlusPlus)
22 return;
23 Finder->addMatcher(
24 cxxOperatorCallExpr(
25 anyOf(hasOverloadedOperatorName("="),
26 hasOverloadedOperatorName("+=")),
27 callee(cxxMethodDecl(ofClass(classTemplateSpecializationDecl(
28 hasName("::std::basic_string"),
29 hasTemplateArgument(0, refersToType(hasCanonicalType(
30 qualType().bind("type")))))))),
31 hasArgument(
32 1,
33 ignoringImpCasts(
34 expr(hasType(isInteger()), unless(hasType(isAnyCharacter())),
35 // Ignore calls to tolower/toupper (see PR27723).
36 unless(callExpr(callee(functionDecl(
37 hasAnyName("tolower", "std::tolower", "toupper",
38 "std::toupper"))))),
39 // Do not warn if assigning e.g. `CodePoint` to
40 // `basic_string<CodePoint>`
41 unless(hasType(qualType(
42 hasCanonicalType(equalsBoundNode("type"))))))
43 .bind("expr"))),
44 unless(isInTemplateInstantiation())),
45 this);
46 }
47
48 class CharExpressionDetector {
49 public:
50 CharExpressionDetector(QualType CharType, const ASTContext &Ctx)
51 : CharType(CharType), Ctx(Ctx) {}
52
53 bool isLikelyCharExpression(const Expr *E) const {
54 if (isCharTyped(E))
55 return true;
56
57 if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
58 const auto *LHS = BinOp->getLHS()->IgnoreParenImpCasts();
59 const auto *RHS = BinOp->getRHS()->IgnoreParenImpCasts();
60 // Handle both directions, e.g. `'a' + (i % 26)` and `(i % 26) + 'a'`.
61 if (BinOp->isAdditiveOp() || BinOp->isBitwiseOp())
62 return handleBinaryOp(BinOp->getOpcode(), LHS, RHS) ||
63 handleBinaryOp(BinOp->getOpcode(), RHS, LHS);
64 // Except in the case of '%'.
65 if (BinOp->getOpcode() == BO_Rem)
66 return handleBinaryOp(BinOp->getOpcode(), LHS, RHS);
67 return false;
68 }
69
70 // Ternary where at least one branch is a likely char expression, e.g.
71 // i < 265 ? i : ' '
72 if (const auto *CondOp = dyn_cast<AbstractConditionalOperator>(E))
73 return isLikelyCharExpression(
74 CondOp->getFalseExpr()->IgnoreParenImpCasts()) ||
75 isLikelyCharExpression(
76 CondOp->getTrueExpr()->IgnoreParenImpCasts());
77 return false;
78 }
79
80 private:
81 bool handleBinaryOp(clang::BinaryOperatorKind Opcode, const Expr *const LHS,
82 const Expr *const RHS) const {
83 // <char_expr> <op> <char_expr> (c++ integer promotion rules make this an
84 // int), e.g.
85 // 'a' + c
86 if (isCharTyped(LHS) && isCharTyped(RHS))
87 return true;
88
89 // <expr> & <char_valued_constant> or <expr> % <char_valued_constant>, e.g.
90 // i & 0xff
91 if ((Opcode == BO_And || Opcode == BO_Rem) && isCharValuedConstant(RHS))
92 return true;
93
94 // <char_expr> | <char_valued_constant>, e.g.
95 // c | 0x80
96 if (Opcode == BO_Or && isCharTyped(LHS) && isCharValuedConstant(RHS))
97 return true;
98
99 // <char_constant> + <likely_char_expr>, e.g.
100 // 'a' + (i % 26)
101 if (Opcode == BO_Add)
102 return isCharConstant(LHS) && isLikelyCharExpression(RHS);
103
104 return false;
105 }
106
107 // Returns true if `E` is an character constant.
108 bool isCharConstant(const Expr *E) const {
109 return isCharTyped(E) && isCharValuedConstant(E);
110 };
111
112 // Returns true if `E` is an integer constant which fits in `CharType`.
113 bool isCharValuedConstant(const Expr *E) const {
114 if (E->isInstantiationDependent())
115 return false;
116 Expr::EvalResult EvalResult;
117 if (!E->EvaluateAsInt(EvalResult, Ctx, Expr::SE_AllowSideEffects))
118 return false;
119 return EvalResult.Val.getInt().getActiveBits() <= Ctx.getTypeSize(CharType);
120 };
121
122 // Returns true if `E` has the right character type.
123 bool isCharTyped(const Expr *E) const {
124 return E->getType().getCanonicalType().getTypePtr() ==
125 CharType.getTypePtr();
126 };
127
128 const QualType CharType;
129 const ASTContext &Ctx;
130 };
131
132 void StringIntegerAssignmentCheck::check(
133 const MatchFinder::MatchResult &Result) {
134 const auto *Argument = Result.Nodes.getNodeAs<Expr>("expr");
135 const auto CharType =
136 Result.Nodes.getNodeAs<QualType>("type")->getCanonicalType();
137 SourceLocation Loc = Argument->getBeginLoc();
138
139 // Try to detect a few common expressions to reduce false positives.
140 if (CharExpressionDetector(CharType, *Result.Context)
141 .isLikelyCharExpression(Argument))
142 return;
143
144 auto Diag =
145 diag(Loc, "an integer is interpreted as a character code when assigning "
146 "it to a string; if this is intended, cast the integer to the "
147 "appropriate character type; if you want a string "
148 "representation, use the appropriate conversion facility");
149
150 if (Loc.isMacroID())
151 return;
152
153 bool IsWideCharType = CharType->isWideCharType();
154 if (!CharType->isCharType() && !IsWideCharType)
155 return;
156 bool IsOneDigit = false;
157 bool IsLiteral = false;
158 if (const auto *Literal = dyn_cast<IntegerLiteral>(Argument)) {
159 IsOneDigit = Literal->getValue().getLimitedValue() < 10;
160 IsLiteral = true;
161 }
162
163 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
164 Argument->getEndLoc(), 0, *Result.SourceManager, getLangOpts());
165 if (IsOneDigit) {
166 Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "L'" : "'")
167 << FixItHint::CreateInsertion(EndLoc, "'");
168 return;
169 }
170 if (IsLiteral) {
171 Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "L\"" : "\"")
172 << FixItHint::CreateInsertion(EndLoc, "\"");
173 return;
174 }
175
176 if (getLangOpts().CPlusPlus11) {
177 Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "std::to_wstring("
178 : "std::to_string(")
179 << FixItHint::CreateInsertion(EndLoc, ")");
180 }
181 }
182
183 } // namespace bugprone
184 } // namespace tidy
185 } // namespace clang
Loc
SourceLocation Loc
&#39;#&#39; location in the include directive
Definition: IncludeOrderCheck.cpp:37
clang::ast_matchers
Definition: AbseilMatcher.h:14
StringIntegerAssignmentCheck.h
clang::tidy::bugprone::CharExpressionDetector::CharExpressionDetector
CharExpressionDetector(QualType CharType, const ASTContext &Ctx)
Definition: StringIntegerAssignmentCheck.cpp:50
Ctx
Context Ctx
Definition: TUScheduler.cpp:237
clang::clangd::CharType
CharType
Definition: FuzzyMatch.h:41
clang::tidy::bugprone::CharExpressionDetector::isLikelyCharExpression
bool isLikelyCharExpression(const Expr *E) const
Definition: StringIntegerAssignmentCheck.cpp:53
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::bugprone::CharExpressionDetector
Definition: StringIntegerAssignmentCheck.cpp:48
E
const Expr * E
Definition: AvoidBindCheck.cpp:82