clang-tools 6.0.0

AvoidBindCheck.cpp
Go to the documentation of this file.
1 //===--- AvoidBindCheck.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 "AvoidBindCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Basic/LLVM.h"
14 #include "clang/Basic/LangOptions.h"
15 #include "clang/Basic/SourceLocation.h"
16 #include "clang/Lex/Lexer.h"
17 #include "llvm/ADT/ArrayRef.h"
18 #include "llvm/ADT/SmallSet.h"
19 #include "llvm/ADT/SmallVector.h"
20 #include "llvm/ADT/STLExtras.h"
21 #include "llvm/ADT/StringRef.h"
22 #include "llvm/Support/Casting.h"
23 #include "llvm/Support/Regex.h"
24 #include "llvm/Support/raw_ostream.h"
25 #include <algorithm>
26 #include <cstddef>
27 #include <string>
28
29 using namespace clang::ast_matchers;
30
31 namespace clang {
32 namespace tidy {
33 namespace modernize {
34
35 namespace {
36
37 enum BindArgumentKind { BK_Temporary, BK_Placeholder, BK_CallExpr, BK_Other };
38
39 struct BindArgument {
40 StringRef Tokens;
41 BindArgumentKind Kind = BK_Other;
42 size_t PlaceHolderIndex = 0;
43 };
44
45 } // end namespace
46
47 static SmallVector<BindArgument, 4>
48 buildBindArguments(const MatchFinder::MatchResult &Result, const CallExpr *C) {
49 SmallVector<BindArgument, 4> BindArguments;
50 llvm::Regex MatchPlaceholder("^_([0-9]+)$");
51
52 // Start at index 1 as first argument to bind is the function name.
53 for (size_t I = 1, ArgCount = C->getNumArgs(); I < ArgCount; ++I) {
54 const Expr *E = C->getArg(I);
55 BindArgument B;
56 if (const auto *M = dyn_cast<MaterializeTemporaryExpr>(E)) {
57 const auto *TE = M->GetTemporaryExpr();
58 B.Kind = isa<CallExpr>(TE) ? BK_CallExpr : BK_Temporary;
59 }
60
61 B.Tokens = Lexer::getSourceText(
62 CharSourceRange::getTokenRange(E->getLocStart(), E->getLocEnd()),
63 *Result.SourceManager, Result.Context->getLangOpts());
64
65 SmallVector<StringRef, 2> Matches;
66 if (B.Kind == BK_Other && MatchPlaceholder.match(B.Tokens, &Matches)) {
67 B.Kind = BK_Placeholder;
68 B.PlaceHolderIndex = std::stoi(Matches[1]);
69 }
70 BindArguments.push_back(B);
71 }
72 return BindArguments;
73 }
74
75 static void addPlaceholderArgs(const ArrayRef<BindArgument> Args,
76 llvm::raw_ostream &Stream) {
77 auto MaxPlaceholderIt =
78 std::max_element(Args.begin(), Args.end(),
79 [](const BindArgument &B1, const BindArgument &B2) {
80 return B1.PlaceHolderIndex < B2.PlaceHolderIndex;
81 });
82
83 // Placeholders (if present) have index 1 or greater.
84 if (MaxPlaceholderIt == Args.end() || MaxPlaceholderIt->PlaceHolderIndex == 0)
85 return;
86
87 size_t PlaceholderCount = MaxPlaceholderIt->PlaceHolderIndex;
88 Stream << "(";
89 StringRef Delimiter = "";
90 for (size_t I = 1; I <= PlaceholderCount; ++I) {
91 Stream << Delimiter << "auto && arg" << I;
92 Delimiter = ", ";
93 }
94 Stream << ")";
95 }
96
97 static void addFunctionCallArgs(const ArrayRef<BindArgument> Args,
98 llvm::raw_ostream &Stream) {
99 StringRef Delimiter = "";
100 for (const auto &B : Args) {
101 if (B.PlaceHolderIndex)
102 Stream << Delimiter << "arg" << B.PlaceHolderIndex;
103 else
104 Stream << Delimiter << B.Tokens;
105 Delimiter = ", ";
106 }
107 }
108
109 static bool isPlaceHolderIndexRepeated(const ArrayRef<BindArgument> Args) {
110 llvm::SmallSet<size_t, 4> PlaceHolderIndices;
111 for (const BindArgument &B : Args) {
112 if (B.PlaceHolderIndex) {
113 if (!PlaceHolderIndices.insert(B.PlaceHolderIndex).second)
114 return true;
115 }
116 }
117 return false;
118 }
119
120 void AvoidBindCheck::registerMatchers(MatchFinder *Finder) {
121 if (!getLangOpts().CPlusPlus14) // Need C++14 for generic lambdas.
122 return;
123
124 Finder->addMatcher(
125 callExpr(
126 callee(namedDecl(hasName("::std::bind"))),
127 hasArgument(0, declRefExpr(to(functionDecl().bind("f"))).bind("ref")))
128 .bind("bind"),
129 this);
130 }
131
132 void AvoidBindCheck::check(const MatchFinder::MatchResult &Result) {
133 const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("bind");
134 auto Diag = diag(MatchedDecl->getLocStart(), "prefer a lambda to std::bind");
135
136 const auto Args = buildBindArguments(Result, MatchedDecl);
137
138 // Do not attempt to create fixits for nested call expressions.
139 // FIXME: Create lambda capture variables to capture output of calls.
140 // NOTE: Supporting nested std::bind will be more difficult due to placeholder
141 // sharing between outer and inner std:bind invocations.
142 if (llvm::any_of(Args,
143 [](const BindArgument &B) { return B.Kind == BK_CallExpr; }))
144 return;
145
146 // Do not attempt to create fixits when placeholders are reused.
147 // Unused placeholders are supported by requiring C++14 generic lambdas.
148 // FIXME: Support this case by deducing the common type.
149 if (isPlaceHolderIndexRepeated(Args))
150 return;
151
152 const auto *F = Result.Nodes.getNodeAs<FunctionDecl>("f");
153
154 // std::bind can support argument count mismatch between its arguments and the
155 // bound function's arguments. Do not attempt to generate a fixit for such
156 // cases.
157 // FIXME: Support this case by creating unused lambda capture variables.
158 if (F->getNumParams() != Args.size())
159 return;
160
161 std::string Buffer;
162 llvm::raw_string_ostream Stream(Buffer);
163
164 bool HasCapturedArgument = llvm::any_of(
165 Args, [](const BindArgument &B) { return B.Kind == BK_Other; });
166 const auto *Ref = Result.Nodes.getNodeAs<DeclRefExpr>("ref");
167 Stream << "[" << (HasCapturedArgument ? "=" : "") << "]";
168 addPlaceholderArgs(Args, Stream);
169 Stream << " { return ";
170 Ref->printPretty(Stream, nullptr, Result.Context->getPrintingPolicy());
171 Stream << "(";
172 addFunctionCallArgs(Args, Stream);
173 Stream << "); };";
174
175 Diag << FixItHint::CreateReplacement(MatchedDecl->getSourceRange(),
176 Stream.str());
177 }
178
179 } // namespace modernize
180 } // namespace tidy
181 } // namespace clang
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
AvoidBindCheck.h
clang::tidy::modernize::isPlaceHolderIndexRepeated
static bool isPlaceHolderIndexRepeated(const ArrayRef< BindArgument > Args)
Definition: AvoidBindCheck.cpp:109
clang::tidy::modernize::addFunctionCallArgs
static void addFunctionCallArgs(const ArrayRef< BindArgument > Args, llvm::raw_ostream &Stream)
Definition: AvoidBindCheck.cpp:97
clang::tidy::modernize::@237::BindArgumentKind
BindArgumentKind
Definition: AvoidBindCheck.cpp:37
Kind
BindArgumentKind Kind
Definition: AvoidBindCheck.cpp:41
Tokens
StringRef Tokens
Definition: AvoidBindCheck.cpp:40
clang::tidy::modernize::addPlaceholderArgs
static void addPlaceholderArgs(const ArrayRef< BindArgument > Args, llvm::raw_ostream &Stream)
Definition: AvoidBindCheck.cpp:75
clang
Definition: AndroidTidyModule.cpp:28
clang::tidy::modernize::buildBindArguments
static SmallVector< BindArgument, 4 > buildBindArguments(const MatchFinder::MatchResult &Result, const CallExpr *C)
Definition: AvoidBindCheck.cpp:48
PlaceHolderIndex
size_t PlaceHolderIndex
Definition: AvoidBindCheck.cpp:42