| //===--- 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" |
| #include "clang/Lex/Lexer.h" |
| |
| using namespace clang::ast_matchers; |
| |
| namespace clang { |
| namespace tidy { |
| namespace bugprone { |
| |
| void MisplacedOperatorInStrlenInAllocCheck::registerMatchers( |
| MatchFinder *Finder) { |
| const auto StrLenFunc = functionDecl(hasAnyName( |
| "::strlen", "::std::strlen", "::strnlen", "::std::strnlen", "::strnlen_s", |
| "::std::strnlen_s", "::wcslen", "::std::wcslen", "::wcsnlen", |
| "::std::wcsnlen", "::wcsnlen_s", "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( |
| hasAnyName("::malloc", "std::malloc", "::alloca", "std::alloca")); |
| const auto Alloc1Func = functionDecl( |
| hasAnyName("::calloc", "std::calloc", "::realloc", "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( |
| traverse(TK_AsIs, callExpr(callee(decl(anyOf(Alloc0Func, Alloc0FuncPtr))), |
| hasArgument(0, BadArg)) |
| .bind("Alloc")), |
| this); |
| Finder->addMatcher( |
| traverse(TK_AsIs, callExpr(callee(decl(anyOf(Alloc1Func, Alloc1FuncPtr))), |
| hasArgument(1, BadArg)) |
| .bind("Alloc")), |
| this); |
| Finder->addMatcher( |
| traverse(TK_AsIs, |
| cxxNewExpr(isArray(), hasArraySize(BadArg)).bind("Alloc")), |
| this); |
| } |
| |
| void MisplacedOperatorInStrlenInAllocCheck::check( |
| const MatchFinder::MatchResult &Result) { |
| const Expr *Alloc = Result.Nodes.getNodeAs<CallExpr>("Alloc"); |
| if (!Alloc) |
| Alloc = Result.Nodes.getNodeAs<CXXNewExpr>("Alloc"); |
| assert(Alloc && "Matched node bound by 'Alloc' should be either 'CallExpr'" |
| " or 'CXXNewExpr'"); |
| |
| const auto *StrLen = Result.Nodes.getNodeAs<CallExpr>("StrLen"); |
| const auto *BinOp = Result.Nodes.getNodeAs<BinaryOperator>("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 |