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 static bool isLikelyCharExpression(const Expr *Argument, 49 const ASTContext &Ctx) { 50 const auto *BinOp = dyn_cast<BinaryOperator>(Argument); 51 if (!BinOp) 52 return false; 53 const auto *LHS = BinOp->getLHS()->IgnoreParenImpCasts(); 54 const auto *RHS = BinOp->getRHS()->IgnoreParenImpCasts(); 55 // <expr> & <mask>, mask is a compile time constant. 56 Expr::EvalResult RHSVal; 57 if (BinOp->getOpcode() == BO_And && 58 (RHS->EvaluateAsInt(RHSVal, Ctx, Expr::SE_AllowSideEffects) || 59 LHS->EvaluateAsInt(RHSVal, Ctx, Expr::SE_AllowSideEffects))) 60 return true; 61 // <char literal> + (<expr> % <mod>), where <base> is a char literal. 62 const auto IsCharPlusModExpr = [](const Expr *L, const Expr *R) { 63 const auto *ROp = dyn_cast<BinaryOperator>(R); 64 return ROp && ROp->getOpcode() == BO_Rem && isa<CharacterLiteral>(L); 65 }; 66 if (BinOp->getOpcode() == BO_Add) { 67 if (IsCharPlusModExpr(LHS, RHS) || IsCharPlusModExpr(RHS, LHS)) 68 return true; 69 } 70 return false; 71 } 72 73 void StringIntegerAssignmentCheck::check( 74 const MatchFinder::MatchResult &Result) { 75 const auto *Argument = Result.Nodes.getNodeAs<Expr>("expr"); 76 SourceLocation Loc = Argument->getBeginLoc(); 77 78 // Try to detect a few common expressions to reduce false positives. 79 if (isLikelyCharExpression(Argument, *Result.Context)) 80 return; 81 82 auto Diag = 83 diag(Loc, "an integer is interpreted as a character code when assigning " 84 "it to a string; if this is intended, cast the integer to the " 85 "appropriate character type; if you want a string " 86 "representation, use the appropriate conversion facility"); 87 88 if (Loc.isMacroID()) 89 return; 90 91 auto CharType = *Result.Nodes.getNodeAs<QualType>("type"); 92 bool IsWideCharType = CharType->isWideCharType(); 93 if (!CharType->isCharType() && !IsWideCharType) 94 return; 95 bool IsOneDigit = false; 96 bool IsLiteral = false; 97 if (const auto *Literal = dyn_cast<IntegerLiteral>(Argument)) { 98 IsOneDigit = Literal->getValue().getLimitedValue() < 10; 99 IsLiteral = true; 100 } 101 102 SourceLocation EndLoc = Lexer::getLocForEndOfToken( 103 Argument->getEndLoc(), 0, *Result.SourceManager, getLangOpts()); 104 if (IsOneDigit) { 105 Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "L'" : "'") 106 << FixItHint::CreateInsertion(EndLoc, "'"); 107 return; 108 } 109 if (IsLiteral) { 110 Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "L\"" : "\"") 111 << FixItHint::CreateInsertion(EndLoc, "\""); 112 return; 113 } 114 115 if (getLangOpts().CPlusPlus11) { 116 Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "std::to_wstring(" 117 : "std::to_string(") 118 << FixItHint::CreateInsertion(EndLoc, ")"); 119 } 120 } 121 122 } // namespace bugprone 123 } // namespace tidy 124 } // namespace clang 125