blob: 16401b7d4758f3c3f0b102e3b56a0d50dc1001e9 [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
Gabor Horvath21b76ba2015-02-17 21:45:38 +000020static bool areTypesCompatible(QualType Left, QualType Right) {
21 if (const auto *LeftRefType = Left->getAs<ReferenceType>())
22 Left = LeftRefType->getPointeeType();
23 if (const auto *RightRefType = Right->getAs<ReferenceType>())
24 Right = RightRefType->getPointeeType();
25 return Left->getCanonicalTypeUnqualified() ==
26 Right->getCanonicalTypeUnqualified();
27}
28
Gabor Horvath3880bee2015-02-07 19:54:19 +000029void InefficientAlgorithmCheck::registerMatchers(MatchFinder *Finder) {
30 const std::string Algorithms =
Gabor Horvath21b76ba2015-02-17 21:45:38 +000031 "^::std::(find|count|equal_range|lower_bound|upper_bound)$";
Gabor Horvath3880bee2015-02-07 19:54:19 +000032 const auto ContainerMatcher = classTemplateSpecializationDecl(
Gabor Horvath03295192015-02-12 18:19:34 +000033 matchesName("^::std::(unordered_)?(multi)?(set|map)$"));
Gabor Horvath3880bee2015-02-07 19:54:19 +000034 const auto Matcher =
35 callExpr(
36 callee(functionDecl(matchesName(Algorithms))),
37 hasArgument(
38 0, constructExpr(has(memberCallExpr(
39 callee(methodDecl(hasName("begin"))),
40 on(declRefExpr(
41 hasDeclaration(decl().bind("IneffContObj")),
42 anyOf(hasType(ContainerMatcher.bind("IneffCont")),
43 hasType(pointsTo(
44 ContainerMatcher.bind("IneffContPtr")))))
45 .bind("IneffContExpr")))))),
46 hasArgument(1, constructExpr(has(memberCallExpr(
47 callee(methodDecl(hasName("end"))),
48 on(declRefExpr(hasDeclaration(
49 equalsBoundNode("IneffContObj")))))))),
50 hasArgument(2, expr().bind("AlgParam")),
51 unless(isInTemplateInstantiation())).bind("IneffAlg");
52
53 Finder->addMatcher(Matcher, this);
54}
55
56void InefficientAlgorithmCheck::check(const MatchFinder::MatchResult &Result) {
57 const auto *AlgCall = Result.Nodes.getNodeAs<CallExpr>("IneffAlg");
58 const auto *IneffCont =
59 Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("IneffCont");
60 bool PtrToContainer = false;
61 if (!IneffCont) {
62 IneffCont =
63 Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("IneffContPtr");
64 PtrToContainer = true;
65 }
66 const llvm::StringRef IneffContName = IneffCont->getName();
67 const bool Unordered =
68 IneffContName.find("unordered") != llvm::StringRef::npos;
Gabor Horvath21b76ba2015-02-17 21:45:38 +000069 const bool Maplike = IneffContName.find("map") != llvm::StringRef::npos;
70
71 // Store if the key type of the container is compatible with the value
72 // that is searched for.
73 QualType ValueType = AlgCall->getArg(2)->getType();
74 QualType KeyType =
75 IneffCont->getTemplateArgs()[0].getAsType().getCanonicalType();
76 const bool CompatibleTypes = areTypesCompatible(KeyType, ValueType);
Gabor Horvath3880bee2015-02-07 19:54:19 +000077
78 // Check if the comparison type for the algorithm and the container matches.
79 if (AlgCall->getNumArgs() == 4 && !Unordered) {
80 const Expr *Arg = AlgCall->getArg(3);
81 const QualType AlgCmp =
82 Arg->getType().getUnqualifiedType().getCanonicalType();
83 const unsigned CmpPosition =
84 (IneffContName.find("map") == llvm::StringRef::npos) ? 1 : 2;
85 const QualType ContainerCmp = IneffCont->getTemplateArgs()[CmpPosition]
86 .getAsType()
87 .getUnqualifiedType()
88 .getCanonicalType();
89 if (AlgCmp != ContainerCmp) {
90 diag(Arg->getLocStart(),
91 "different comparers used in the algorithm and the container");
92 return;
93 }
94 }
95
96 const auto *AlgDecl = AlgCall->getDirectCallee();
97 if (!AlgDecl)
98 return;
99
100 if (Unordered && AlgDecl->getName().find("bound") != llvm::StringRef::npos)
101 return;
102
103 const auto *AlgParam = Result.Nodes.getNodeAs<Expr>("AlgParam");
104 const auto *IneffContExpr = Result.Nodes.getNodeAs<Expr>("IneffContExpr");
105 FixItHint Hint;
106
Gabor Horvath21b76ba2015-02-17 21:45:38 +0000107 if (!AlgCall->getLocStart().isMacroID() && !Maplike && CompatibleTypes) {
Gabor Horvath3880bee2015-02-07 19:54:19 +0000108 std::string ReplacementText =
109 (llvm::Twine(Lexer::getSourceText(
110 CharSourceRange::getTokenRange(IneffContExpr->getSourceRange()),
111 *Result.SourceManager, Result.Context->getLangOpts())) +
112 (PtrToContainer ? "->" : ".") + AlgDecl->getName() + "(" +
113 Lexer::getSourceText(
114 CharSourceRange::getTokenRange(AlgParam->getSourceRange()),
115 *Result.SourceManager, Result.Context->getLangOpts()) +
116 ")").str();
117 Hint = FixItHint::CreateReplacement(AlgCall->getSourceRange(),
118 ReplacementText);
119 }
120
121 diag(AlgCall->getLocStart(),
122 "this STL algorithm call should be replaced with a container method")
123 << Hint;
124}
125
126} // namespace tidy
127} // namespace clang