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