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