blob: f8db0b3ef945ee344bf841ed46fd74313aa0b0c4 [file] [log] [blame]
//===--- MisplacedOperatorInStrlenInAllocCheck.cpp - clang-tidy------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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(allOf(hasOperatorName("+"),
hasRHS(ignoringParenImpCasts(
integerLiteral(equals(1))))))
.bind("BinOp"))))
.bind("StrLen");
const auto BadArg = anyOf(
allOf(hasDescendant(BadUse),
unless(binaryOperator(allOf(
hasOperatorName("+"), hasLHS(BadUse),
hasRHS(ignoringParenImpCasts(integerLiteral(equals(1)))))))),
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<CallExpr>("Alloc");
if (!Alloc)
Alloc = Result.Nodes.getNodeAs<CXXNewExpr>("Alloc");
assert(Alloc && "Matched node bound by 'Alloc' shoud 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->getLocStart(),
"addition operator is applied to the argument of %0 instead of its "
"result") << StrLen->getDirectCallee()->getName() << Hint;
}
} // namespace bugprone
} // namespace tidy
} // namespace clang