UnnecessaryValueParamCheck.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. //===--- UnnecessaryValueParamCheck.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 "UnnecessaryValueParamCheck.h"
  9. #include "../utils/DeclRefExprUtils.h"
  10. #include "../utils/FixItHintUtils.h"
  11. #include "../utils/Matchers.h"
  12. #include "../utils/OptionsUtils.h"
  13. #include "../utils/TypeTraits.h"
  14. #include "clang/Frontend/CompilerInstance.h"
  15. #include "clang/Lex/Lexer.h"
  16. #include "clang/Lex/Preprocessor.h"
  17. #include <optional>
  18. using namespace clang::ast_matchers;
  19. namespace clang::tidy::performance {
  20. namespace {
  21. std::string paramNameOrIndex(StringRef Name, size_t Index) {
  22. return (Name.empty() ? llvm::Twine('#') + llvm::Twine(Index + 1)
  23. : llvm::Twine('\'') + Name + llvm::Twine('\''))
  24. .str();
  25. }
  26. bool isReferencedOutsideOfCallExpr(const FunctionDecl &Function,
  27. ASTContext &Context) {
  28. auto Matches = match(declRefExpr(to(functionDecl(equalsNode(&Function))),
  29. unless(hasAncestor(callExpr()))),
  30. Context);
  31. return !Matches.empty();
  32. }
  33. bool hasLoopStmtAncestor(const DeclRefExpr &DeclRef, const Decl &Decl,
  34. ASTContext &Context) {
  35. auto Matches = match(
  36. traverse(TK_AsIs,
  37. decl(forEachDescendant(declRefExpr(
  38. equalsNode(&DeclRef),
  39. unless(hasAncestor(stmt(anyOf(forStmt(), cxxForRangeStmt(),
  40. whileStmt(), doStmt())))))))),
  41. Decl, Context);
  42. return Matches.empty();
  43. }
  44. } // namespace
  45. UnnecessaryValueParamCheck::UnnecessaryValueParamCheck(
  46. StringRef Name, ClangTidyContext *Context)
  47. : ClangTidyCheck(Name, Context),
  48. Inserter(Options.getLocalOrGlobal("IncludeStyle",
  49. utils::IncludeSorter::IS_LLVM),
  50. areDiagsSelfContained()),
  51. AllowedTypes(
  52. utils::options::parseStringList(Options.get("AllowedTypes", ""))) {}
  53. void UnnecessaryValueParamCheck::registerMatchers(MatchFinder *Finder) {
  54. const auto ExpensiveValueParamDecl = parmVarDecl(
  55. hasType(qualType(
  56. hasCanonicalType(matchers::isExpensiveToCopy()),
  57. unless(anyOf(hasCanonicalType(referenceType()),
  58. hasDeclaration(namedDecl(
  59. matchers::matchesAnyListedName(AllowedTypes))))))),
  60. decl().bind("param"));
  61. Finder->addMatcher(
  62. traverse(
  63. TK_AsIs,
  64. functionDecl(hasBody(stmt()), isDefinition(), unless(isImplicit()),
  65. unless(cxxMethodDecl(anyOf(isOverride(), isFinal()))),
  66. has(typeLoc(forEach(ExpensiveValueParamDecl))),
  67. unless(isInstantiated()), decl().bind("functionDecl"))),
  68. this);
  69. }
  70. void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {
  71. const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
  72. const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>("functionDecl");
  73. TraversalKindScope RAII(*Result.Context, TK_AsIs);
  74. FunctionParmMutationAnalyzer &Analyzer =
  75. MutationAnalyzers.try_emplace(Function, *Function, *Result.Context)
  76. .first->second;
  77. if (Analyzer.isMutated(Param))
  78. return;
  79. const bool IsConstQualified =
  80. Param->getType().getCanonicalType().isConstQualified();
  81. // If the parameter is non-const, check if it has a move constructor and is
  82. // only referenced once to copy-construct another object or whether it has a
  83. // move assignment operator and is only referenced once when copy-assigned.
  84. // In this case wrap DeclRefExpr with std::move() to avoid the unnecessary
  85. // copy.
  86. if (!IsConstQualified) {
  87. auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs(
  88. *Param, *Function, *Result.Context);
  89. if (AllDeclRefExprs.size() == 1) {
  90. auto CanonicalType = Param->getType().getCanonicalType();
  91. const auto &DeclRefExpr = **AllDeclRefExprs.begin();
  92. if (!hasLoopStmtAncestor(DeclRefExpr, *Function, *Result.Context) &&
  93. ((utils::type_traits::hasNonTrivialMoveConstructor(CanonicalType) &&
  94. utils::decl_ref_expr::isCopyConstructorArgument(
  95. DeclRefExpr, *Function, *Result.Context)) ||
  96. (utils::type_traits::hasNonTrivialMoveAssignment(CanonicalType) &&
  97. utils::decl_ref_expr::isCopyAssignmentArgument(
  98. DeclRefExpr, *Function, *Result.Context)))) {
  99. handleMoveFix(*Param, DeclRefExpr, *Result.Context);
  100. return;
  101. }
  102. }
  103. }
  104. const size_t Index = llvm::find(Function->parameters(), Param) -
  105. Function->parameters().begin();
  106. auto Diag =
  107. diag(Param->getLocation(),
  108. "the %select{|const qualified }0parameter %1 is copied for each "
  109. "invocation%select{ but only used as a const reference|}0; consider "
  110. "making it a %select{const |}0reference")
  111. << IsConstQualified << paramNameOrIndex(Param->getName(), Index);
  112. // Do not propose fixes when:
  113. // 1. the ParmVarDecl is in a macro, since we cannot place them correctly
  114. // 2. the function is virtual as it might break overrides
  115. // 3. the function is referenced outside of a call expression within the
  116. // compilation unit as the signature change could introduce build errors.
  117. // 4. the function is a primary template or an explicit template
  118. // specialization.
  119. const auto *Method = llvm::dyn_cast<CXXMethodDecl>(Function);
  120. if (Param->getBeginLoc().isMacroID() || (Method && Method->isVirtual()) ||
  121. isReferencedOutsideOfCallExpr(*Function, *Result.Context) ||
  122. (Function->getTemplatedKind() != FunctionDecl::TK_NonTemplate))
  123. return;
  124. for (const auto *FunctionDecl = Function; FunctionDecl != nullptr;
  125. FunctionDecl = FunctionDecl->getPreviousDecl()) {
  126. const auto &CurrentParam = *FunctionDecl->getParamDecl(Index);
  127. Diag << utils::fixit::changeVarDeclToReference(CurrentParam,
  128. *Result.Context);
  129. // The parameter of each declaration needs to be checked individually as to
  130. // whether it is const or not as constness can differ between definition and
  131. // declaration.
  132. if (!CurrentParam.getType().getCanonicalType().isConstQualified()) {
  133. if (std::optional<FixItHint> Fix = utils::fixit::addQualifierToVarDecl(
  134. CurrentParam, *Result.Context, DeclSpec::TQ::TQ_const))
  135. Diag << *Fix;
  136. }
  137. }
  138. }
  139. void UnnecessaryValueParamCheck::registerPPCallbacks(
  140. const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
  141. Inserter.registerPreprocessor(PP);
  142. }
  143. void UnnecessaryValueParamCheck::storeOptions(
  144. ClangTidyOptions::OptionMap &Opts) {
  145. Options.store(Opts, "IncludeStyle", Inserter.getStyle());
  146. Options.store(Opts, "AllowedTypes",
  147. utils::options::serializeStringList(AllowedTypes));
  148. }
  149. void UnnecessaryValueParamCheck::onEndOfTranslationUnit() {
  150. MutationAnalyzers.clear();
  151. }
  152. void UnnecessaryValueParamCheck::handleMoveFix(const ParmVarDecl &Var,
  153. const DeclRefExpr &CopyArgument,
  154. const ASTContext &Context) {
  155. auto Diag = diag(CopyArgument.getBeginLoc(),
  156. "parameter %0 is passed by value and only copied once; "
  157. "consider moving it to avoid unnecessary copies")
  158. << &Var;
  159. // Do not propose fixes in macros since we cannot place them correctly.
  160. if (CopyArgument.getBeginLoc().isMacroID())
  161. return;
  162. const auto &SM = Context.getSourceManager();
  163. auto EndLoc = Lexer::getLocForEndOfToken(CopyArgument.getLocation(), 0, SM,
  164. Context.getLangOpts());
  165. Diag << FixItHint::CreateInsertion(CopyArgument.getBeginLoc(), "std::move(")
  166. << FixItHint::CreateInsertion(EndLoc, ")")
  167. << Inserter.createIncludeInsertion(
  168. SM.getFileID(CopyArgument.getBeginLoc()), "<utility>");
  169. }
  170. } // namespace clang::tidy::performance