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 10 #include "MoveForwardingReferenceCheck.h" 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 bugprone { 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 bugprone 132 } // namespace tidy 133 } // namespace clang 134