1 //===--- MisplacedPointerArithmeticInAllocCheck.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 "MisplacedPointerArithmeticInAllocCheck.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 MisplacedPointerArithmeticInAllocCheck::registerMatchers(
19     MatchFinder *Finder) {
20   const auto AllocFunc =
21       functionDecl(hasAnyName("::malloc", "std::malloc", "::alloca", "::calloc",
22                               "std::calloc", "::realloc", "std::realloc"));
23 
24   const auto AllocFuncPtr =
25       varDecl(hasType(isConstQualified()),
26               hasInitializer(ignoringParenImpCasts(
27                   declRefExpr(hasDeclaration(AllocFunc)))));
28 
29   const auto AdditiveOperator = binaryOperator(hasAnyOperatorName("+", "-"));
30 
31   const auto IntExpr = expr(hasType(isInteger()));
32 
33   const auto AllocCall = callExpr(callee(decl(anyOf(AllocFunc, AllocFuncPtr))));
34 
35   Finder->addMatcher(
36       binaryOperator(
37           AdditiveOperator,
38           hasLHS(anyOf(AllocCall, castExpr(hasSourceExpression(AllocCall)))),
39           hasRHS(IntExpr))
40           .bind("PtrArith"),
41       this);
42 
43   const auto New = cxxNewExpr(unless(isArray()));
44 
45   Finder->addMatcher(binaryOperator(AdditiveOperator,
46                                     hasLHS(anyOf(New, castExpr(New))),
47                                     hasRHS(IntExpr))
48                          .bind("PtrArith"),
49                      this);
50 
51   const auto ArrayNew = cxxNewExpr(isArray());
52 
53   Finder->addMatcher(binaryOperator(AdditiveOperator,
54                                     hasLHS(anyOf(ArrayNew, castExpr(ArrayNew))),
55                                     hasRHS(IntExpr))
56                          .bind("PtrArith"),
57                      this);
58 }
59 
check(const MatchFinder::MatchResult & Result)60 void MisplacedPointerArithmeticInAllocCheck::check(
61     const MatchFinder::MatchResult &Result) {
62   const auto *PtrArith = Result.Nodes.getNodeAs<BinaryOperator>("PtrArith");
63   const Expr *AllocExpr = PtrArith->getLHS()->IgnoreParenCasts();
64   std::string CallName;
65 
66   if (const auto *Call = dyn_cast<CallExpr>(AllocExpr)) {
67     const NamedDecl *Func = Call->getDirectCallee();
68     if (!Func) {
69       Func = cast<NamedDecl>(Call->getCalleeDecl());
70     }
71     CallName = Func->getName().str();
72   } else {
73     const auto *New = cast<CXXNewExpr>(AllocExpr);
74     if (New->isArray()) {
75       CallName = "operator new[]";
76     } else {
77       const auto *CtrE = New->getConstructExpr();
78       if (!CtrE || !CtrE->getArg(CtrE->getNumArgs() - 1)
79                                      ->getType()
80                                      ->isIntegralOrEnumerationType())
81         return;
82       CallName = "operator new";
83     }
84   }
85 
86   const SourceRange OldRParen = SourceRange(PtrArith->getLHS()->getEndLoc());
87   const StringRef RParen =
88       Lexer::getSourceText(CharSourceRange::getTokenRange(OldRParen),
89                            *Result.SourceManager, getLangOpts());
90   const SourceLocation NewRParen = Lexer::getLocForEndOfToken(
91       PtrArith->getEndLoc(), 0, *Result.SourceManager, getLangOpts());
92 
93   diag(PtrArith->getBeginLoc(),
94        "arithmetic operation is applied to the result of %0() instead of its "
95        "size-like argument")
96       << CallName << FixItHint::CreateRemoval(OldRParen)
97       << FixItHint::CreateInsertion(NewRParen, RParen);
98 }
99 
100 } // namespace clang::tidy::bugprone
101