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