110 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//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 14using namespace clang::ast_matchers;15 16namespace clang::tidy::bugprone {17 18void 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 71void 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::bugprone110