blob: 906140db54e3ad4c6634a8dfced334301dd87692 [file] [log] [blame]
Gabor Horvath3880bee2015-02-07 19:54:19 +00001//===--- InefficientAlgorithmCheck.cpp - clang-tidy------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "InefficientAlgorithmCheck.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
14
15using namespace clang::ast_matchers;
16
17namespace clang {
18namespace tidy {
19
20void InefficientAlgorithmCheck::registerMatchers(MatchFinder *Finder) {
21 const std::string Algorithms =
Gabor Horvath03295192015-02-12 18:19:34 +000022 "^::std::(find|count|equal_range|lower_blound|upper_bound)$";
Gabor Horvath3880bee2015-02-07 19:54:19 +000023 const auto ContainerMatcher = classTemplateSpecializationDecl(
Gabor Horvath03295192015-02-12 18:19:34 +000024 matchesName("^::std::(unordered_)?(multi)?(set|map)$"));
Gabor Horvath3880bee2015-02-07 19:54:19 +000025 const auto Matcher =
26 callExpr(
27 callee(functionDecl(matchesName(Algorithms))),
28 hasArgument(
29 0, constructExpr(has(memberCallExpr(
30 callee(methodDecl(hasName("begin"))),
31 on(declRefExpr(
32 hasDeclaration(decl().bind("IneffContObj")),
33 anyOf(hasType(ContainerMatcher.bind("IneffCont")),
34 hasType(pointsTo(
35 ContainerMatcher.bind("IneffContPtr")))))
36 .bind("IneffContExpr")))))),
37 hasArgument(1, constructExpr(has(memberCallExpr(
38 callee(methodDecl(hasName("end"))),
39 on(declRefExpr(hasDeclaration(
40 equalsBoundNode("IneffContObj")))))))),
41 hasArgument(2, expr().bind("AlgParam")),
42 unless(isInTemplateInstantiation())).bind("IneffAlg");
43
44 Finder->addMatcher(Matcher, this);
45}
46
47void InefficientAlgorithmCheck::check(const MatchFinder::MatchResult &Result) {
48 const auto *AlgCall = Result.Nodes.getNodeAs<CallExpr>("IneffAlg");
49 const auto *IneffCont =
50 Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("IneffCont");
51 bool PtrToContainer = false;
52 if (!IneffCont) {
53 IneffCont =
54 Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("IneffContPtr");
55 PtrToContainer = true;
56 }
57 const llvm::StringRef IneffContName = IneffCont->getName();
58 const bool Unordered =
59 IneffContName.find("unordered") != llvm::StringRef::npos;
60
61 // Check if the comparison type for the algorithm and the container matches.
62 if (AlgCall->getNumArgs() == 4 && !Unordered) {
63 const Expr *Arg = AlgCall->getArg(3);
64 const QualType AlgCmp =
65 Arg->getType().getUnqualifiedType().getCanonicalType();
66 const unsigned CmpPosition =
67 (IneffContName.find("map") == llvm::StringRef::npos) ? 1 : 2;
68 const QualType ContainerCmp = IneffCont->getTemplateArgs()[CmpPosition]
69 .getAsType()
70 .getUnqualifiedType()
71 .getCanonicalType();
72 if (AlgCmp != ContainerCmp) {
73 diag(Arg->getLocStart(),
74 "different comparers used in the algorithm and the container");
75 return;
76 }
77 }
78
79 const auto *AlgDecl = AlgCall->getDirectCallee();
80 if (!AlgDecl)
81 return;
82
83 if (Unordered && AlgDecl->getName().find("bound") != llvm::StringRef::npos)
84 return;
85
86 const auto *AlgParam = Result.Nodes.getNodeAs<Expr>("AlgParam");
87 const auto *IneffContExpr = Result.Nodes.getNodeAs<Expr>("IneffContExpr");
88 FixItHint Hint;
89
90 if (!AlgCall->getLocStart().isMacroID()) {
91 std::string ReplacementText =
92 (llvm::Twine(Lexer::getSourceText(
93 CharSourceRange::getTokenRange(IneffContExpr->getSourceRange()),
94 *Result.SourceManager, Result.Context->getLangOpts())) +
95 (PtrToContainer ? "->" : ".") + AlgDecl->getName() + "(" +
96 Lexer::getSourceText(
97 CharSourceRange::getTokenRange(AlgParam->getSourceRange()),
98 *Result.SourceManager, Result.Context->getLangOpts()) +
99 ")").str();
100 Hint = FixItHint::CreateReplacement(AlgCall->getSourceRange(),
101 ReplacementText);
102 }
103
104 diag(AlgCall->getLocStart(),
105 "this STL algorithm call should be replaced with a container method")
106 << Hint;
107}
108
109} // namespace tidy
110} // namespace clang