//===--- MisplacedOperatorInStrlenInAllocCheck.cpp - clang-tidy------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "MisplacedOperatorInStrlenInAllocCheck.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" using namespace clang::ast_matchers; namespace clang { namespace tidy { namespace bugprone { void MisplacedOperatorInStrlenInAllocCheck::registerMatchers( MatchFinder *Finder) { const auto StrLenFunc = functionDecl(anyOf( hasName("::strlen"), hasName("::std::strlen"), hasName("::strnlen"), hasName("::std::strnlen"), hasName("::strnlen_s"), hasName("::std::strnlen_s"), hasName("::wcslen"), hasName("::std::wcslen"), hasName("::wcsnlen"), hasName("::std::wcsnlen"), hasName("::wcsnlen_s"), hasName("std::wcsnlen_s"))); const auto BadUse = callExpr(callee(StrLenFunc), hasAnyArgument(ignoringImpCasts( binaryOperator( hasOperatorName("+"), hasRHS(ignoringParenImpCasts(integerLiteral(equals(1))))) .bind("BinOp")))) .bind("StrLen"); const auto BadArg = anyOf( allOf(unless(binaryOperator( hasOperatorName("+"), hasLHS(BadUse), hasRHS(ignoringParenImpCasts(integerLiteral(equals(1)))))), hasDescendant(BadUse)), BadUse); const auto Alloc0Func = functionDecl(anyOf(hasName("::malloc"), hasName("std::malloc"), hasName("::alloca"), hasName("std::alloca"))); const auto Alloc1Func = functionDecl(anyOf(hasName("::calloc"), hasName("std::calloc"), hasName("::realloc"), hasName("std::realloc"))); const auto Alloc0FuncPtr = varDecl(hasType(isConstQualified()), hasInitializer(ignoringParenImpCasts( declRefExpr(hasDeclaration(Alloc0Func))))); const auto Alloc1FuncPtr = varDecl(hasType(isConstQualified()), hasInitializer(ignoringParenImpCasts( declRefExpr(hasDeclaration(Alloc1Func))))); Finder->addMatcher(callExpr(callee(decl(anyOf(Alloc0Func, Alloc0FuncPtr))), hasArgument(0, BadArg)) .bind("Alloc"), this); Finder->addMatcher(callExpr(callee(decl(anyOf(Alloc1Func, Alloc1FuncPtr))), hasArgument(1, BadArg)) .bind("Alloc"), this); Finder->addMatcher( cxxNewExpr(isArray(), hasArraySize(BadArg)).bind("Alloc"), this); } void MisplacedOperatorInStrlenInAllocCheck::check( const MatchFinder::MatchResult &Result) { const Expr *Alloc = Result.Nodes.getNodeAs("Alloc"); if (!Alloc) Alloc = Result.Nodes.getNodeAs("Alloc"); assert(Alloc && "Matched node bound by 'Alloc' shoud be either 'CallExpr'" " or 'CXXNewExpr'"); const auto *StrLen = Result.Nodes.getNodeAs("StrLen"); const auto *BinOp = Result.Nodes.getNodeAs("BinOp"); const StringRef StrLenText = Lexer::getSourceText( CharSourceRange::getTokenRange(StrLen->getSourceRange()), *Result.SourceManager, getLangOpts()); const StringRef Arg0Text = Lexer::getSourceText( CharSourceRange::getTokenRange(StrLen->getArg(0)->getSourceRange()), *Result.SourceManager, getLangOpts()); const StringRef StrLenBegin = StrLenText.substr(0, StrLenText.find(Arg0Text)); const StringRef StrLenEnd = StrLenText.substr( StrLenText.find(Arg0Text) + Arg0Text.size(), StrLenText.size()); const StringRef LHSText = Lexer::getSourceText( CharSourceRange::getTokenRange(BinOp->getLHS()->getSourceRange()), *Result.SourceManager, getLangOpts()); const StringRef RHSText = Lexer::getSourceText( CharSourceRange::getTokenRange(BinOp->getRHS()->getSourceRange()), *Result.SourceManager, getLangOpts()); auto Hint = FixItHint::CreateReplacement( StrLen->getSourceRange(), (StrLenBegin + LHSText + StrLenEnd + " + " + RHSText).str()); diag(Alloc->getBeginLoc(), "addition operator is applied to the argument of %0 instead of its " "result") << StrLen->getDirectCallee()->getName() << Hint; } } // namespace bugprone } // namespace tidy } // namespace clang