clang-tools  4.0.0
MoveForwardingReferenceCheck.cpp
Go to the documentation of this file.
1 //===--- MoveForwardingReferenceCheck.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 
11 #include "clang/Lex/Lexer.h"
12 #include "llvm/Support/raw_ostream.h"
13 
14 #include <algorithm>
15 
16 using namespace clang::ast_matchers;
17 
18 namespace clang {
19 namespace tidy {
20 namespace misc {
21 
22 static void replaceMoveWithForward(const UnresolvedLookupExpr *Callee,
23  const ParmVarDecl *ParmVar,
24  const TemplateTypeParmDecl *TypeParmDecl,
25  DiagnosticBuilder &Diag,
26  const ASTContext &Context) {
27  const SourceManager &SM = Context.getSourceManager();
28  const LangOptions &LangOpts = Context.getLangOpts();
29 
30  CharSourceRange CallRange =
31  Lexer::makeFileCharRange(CharSourceRange::getTokenRange(
32  Callee->getLocStart(), Callee->getLocEnd()),
33  SM, LangOpts);
34 
35  if (CallRange.isValid()) {
36  const std::string TypeName =
37  TypeParmDecl->getIdentifier()
38  ? TypeParmDecl->getName().str()
39  : (llvm::Twine("decltype(") + ParmVar->getName() + ")").str();
40 
41  const std::string ForwardName =
42  (llvm::Twine("forward<") + TypeName + ">").str();
43 
44  // Create a replacement only if we see a "standard" way of calling
45  // std::move(). This will hopefully prevent erroneous replacements if the
46  // code does unusual things (e.g. create an alias for std::move() in
47  // another namespace).
48  NestedNameSpecifier *NNS = Callee->getQualifier();
49  if (!NNS) {
50  // Called as "move" (i.e. presumably the code had a "using std::move;").
51  // We still conservatively put a "std::" in front of the forward because
52  // we don't know whether the code also had a "using std::forward;".
53  Diag << FixItHint::CreateReplacement(CallRange, "std::" + ForwardName);
54  } else if (const NamespaceDecl *Namespace = NNS->getAsNamespace()) {
55  if (Namespace->getName() == "std") {
56  if (!NNS->getPrefix()) {
57  // Called as "std::move".
58  Diag << FixItHint::CreateReplacement(CallRange,
59  "std::" + ForwardName);
60  } else if (NNS->getPrefix()->getKind() == NestedNameSpecifier::Global) {
61  // Called as "::std::move".
62  Diag << FixItHint::CreateReplacement(CallRange,
63  "::std::" + ForwardName);
64  }
65  }
66  }
67  }
68 }
69 
70 void MoveForwardingReferenceCheck::registerMatchers(MatchFinder *Finder) {
71  if (!getLangOpts().CPlusPlus11)
72  return;
73 
74  // Matches a ParmVarDecl for a forwarding reference, i.e. a non-const rvalue
75  // reference of a function template parameter type.
76  auto ForwardingReferenceParmMatcher =
77  parmVarDecl(
78  hasType(qualType(rValueReferenceType(),
79  references(templateTypeParmType(hasDeclaration(
80  templateTypeParmDecl().bind("type-parm-decl")))),
81  unless(references(qualType(isConstQualified()))))))
82  .bind("parm-var");
83 
84  Finder->addMatcher(
85  callExpr(callee(unresolvedLookupExpr(
86  hasAnyDeclaration(namedDecl(
87  hasUnderlyingDecl(hasName("::std::move")))))
88  .bind("lookup")),
89  argumentCountIs(1),
90  hasArgument(0, ignoringParenImpCasts(declRefExpr(
91  to(ForwardingReferenceParmMatcher)))))
92  .bind("call-move"),
93  this);
94 }
95 
96 void MoveForwardingReferenceCheck::check(
97  const MatchFinder::MatchResult &Result) {
98  const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");
99  const auto *UnresolvedLookup =
100  Result.Nodes.getNodeAs<UnresolvedLookupExpr>("lookup");
101  const auto *ParmVar = Result.Nodes.getNodeAs<ParmVarDecl>("parm-var");
102  const auto *TypeParmDecl =
103  Result.Nodes.getNodeAs<TemplateTypeParmDecl>("type-parm-decl");
104 
105  // Get the FunctionDecl and FunctionTemplateDecl containing the function
106  // parameter.
107  const auto *FuncForParam = dyn_cast<FunctionDecl>(ParmVar->getDeclContext());
108  if (!FuncForParam)
109  return;
110  const FunctionTemplateDecl *FuncTemplate =
111  FuncForParam->getDescribedFunctionTemplate();
112  if (!FuncTemplate)
113  return;
114 
115  // Check that the template type parameter belongs to the same function
116  // template as the function parameter of that type. (This implies that type
117  // deduction will happen on the type.)
118  const TemplateParameterList *Params = FuncTemplate->getTemplateParameters();
119  if (!std::count(Params->begin(), Params->end(), TypeParmDecl))
120  return;
121 
122  auto Diag = diag(CallMove->getExprLoc(),
123  "forwarding reference passed to std::move(), which may "
124  "unexpectedly cause lvalues to be moved; use "
125  "std::forward() instead");
126 
127  replaceMoveWithForward(UnresolvedLookup, ParmVar, TypeParmDecl, Diag,
128  *Result.Context);
129 }
130 
131 } // namespace misc
132 } // namespace tidy
133 } // namespace clang
LangOptions LangOpts
Definition: ClangTidy.cpp:240
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:262
SourceManager & SM
ClangTidyContext & Context
Definition: ClangTidy.cpp:87
static void replaceMoveWithForward(const UnresolvedLookupExpr *Callee, const ParmVarDecl *ParmVar, const TemplateTypeParmDecl *TypeParmDecl, DiagnosticBuilder &Diag, const ASTContext &Context)
const NamedDecl * Result
Definition: USRFinder.cpp:162