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(ast_type_traits::TK_AsIs, 59 callExpr(callee(decl(anyOf(Alloc0Func, Alloc0FuncPtr))), 60 hasArgument(0, BadArg)) 61 .bind("Alloc")), 62 this); 63 Finder->addMatcher( 64 traverse(ast_type_traits::TK_AsIs, 65 callExpr(callee(decl(anyOf(Alloc1Func, Alloc1FuncPtr))), 66 hasArgument(1, BadArg)) 67 .bind("Alloc")), 68 this); 69 Finder->addMatcher( 70 traverse(ast_type_traits::TK_AsIs, 71 cxxNewExpr(isArray(), hasArraySize(BadArg)).bind("Alloc")), 72 this); 73 } 74 75 void MisplacedOperatorInStrlenInAllocCheck::check( 76 const MatchFinder::MatchResult &Result) { 77 const Expr *Alloc = Result.Nodes.getNodeAs<CallExpr>("Alloc"); 78 if (!Alloc) 79 Alloc = Result.Nodes.getNodeAs<CXXNewExpr>("Alloc"); 80 assert(Alloc && "Matched node bound by 'Alloc' should be either 'CallExpr'" 81 " or 'CXXNewExpr'"); 82 83 const auto *StrLen = Result.Nodes.getNodeAs<CallExpr>("StrLen"); 84 const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("BinOp"); 85 86 const StringRef StrLenText = Lexer::getSourceText( 87 CharSourceRange::getTokenRange(StrLen->getSourceRange()), 88 *Result.SourceManager, getLangOpts()); 89 const StringRef Arg0Text = Lexer::getSourceText( 90 CharSourceRange::getTokenRange(StrLen->getArg(0)->getSourceRange()), 91 *Result.SourceManager, getLangOpts()); 92 const StringRef StrLenBegin = StrLenText.substr(0, StrLenText.find(Arg0Text)); 93 const StringRef StrLenEnd = StrLenText.substr( 94 StrLenText.find(Arg0Text) + Arg0Text.size(), StrLenText.size()); 95 96 const StringRef LHSText = Lexer::getSourceText( 97 CharSourceRange::getTokenRange(BinOp->getLHS()->getSourceRange()), 98 *Result.SourceManager, getLangOpts()); 99 const StringRef RHSText = Lexer::getSourceText( 100 CharSourceRange::getTokenRange(BinOp->getRHS()->getSourceRange()), 101 *Result.SourceManager, getLangOpts()); 102 103 auto Hint = FixItHint::CreateReplacement( 104 StrLen->getSourceRange(), 105 (StrLenBegin + LHSText + StrLenEnd + " + " + RHSText).str()); 106 107 diag(Alloc->getBeginLoc(), 108 "addition operator is applied to the argument of %0 instead of its " 109 "result") 110 << StrLen->getDirectCallee()->getName() << Hint; 111 } 112 113 } // namespace bugprone 114 } // namespace tidy 115 } // namespace clang 116