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(qualType().bind("type"))))))), 30 hasArgument( 31 1, 32 ignoringImpCasts( 33 expr(hasType(isInteger()), unless(hasType(isAnyCharacter())), 34 // Ignore calls to tolower/toupper (see PR27723). 35 unless(callExpr(callee(functionDecl( 36 hasAnyName("tolower", "std::tolower", "toupper", 37 "std::toupper")))))) 38 .bind("expr"))), 39 unless(isInTemplateInstantiation())), 40 this); 41 } 42 43 void StringIntegerAssignmentCheck::check( 44 const MatchFinder::MatchResult &Result) { 45 const auto *Argument = Result.Nodes.getNodeAs<Expr>("expr"); 46 SourceLocation Loc = Argument->getBeginLoc(); 47 48 auto Diag = 49 diag(Loc, "an integer is interpreted as a character code when assigning " 50 "it to a string; if this is intended, cast the integer to the " 51 "appropriate character type; if you want a string " 52 "representation, use the appropriate conversion facility"); 53 54 if (Loc.isMacroID()) 55 return; 56 57 auto CharType = *Result.Nodes.getNodeAs<QualType>("type"); 58 bool IsWideCharType = CharType->isWideCharType(); 59 if (!CharType->isCharType() && !IsWideCharType) 60 return; 61 bool IsOneDigit = false; 62 bool IsLiteral = false; 63 if (const auto *Literal = dyn_cast<IntegerLiteral>(Argument)) { 64 IsOneDigit = Literal->getValue().getLimitedValue() < 10; 65 IsLiteral = true; 66 } 67 68 SourceLocation EndLoc = Lexer::getLocForEndOfToken( 69 Argument->getEndLoc(), 0, *Result.SourceManager, getLangOpts()); 70 if (IsOneDigit) { 71 Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "L'" : "'") 72 << FixItHint::CreateInsertion(EndLoc, "'"); 73 return; 74 } 75 if (IsLiteral) { 76 Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "L\"" : "\"") 77 << FixItHint::CreateInsertion(EndLoc, "\""); 78 return; 79 } 80 81 if (getLangOpts().CPlusPlus11) { 82 Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "std::to_wstring(" 83 : "std::to_string(") 84 << FixItHint::CreateInsertion(EndLoc, ")"); 85 } 86 } 87 88 } // namespace bugprone 89 } // namespace tidy 90 } // namespace clang 91