1 //===--- MisplacedOperatorInStrlenInAllocCheck.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 "MisplacedOperatorInStrlenInAllocCheck.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::tidy::bugprone {
17 
registerMatchers(MatchFinder * Finder)18 void MisplacedOperatorInStrlenInAllocCheck::registerMatchers(
19     MatchFinder *Finder) {
20   const auto StrLenFunc = functionDecl(hasAnyName(
21       "::strlen", "::std::strlen", "::strnlen", "::std::strnlen", "::strnlen_s",
22       "::std::strnlen_s", "::wcslen", "::std::wcslen", "::wcsnlen",
23       "::std::wcsnlen", "::wcsnlen_s", "std::wcsnlen_s"));
24 
25   const auto BadUse =
26       callExpr(callee(StrLenFunc),
27                hasAnyArgument(ignoringImpCasts(
28                    binaryOperator(
29                        hasOperatorName("+"),
30                        hasRHS(ignoringParenImpCasts(integerLiteral(equals(1)))))
31                        .bind("BinOp"))))
32           .bind("StrLen");
33 
34   const auto BadArg = anyOf(
35       allOf(unless(binaryOperator(
36                 hasOperatorName("+"), hasLHS(BadUse),
37                 hasRHS(ignoringParenImpCasts(integerLiteral(equals(1)))))),
38             hasDescendant(BadUse)),
39       BadUse);
40 
41   const auto Alloc0Func = functionDecl(
42       hasAnyName("::malloc", "std::malloc", "::alloca", "std::alloca"));
43   const auto Alloc1Func = functionDecl(
44       hasAnyName("::calloc", "std::calloc", "::realloc", "std::realloc"));
45 
46   const auto Alloc0FuncPtr =
47       varDecl(hasType(isConstQualified()),
48               hasInitializer(ignoringParenImpCasts(
49                   declRefExpr(hasDeclaration(Alloc0Func)))));
50   const auto Alloc1FuncPtr =
51       varDecl(hasType(isConstQualified()),
52               hasInitializer(ignoringParenImpCasts(
53                   declRefExpr(hasDeclaration(Alloc1Func)))));
54 
55   Finder->addMatcher(
56       traverse(TK_AsIs, callExpr(callee(decl(anyOf(Alloc0Func, Alloc0FuncPtr))),
57                                  hasArgument(0, BadArg))
58                             .bind("Alloc")),
59       this);
60   Finder->addMatcher(
61       traverse(TK_AsIs, callExpr(callee(decl(anyOf(Alloc1Func, Alloc1FuncPtr))),
62                                  hasArgument(1, BadArg))
63                             .bind("Alloc")),
64       this);
65   Finder->addMatcher(
66       traverse(TK_AsIs,
67                cxxNewExpr(isArray(), hasArraySize(BadArg)).bind("Alloc")),
68       this);
69 }
70 
check(const MatchFinder::MatchResult & Result)71 void MisplacedOperatorInStrlenInAllocCheck::check(
72     const MatchFinder::MatchResult &Result) {
73   const Expr *Alloc = Result.Nodes.getNodeAs<CallExpr>("Alloc");
74   if (!Alloc)
75     Alloc = Result.Nodes.getNodeAs<CXXNewExpr>("Alloc");
76   assert(Alloc && "Matched node bound by 'Alloc' should be either 'CallExpr'"
77          " or 'CXXNewExpr'");
78 
79   const auto *StrLen = Result.Nodes.getNodeAs<CallExpr>("StrLen");
80   const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("BinOp");
81 
82   const StringRef StrLenText = Lexer::getSourceText(
83       CharSourceRange::getTokenRange(StrLen->getSourceRange()),
84       *Result.SourceManager, getLangOpts());
85   const StringRef Arg0Text = Lexer::getSourceText(
86       CharSourceRange::getTokenRange(StrLen->getArg(0)->getSourceRange()),
87       *Result.SourceManager, getLangOpts());
88   const StringRef StrLenBegin = StrLenText.substr(0, StrLenText.find(Arg0Text));
89   const StringRef StrLenEnd = StrLenText.substr(
90       StrLenText.find(Arg0Text) + Arg0Text.size(), StrLenText.size());
91 
92   const StringRef LHSText = Lexer::getSourceText(
93       CharSourceRange::getTokenRange(BinOp->getLHS()->getSourceRange()),
94       *Result.SourceManager, getLangOpts());
95   const StringRef RHSText = Lexer::getSourceText(
96       CharSourceRange::getTokenRange(BinOp->getRHS()->getSourceRange()),
97       *Result.SourceManager, getLangOpts());
98 
99   auto Hint = FixItHint::CreateReplacement(
100       StrLen->getSourceRange(),
101       (StrLenBegin + LHSText + StrLenEnd + " + " + RHSText).str());
102 
103   diag(Alloc->getBeginLoc(),
104        "addition operator is applied to the argument of %0 instead of its "
105        "result")
106       << StrLen->getDirectCallee()->getName() << Hint;
107 }
108 
109 } // namespace clang::tidy::bugprone
110