10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/Lex/Lexer.h"
21 hasAnyOverloadedOperatorName(
"=",
"+="),
22 callee(cxxMethodDecl(ofClass(classTemplateSpecializationDecl(
23 hasName(
"::std::basic_string"),
24 hasTemplateArgument(0, refersToType(hasCanonicalType(
25 qualType().bind(
"type")))))))),
29 expr(hasType(isInteger()), unless(hasType(isAnyCharacter())),
31 unless(callExpr(callee(functionDecl(
32 hasAnyName(
"tolower",
"std::tolower",
"toupper",
36 unless(hasType(qualType(
37 hasCanonicalType(equalsBoundNode(
"type"))))))
39 unless(isInTemplateInstantiation())),
45class CharExpressionDetector {
47 CharExpressionDetector(QualType CharType,
const ASTContext &Ctx)
48 : CharType(CharType), Ctx(Ctx) {}
50 bool isLikelyCharExpression(
const Expr *E)
const {
54 if (
const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
55 const auto *LHS = BinOp->getLHS()->IgnoreParenImpCasts();
56 const auto *RHS = BinOp->getRHS()->IgnoreParenImpCasts();
58 if (BinOp->isAdditiveOp() || BinOp->isBitwiseOp())
59 return handleBinaryOp(BinOp->getOpcode(), LHS, RHS) ||
60 handleBinaryOp(BinOp->getOpcode(), RHS, LHS);
62 if (BinOp->getOpcode() == BO_Rem)
63 return handleBinaryOp(BinOp->getOpcode(), LHS, RHS);
69 if (
const auto *CondOp = dyn_cast<AbstractConditionalOperator>(E))
70 return isLikelyCharExpression(
71 CondOp->getFalseExpr()->IgnoreParenImpCasts()) ||
72 isLikelyCharExpression(
73 CondOp->getTrueExpr()->IgnoreParenImpCasts());
78 bool handleBinaryOp(clang::BinaryOperatorKind Opcode,
const Expr *
const LHS,
79 const Expr *
const RHS)
const {
83 if (isCharTyped(LHS) && isCharTyped(RHS))
88 if ((Opcode == BO_And || Opcode == BO_Rem) && isCharValuedConstant(RHS))
93 if (Opcode == BO_Or && isCharTyped(LHS) && isCharValuedConstant(RHS))
99 return isCharConstant(LHS) && isLikelyCharExpression(RHS);
105 bool isCharConstant(
const Expr *E)
const {
106 return isCharTyped(E) && isCharValuedConstant(E);
110 bool isCharValuedConstant(
const Expr *E)
const {
111 if (E->isInstantiationDependent())
113 Expr::EvalResult EvalResult;
114 if (!E->EvaluateAsInt(EvalResult, Ctx, Expr::SE_AllowSideEffects))
116 return EvalResult.Val.getInt().getActiveBits() <= Ctx.getTypeSize(CharType);
120 bool isCharTyped(
const Expr *E)
const {
121 return E->getType().getCanonicalType().getTypePtr() ==
122 CharType.getTypePtr();
125 const QualType CharType;
126 const ASTContext &Ctx;
132 const MatchFinder::MatchResult &Result) {
133 const auto *Argument = Result.Nodes.getNodeAs<Expr>(
"expr");
134 const auto CharType =
135 Result.Nodes.getNodeAs<QualType>(
"type")->getCanonicalType();
136 const SourceLocation Loc = Argument->getBeginLoc();
139 if (CharExpressionDetector(CharType, *Result.Context)
140 .isLikelyCharExpression(Argument))
144 diag(Loc,
"an integer is interpreted as a character code when assigning "
145 "it to a string; if this is intended, cast the integer to the "
146 "appropriate character type; if you want a string "
147 "representation, use the appropriate conversion facility");
152 const bool IsWideCharType = CharType->isWideCharType();
153 if (!CharType->isCharType() && !IsWideCharType)
155 bool IsOneDigit =
false;
156 bool IsLiteral =
false;
157 if (
const auto *Literal = dyn_cast<IntegerLiteral>(Argument)) {
158 IsOneDigit = Literal->getValue().getLimitedValue() < 10;
162 const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
163 Argument->getEndLoc(), 0, *Result.SourceManager, getLangOpts());
165 Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ?
"L'" :
"'")
166 << FixItHint::CreateInsertion(EndLoc,
"'");
170 Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ?
"L\"" :
"\"")
171 << FixItHint::CreateInsertion(EndLoc,
"\"");
175 if (getLangOpts().CPlusPlus11) {
176 Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ?
"std::to_wstring("
178 << FixItHint::CreateInsertion(EndLoc,
")");
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override