xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/StringIntegerAssignmentCheck.cpp (revision 4bfa716f7c52d5bbf6ab23ea4fc20cec71c5f19a)
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