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