mirror of
https://github.com/Gericom/teak-llvm.git
synced 2025-06-19 03:25:54 -04:00

to reflect the new license. We understand that people may be surprised that we're moving the header entirely to discuss the new license. We checked this carefully with the Foundation's lawyer and we believe this is the correct approach. Essentially, all code in the project is now made available by the LLVM project under our new license, so you will see that the license headers include that license only. Some of our contributors have contributed code under our old license, and accordingly, we have retained a copy of our old license notice in the top-level files in each project and repository. llvm-svn: 351636
163 lines
6.4 KiB
C++
163 lines
6.4 KiB
C++
//===--- InefficientAlgorithmCheck.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 "InefficientAlgorithmCheck.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 performance {
|
|
|
|
static bool areTypesCompatible(QualType Left, QualType Right) {
|
|
if (const auto *LeftRefType = Left->getAs<ReferenceType>())
|
|
Left = LeftRefType->getPointeeType();
|
|
if (const auto *RightRefType = Right->getAs<ReferenceType>())
|
|
Right = RightRefType->getPointeeType();
|
|
return Left->getCanonicalTypeUnqualified() ==
|
|
Right->getCanonicalTypeUnqualified();
|
|
}
|
|
|
|
void InefficientAlgorithmCheck::registerMatchers(MatchFinder *Finder) {
|
|
// Only register the matchers for C++; the functionality currently does not
|
|
// provide any benefit to other languages, despite being benign.
|
|
if (!getLangOpts().CPlusPlus)
|
|
return;
|
|
|
|
const auto Algorithms =
|
|
hasAnyName("::std::find", "::std::count", "::std::equal_range",
|
|
"::std::lower_bound", "::std::upper_bound");
|
|
const auto ContainerMatcher = classTemplateSpecializationDecl(hasAnyName(
|
|
"::std::set", "::std::map", "::std::multiset", "::std::multimap",
|
|
"::std::unordered_set", "::std::unordered_map",
|
|
"::std::unordered_multiset", "::std::unordered_multimap"));
|
|
|
|
const auto Matcher =
|
|
callExpr(
|
|
callee(functionDecl(Algorithms)),
|
|
hasArgument(
|
|
0, cxxConstructExpr(has(ignoringParenImpCasts(cxxMemberCallExpr(
|
|
callee(cxxMethodDecl(hasName("begin"))),
|
|
on(declRefExpr(
|
|
hasDeclaration(decl().bind("IneffContObj")),
|
|
anyOf(hasType(ContainerMatcher.bind("IneffCont")),
|
|
hasType(pointsTo(
|
|
ContainerMatcher.bind("IneffContPtr")))))
|
|
.bind("IneffContExpr"))))))),
|
|
hasArgument(
|
|
1, cxxConstructExpr(has(ignoringParenImpCasts(cxxMemberCallExpr(
|
|
callee(cxxMethodDecl(hasName("end"))),
|
|
on(declRefExpr(
|
|
hasDeclaration(equalsBoundNode("IneffContObj"))))))))),
|
|
hasArgument(2, expr().bind("AlgParam")),
|
|
unless(isInTemplateInstantiation()))
|
|
.bind("IneffAlg");
|
|
|
|
Finder->addMatcher(Matcher, this);
|
|
}
|
|
|
|
void InefficientAlgorithmCheck::check(const MatchFinder::MatchResult &Result) {
|
|
const auto *AlgCall = Result.Nodes.getNodeAs<CallExpr>("IneffAlg");
|
|
const auto *IneffCont =
|
|
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("IneffCont");
|
|
bool PtrToContainer = false;
|
|
if (!IneffCont) {
|
|
IneffCont =
|
|
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("IneffContPtr");
|
|
PtrToContainer = true;
|
|
}
|
|
const llvm::StringRef IneffContName = IneffCont->getName();
|
|
const bool Unordered =
|
|
IneffContName.find("unordered") != llvm::StringRef::npos;
|
|
const bool Maplike = IneffContName.find("map") != llvm::StringRef::npos;
|
|
|
|
// Store if the key type of the container is compatible with the value
|
|
// that is searched for.
|
|
QualType ValueType = AlgCall->getArg(2)->getType();
|
|
QualType KeyType =
|
|
IneffCont->getTemplateArgs()[0].getAsType().getCanonicalType();
|
|
const bool CompatibleTypes = areTypesCompatible(KeyType, ValueType);
|
|
|
|
// Check if the comparison type for the algorithm and the container matches.
|
|
if (AlgCall->getNumArgs() == 4 && !Unordered) {
|
|
const Expr *Arg = AlgCall->getArg(3);
|
|
const QualType AlgCmp =
|
|
Arg->getType().getUnqualifiedType().getCanonicalType();
|
|
const unsigned CmpPosition =
|
|
(IneffContName.find("map") == llvm::StringRef::npos) ? 1 : 2;
|
|
const QualType ContainerCmp = IneffCont->getTemplateArgs()[CmpPosition]
|
|
.getAsType()
|
|
.getUnqualifiedType()
|
|
.getCanonicalType();
|
|
if (AlgCmp != ContainerCmp) {
|
|
diag(Arg->getBeginLoc(),
|
|
"different comparers used in the algorithm and the container");
|
|
return;
|
|
}
|
|
}
|
|
|
|
const auto *AlgDecl = AlgCall->getDirectCallee();
|
|
if (!AlgDecl)
|
|
return;
|
|
|
|
if (Unordered && AlgDecl->getName().find("bound") != llvm::StringRef::npos)
|
|
return;
|
|
|
|
const auto *AlgParam = Result.Nodes.getNodeAs<Expr>("AlgParam");
|
|
const auto *IneffContExpr = Result.Nodes.getNodeAs<Expr>("IneffContExpr");
|
|
FixItHint Hint;
|
|
|
|
SourceManager &SM = *Result.SourceManager;
|
|
LangOptions LangOpts = getLangOpts();
|
|
|
|
CharSourceRange CallRange =
|
|
CharSourceRange::getTokenRange(AlgCall->getSourceRange());
|
|
|
|
// FIXME: Create a common utility to extract a file range that the given token
|
|
// sequence is exactly spelled at (without macro argument expansions etc.).
|
|
// We can't use Lexer::makeFileCharRange here, because for
|
|
//
|
|
// #define F(x) x
|
|
// x(a b c);
|
|
//
|
|
// it will return "x(a b c)", when given the range "a"-"c". It makes sense for
|
|
// removals, but not for replacements.
|
|
//
|
|
// This code is over-simplified, but works for many real cases.
|
|
if (SM.isMacroArgExpansion(CallRange.getBegin()) &&
|
|
SM.isMacroArgExpansion(CallRange.getEnd())) {
|
|
CallRange.setBegin(SM.getSpellingLoc(CallRange.getBegin()));
|
|
CallRange.setEnd(SM.getSpellingLoc(CallRange.getEnd()));
|
|
}
|
|
|
|
if (!CallRange.getBegin().isMacroID() && !Maplike && CompatibleTypes) {
|
|
StringRef ContainerText = Lexer::getSourceText(
|
|
CharSourceRange::getTokenRange(IneffContExpr->getSourceRange()), SM,
|
|
LangOpts);
|
|
StringRef ParamText = Lexer::getSourceText(
|
|
CharSourceRange::getTokenRange(AlgParam->getSourceRange()), SM,
|
|
LangOpts);
|
|
std::string ReplacementText =
|
|
(llvm::Twine(ContainerText) + (PtrToContainer ? "->" : ".") +
|
|
AlgDecl->getName() + "(" + ParamText + ")")
|
|
.str();
|
|
Hint = FixItHint::CreateReplacement(CallRange, ReplacementText);
|
|
}
|
|
|
|
diag(AlgCall->getBeginLoc(),
|
|
"this STL algorithm call should be replaced with a container method")
|
|
<< Hint;
|
|
}
|
|
|
|
} // namespace performance
|
|
} // namespace tidy
|
|
} // namespace clang
|