PassByValueCheck.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. //===--- PassByValueCheck.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 "PassByValueCheck.h"
  9. #include "clang/AST/ASTContext.h"
  10. #include "clang/AST/RecursiveASTVisitor.h"
  11. #include "clang/ASTMatchers/ASTMatchFinder.h"
  12. #include "clang/ASTMatchers/ASTMatchers.h"
  13. #include "clang/Frontend/CompilerInstance.h"
  14. #include "clang/Lex/Lexer.h"
  15. #include "clang/Lex/Preprocessor.h"
  16. using namespace clang::ast_matchers;
  17. using namespace llvm;
  18. namespace clang::tidy::modernize {
  19. namespace {
  20. /// Matches move-constructible classes.
  21. ///
  22. /// Given
  23. /// \code
  24. /// // POD types are trivially move constructible.
  25. /// struct Foo { int a; };
  26. ///
  27. /// struct Bar {
  28. /// Bar(Bar &&) = deleted;
  29. /// int a;
  30. /// };
  31. /// \endcode
  32. /// recordDecl(isMoveConstructible())
  33. /// matches "Foo".
  34. AST_MATCHER(CXXRecordDecl, isMoveConstructible) {
  35. for (const CXXConstructorDecl *Ctor : Node.ctors()) {
  36. if (Ctor->isMoveConstructor() && !Ctor->isDeleted())
  37. return true;
  38. }
  39. return false;
  40. }
  41. } // namespace
  42. static TypeMatcher notTemplateSpecConstRefType() {
  43. return lValueReferenceType(
  44. pointee(unless(elaboratedType(namesType(templateSpecializationType()))),
  45. isConstQualified()));
  46. }
  47. static TypeMatcher nonConstValueType() {
  48. return qualType(unless(anyOf(referenceType(), isConstQualified())));
  49. }
  50. /// Whether or not \p ParamDecl is used exactly one time in \p Ctor.
  51. ///
  52. /// Checks both in the init-list and the body of the constructor.
  53. static bool paramReferredExactlyOnce(const CXXConstructorDecl *Ctor,
  54. const ParmVarDecl *ParamDecl) {
  55. /// \c clang::RecursiveASTVisitor that checks that the given
  56. /// \c ParmVarDecl is used exactly one time.
  57. ///
  58. /// \see ExactlyOneUsageVisitor::hasExactlyOneUsageIn()
  59. class ExactlyOneUsageVisitor
  60. : public RecursiveASTVisitor<ExactlyOneUsageVisitor> {
  61. friend class RecursiveASTVisitor<ExactlyOneUsageVisitor>;
  62. public:
  63. ExactlyOneUsageVisitor(const ParmVarDecl *ParamDecl)
  64. : ParamDecl(ParamDecl) {}
  65. /// Whether or not the parameter variable is referred only once in
  66. /// the
  67. /// given constructor.
  68. bool hasExactlyOneUsageIn(const CXXConstructorDecl *Ctor) {
  69. Count = 0;
  70. TraverseDecl(const_cast<CXXConstructorDecl *>(Ctor));
  71. return Count == 1;
  72. }
  73. private:
  74. /// Counts the number of references to a variable.
  75. ///
  76. /// Stops the AST traversal if more than one usage is found.
  77. bool VisitDeclRefExpr(DeclRefExpr *D) {
  78. if (const ParmVarDecl *To = dyn_cast<ParmVarDecl>(D->getDecl())) {
  79. if (To == ParamDecl) {
  80. ++Count;
  81. if (Count > 1) {
  82. // No need to look further, used more than once.
  83. return false;
  84. }
  85. }
  86. }
  87. return true;
  88. }
  89. const ParmVarDecl *ParamDecl;
  90. unsigned Count;
  91. };
  92. return ExactlyOneUsageVisitor(ParamDecl).hasExactlyOneUsageIn(Ctor);
  93. }
  94. /// Returns true if the given constructor is part of a lvalue/rvalue reference
  95. /// pair, i.e. `Param` is of lvalue reference type, and there exists another
  96. /// constructor such that:
  97. /// - it has the same number of parameters as `Ctor`.
  98. /// - the parameter at the same index as `Param` is an rvalue reference
  99. /// of the same pointee type
  100. /// - all other parameters have the same type as the corresponding parameter in
  101. /// `Ctor` or are rvalue references with the same pointee type.
  102. /// Examples:
  103. /// A::A(const B& Param)
  104. /// A::A(B&&)
  105. ///
  106. /// A::A(const B& Param, const C&)
  107. /// A::A(B&& Param, C&&)
  108. ///
  109. /// A::A(const B&, const C& Param)
  110. /// A::A(B&&, C&& Param)
  111. ///
  112. /// A::A(const B&, const C& Param)
  113. /// A::A(const B&, C&& Param)
  114. ///
  115. /// A::A(const B& Param, int)
  116. /// A::A(B&& Param, int)
  117. static bool hasRValueOverload(const CXXConstructorDecl *Ctor,
  118. const ParmVarDecl *Param) {
  119. if (!Param->getType().getCanonicalType()->isLValueReferenceType()) {
  120. // The parameter is passed by value.
  121. return false;
  122. }
  123. const int ParamIdx = Param->getFunctionScopeIndex();
  124. const CXXRecordDecl *Record = Ctor->getParent();
  125. // Check whether a ctor `C` forms a pair with `Ctor` under the aforementioned
  126. // rules.
  127. const auto IsRValueOverload = [&Ctor, ParamIdx](const CXXConstructorDecl *C) {
  128. if (C == Ctor || C->isDeleted() ||
  129. C->getNumParams() != Ctor->getNumParams())
  130. return false;
  131. for (int I = 0, E = C->getNumParams(); I < E; ++I) {
  132. const clang::QualType CandidateParamType =
  133. C->parameters()[I]->getType().getCanonicalType();
  134. const clang::QualType CtorParamType =
  135. Ctor->parameters()[I]->getType().getCanonicalType();
  136. const bool IsLValueRValuePair =
  137. CtorParamType->isLValueReferenceType() &&
  138. CandidateParamType->isRValueReferenceType() &&
  139. CandidateParamType->getPointeeType()->getUnqualifiedDesugaredType() ==
  140. CtorParamType->getPointeeType()->getUnqualifiedDesugaredType();
  141. if (I == ParamIdx) {
  142. // The parameter of interest must be paired.
  143. if (!IsLValueRValuePair)
  144. return false;
  145. } else {
  146. // All other parameters can be similar or paired.
  147. if (!(CandidateParamType == CtorParamType || IsLValueRValuePair))
  148. return false;
  149. }
  150. }
  151. return true;
  152. };
  153. for (const auto *Candidate : Record->ctors()) {
  154. if (IsRValueOverload(Candidate)) {
  155. return true;
  156. }
  157. }
  158. return false;
  159. }
  160. /// Find all references to \p ParamDecl across all of the
  161. /// redeclarations of \p Ctor.
  162. static SmallVector<const ParmVarDecl *, 2>
  163. collectParamDecls(const CXXConstructorDecl *Ctor,
  164. const ParmVarDecl *ParamDecl) {
  165. SmallVector<const ParmVarDecl *, 2> Results;
  166. unsigned ParamIdx = ParamDecl->getFunctionScopeIndex();
  167. for (const FunctionDecl *Redecl : Ctor->redecls())
  168. Results.push_back(Redecl->getParamDecl(ParamIdx));
  169. return Results;
  170. }
  171. PassByValueCheck::PassByValueCheck(StringRef Name, ClangTidyContext *Context)
  172. : ClangTidyCheck(Name, Context),
  173. Inserter(Options.getLocalOrGlobal("IncludeStyle",
  174. utils::IncludeSorter::IS_LLVM),
  175. areDiagsSelfContained()),
  176. ValuesOnly(Options.get("ValuesOnly", false)) {}
  177. void PassByValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
  178. Options.store(Opts, "IncludeStyle", Inserter.getStyle());
  179. Options.store(Opts, "ValuesOnly", ValuesOnly);
  180. }
  181. void PassByValueCheck::registerMatchers(MatchFinder *Finder) {
  182. Finder->addMatcher(
  183. traverse(
  184. TK_AsIs,
  185. cxxConstructorDecl(
  186. forEachConstructorInitializer(
  187. cxxCtorInitializer(
  188. unless(isBaseInitializer()),
  189. // Clang builds a CXXConstructExpr only when it knows
  190. // which constructor will be called. In dependent contexts
  191. // a ParenListExpr is generated instead of a
  192. // CXXConstructExpr, filtering out templates automatically
  193. // for us.
  194. withInitializer(cxxConstructExpr(
  195. has(ignoringParenImpCasts(declRefExpr(to(
  196. parmVarDecl(
  197. hasType(qualType(
  198. // Match only const-ref or a non-const
  199. // value parameters. Rvalues,
  200. // TemplateSpecializationValues and
  201. // const-values shouldn't be modified.
  202. ValuesOnly
  203. ? nonConstValueType()
  204. : anyOf(notTemplateSpecConstRefType(),
  205. nonConstValueType()))))
  206. .bind("Param"))))),
  207. hasDeclaration(cxxConstructorDecl(
  208. isCopyConstructor(), unless(isDeleted()),
  209. hasDeclContext(
  210. cxxRecordDecl(isMoveConstructible())))))))
  211. .bind("Initializer")))
  212. .bind("Ctor")),
  213. this);
  214. }
  215. void PassByValueCheck::registerPPCallbacks(const SourceManager &SM,
  216. Preprocessor *PP,
  217. Preprocessor *ModuleExpanderPP) {
  218. Inserter.registerPreprocessor(PP);
  219. }
  220. void PassByValueCheck::check(const MatchFinder::MatchResult &Result) {
  221. const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("Ctor");
  222. const auto *ParamDecl = Result.Nodes.getNodeAs<ParmVarDecl>("Param");
  223. const auto *Initializer =
  224. Result.Nodes.getNodeAs<CXXCtorInitializer>("Initializer");
  225. SourceManager &SM = *Result.SourceManager;
  226. // If the parameter is used or anything other than the copy, do not apply
  227. // the changes.
  228. if (!paramReferredExactlyOnce(Ctor, ParamDecl))
  229. return;
  230. // If the parameter is trivial to copy, don't move it. Moving a trivially
  231. // copyable type will cause a problem with performance-move-const-arg
  232. if (ParamDecl->getType().getNonReferenceType().isTriviallyCopyableType(
  233. *Result.Context))
  234. return;
  235. // Do not trigger if we find a paired constructor with an rvalue.
  236. if (hasRValueOverload(Ctor, ParamDecl))
  237. return;
  238. auto Diag = diag(ParamDecl->getBeginLoc(), "pass by value and use std::move");
  239. // If we received a `const&` type, we need to rewrite the function
  240. // declarations.
  241. if (ParamDecl->getType()->isLValueReferenceType()) {
  242. // Check if we can succesfully rewrite all declarations of the constructor.
  243. for (const ParmVarDecl *ParmDecl : collectParamDecls(Ctor, ParamDecl)) {
  244. TypeLoc ParamTL = ParmDecl->getTypeSourceInfo()->getTypeLoc();
  245. ReferenceTypeLoc RefTL = ParamTL.getAs<ReferenceTypeLoc>();
  246. if (RefTL.isNull()) {
  247. // We cannot rewrite this instance. The type is probably hidden behind
  248. // some `typedef`. Do not offer a fix-it in this case.
  249. return;
  250. }
  251. }
  252. // Rewrite all declarations.
  253. for (const ParmVarDecl *ParmDecl : collectParamDecls(Ctor, ParamDecl)) {
  254. TypeLoc ParamTL = ParmDecl->getTypeSourceInfo()->getTypeLoc();
  255. ReferenceTypeLoc RefTL = ParamTL.getAs<ReferenceTypeLoc>();
  256. TypeLoc ValueTL = RefTL.getPointeeLoc();
  257. CharSourceRange TypeRange = CharSourceRange::getTokenRange(
  258. ParmDecl->getBeginLoc(), ParamTL.getEndLoc());
  259. std::string ValueStr =
  260. Lexer::getSourceText(
  261. CharSourceRange::getTokenRange(ValueTL.getSourceRange()), SM,
  262. getLangOpts())
  263. .str();
  264. ValueStr += ' ';
  265. Diag << FixItHint::CreateReplacement(TypeRange, ValueStr);
  266. }
  267. }
  268. // Use std::move in the initialization list.
  269. Diag << FixItHint::CreateInsertion(Initializer->getRParenLoc(), ")")
  270. << FixItHint::CreateInsertion(
  271. Initializer->getLParenLoc().getLocWithOffset(1), "std::move(")
  272. << Inserter.createIncludeInsertion(
  273. Result.SourceManager->getFileID(Initializer->getSourceLocation()),
  274. "<utility>");
  275. }
  276. } // namespace clang::tidy::modernize