MoveConstArgCheck.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. //===--- MoveConstArgCheck.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 "MoveConstArgCheck.h"
  9. #include "clang/Lex/Lexer.h"
  10. using namespace clang::ast_matchers;
  11. namespace clang::tidy::performance {
  12. static void replaceCallWithArg(const CallExpr *Call, DiagnosticBuilder &Diag,
  13. const SourceManager &SM,
  14. const LangOptions &LangOpts) {
  15. const Expr *Arg = Call->getArg(0);
  16. CharSourceRange BeforeArgumentsRange = Lexer::makeFileCharRange(
  17. CharSourceRange::getCharRange(Call->getBeginLoc(), Arg->getBeginLoc()),
  18. SM, LangOpts);
  19. CharSourceRange AfterArgumentsRange = Lexer::makeFileCharRange(
  20. CharSourceRange::getCharRange(Call->getEndLoc(),
  21. Call->getEndLoc().getLocWithOffset(1)),
  22. SM, LangOpts);
  23. if (BeforeArgumentsRange.isValid() && AfterArgumentsRange.isValid()) {
  24. Diag << FixItHint::CreateRemoval(BeforeArgumentsRange)
  25. << FixItHint::CreateRemoval(AfterArgumentsRange);
  26. }
  27. }
  28. void MoveConstArgCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
  29. Options.store(Opts, "CheckTriviallyCopyableMove", CheckTriviallyCopyableMove);
  30. Options.store(Opts, "CheckMoveToConstRef", CheckMoveToConstRef);
  31. }
  32. void MoveConstArgCheck::registerMatchers(MatchFinder *Finder) {
  33. auto MoveCallMatcher =
  34. callExpr(callee(functionDecl(hasName("::std::move"))), argumentCountIs(1),
  35. unless(isInTemplateInstantiation()))
  36. .bind("call-move");
  37. Finder->addMatcher(MoveCallMatcher, this);
  38. auto ConstTypeParmMatcher =
  39. qualType(references(isConstQualified())).bind("invocation-parm-type");
  40. auto RValueTypeParmMatcher =
  41. qualType(rValueReferenceType()).bind("invocation-parm-type");
  42. // Matches respective ParmVarDecl for a CallExpr or CXXConstructExpr.
  43. auto ArgumentWithParamMatcher = forEachArgumentWithParam(
  44. MoveCallMatcher, parmVarDecl(anyOf(hasType(ConstTypeParmMatcher),
  45. hasType(RValueTypeParmMatcher)))
  46. .bind("invocation-parm"));
  47. // Matches respective types of arguments for a CallExpr or CXXConstructExpr
  48. // and it works on calls through function pointers as well.
  49. auto ArgumentWithParamTypeMatcher = forEachArgumentWithParamType(
  50. MoveCallMatcher, anyOf(ConstTypeParmMatcher, RValueTypeParmMatcher));
  51. Finder->addMatcher(
  52. invocation(anyOf(ArgumentWithParamMatcher, ArgumentWithParamTypeMatcher))
  53. .bind("receiving-expr"),
  54. this);
  55. }
  56. bool IsRValueReferenceParam(const Expr *Invocation,
  57. const QualType *InvocationParmType,
  58. const Expr *Arg) {
  59. if (Invocation && (*InvocationParmType)->isRValueReferenceType() &&
  60. Arg->isLValue()) {
  61. if (!Invocation->getType()->isRecordType())
  62. return true;
  63. else {
  64. if (const auto *ConstructCallExpr =
  65. dyn_cast<CXXConstructExpr>(Invocation)) {
  66. if (const auto *ConstructorDecl = ConstructCallExpr->getConstructor()) {
  67. if (!ConstructorDecl->isCopyOrMoveConstructor() &&
  68. !ConstructorDecl->isDefaultConstructor())
  69. return true;
  70. }
  71. }
  72. }
  73. }
  74. return false;
  75. }
  76. void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) {
  77. const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");
  78. const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>("receiving-expr");
  79. const auto *InvocationParm =
  80. Result.Nodes.getNodeAs<ParmVarDecl>("invocation-parm");
  81. const auto *InvocationParmType =
  82. Result.Nodes.getNodeAs<QualType>("invocation-parm-type");
  83. // Skipping matchers which have been matched.
  84. if (!ReceivingExpr && AlreadyCheckedMoves.contains(CallMove))
  85. return;
  86. if (ReceivingExpr)
  87. AlreadyCheckedMoves.insert(CallMove);
  88. const Expr *Arg = CallMove->getArg(0);
  89. SourceManager &SM = Result.Context->getSourceManager();
  90. CharSourceRange MoveRange =
  91. CharSourceRange::getCharRange(CallMove->getSourceRange());
  92. CharSourceRange FileMoveRange =
  93. Lexer::makeFileCharRange(MoveRange, SM, getLangOpts());
  94. if (!FileMoveRange.isValid())
  95. return;
  96. bool IsConstArg = Arg->getType().isConstQualified();
  97. bool IsTriviallyCopyable =
  98. Arg->getType().isTriviallyCopyableType(*Result.Context);
  99. if (IsConstArg || IsTriviallyCopyable) {
  100. if (const CXXRecordDecl *R = Arg->getType()->getAsCXXRecordDecl()) {
  101. // According to [expr.prim.lambda]p3, "whether the closure type is
  102. // trivially copyable" property can be changed by the implementation of
  103. // the language, so we shouldn't rely on it when issuing diagnostics.
  104. if (R->isLambda())
  105. return;
  106. // Don't warn when the type is not copyable.
  107. for (const auto *Ctor : R->ctors()) {
  108. if (Ctor->isCopyConstructor() && Ctor->isDeleted())
  109. return;
  110. }
  111. }
  112. if (!IsConstArg && IsTriviallyCopyable && !CheckTriviallyCopyableMove)
  113. return;
  114. bool IsVariable = isa<DeclRefExpr>(Arg);
  115. // std::move shouldn't be removed when an lvalue wrapped by std::move is
  116. // passed to the function with an rvalue reference parameter.
  117. bool IsRVRefParam =
  118. IsRValueReferenceParam(ReceivingExpr, InvocationParmType, Arg);
  119. const auto *Var =
  120. IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr;
  121. {
  122. auto Diag = diag(FileMoveRange.getBegin(),
  123. "std::move of the %select{|const }0"
  124. "%select{expression|variable %5}1 "
  125. "%select{|of the trivially-copyable type %6 }2"
  126. "has no effect%select{; remove std::move()|}3"
  127. "%select{| or make the variable non-const}4")
  128. << IsConstArg << IsVariable << IsTriviallyCopyable
  129. << IsRVRefParam
  130. << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var
  131. << Arg->getType();
  132. if (!IsRVRefParam)
  133. replaceCallWithArg(CallMove, Diag, SM, getLangOpts());
  134. }
  135. if (IsRVRefParam) {
  136. // Generate notes for an invocation with an rvalue reference parameter.
  137. const auto *ReceivingCallExpr = dyn_cast<CallExpr>(ReceivingExpr);
  138. const auto *ReceivingConstructExpr =
  139. dyn_cast<CXXConstructExpr>(ReceivingExpr);
  140. // Skipping the invocation which is a template instantiation.
  141. if ((!ReceivingCallExpr || !ReceivingCallExpr->getDirectCallee() ||
  142. ReceivingCallExpr->getDirectCallee()->isTemplateInstantiation()) &&
  143. (!ReceivingConstructExpr ||
  144. !ReceivingConstructExpr->getConstructor() ||
  145. ReceivingConstructExpr->getConstructor()->isTemplateInstantiation()))
  146. return;
  147. const NamedDecl *FunctionName = nullptr;
  148. FunctionName =
  149. ReceivingCallExpr
  150. ? ReceivingCallExpr->getDirectCallee()->getUnderlyingDecl()
  151. : ReceivingConstructExpr->getConstructor()->getUnderlyingDecl();
  152. QualType NoRefType = (*InvocationParmType)->getPointeeType();
  153. PrintingPolicy PolicyWithSuppressedTag(getLangOpts());
  154. PolicyWithSuppressedTag.SuppressTagKeyword = true;
  155. PolicyWithSuppressedTag.SuppressUnwrittenScope = true;
  156. std::string ExpectParmTypeName =
  157. NoRefType.getAsString(PolicyWithSuppressedTag);
  158. if (!NoRefType->isPointerType()) {
  159. NoRefType.addConst();
  160. ExpectParmTypeName =
  161. NoRefType.getAsString(PolicyWithSuppressedTag) + " &";
  162. }
  163. diag(InvocationParm->getLocation(),
  164. "consider changing the %ordinal0 parameter of %1 from %2 to '%3'",
  165. DiagnosticIDs::Note)
  166. << (InvocationParm->getFunctionScopeIndex() + 1) << FunctionName
  167. << *InvocationParmType << ExpectParmTypeName;
  168. }
  169. } else if (ReceivingExpr && CheckMoveToConstRef) {
  170. if ((*InvocationParmType)->isRValueReferenceType())
  171. return;
  172. auto Diag = diag(FileMoveRange.getBegin(),
  173. "passing result of std::move() as a const reference "
  174. "argument; no move will actually happen");
  175. replaceCallWithArg(CallMove, Diag, SM, getLangOpts());
  176. }
  177. }
  178. } // namespace clang::tidy::performance