MoveForwardingReferenceCheck.cpp 5.2 KB

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