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