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