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