DeclRefExprUtils.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. //===--- DeclRefExprUtils.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 "DeclRefExprUtils.h"
  9. #include "Matchers.h"
  10. #include "clang/AST/ASTContext.h"
  11. #include "clang/AST/DeclCXX.h"
  12. #include "clang/ASTMatchers/ASTMatchFinder.h"
  13. namespace clang::tidy::utils::decl_ref_expr {
  14. using namespace ::clang::ast_matchers;
  15. using llvm::SmallPtrSet;
  16. namespace {
  17. template <typename S> bool isSetDifferenceEmpty(const S &S1, const S &S2) {
  18. for (auto E : S1)
  19. if (S2.count(E) == 0)
  20. return false;
  21. return true;
  22. }
  23. // Extracts all Nodes keyed by ID from Matches and inserts them into Nodes.
  24. template <typename Node>
  25. void extractNodesByIdTo(ArrayRef<BoundNodes> Matches, StringRef ID,
  26. SmallPtrSet<const Node *, 16> &Nodes) {
  27. for (const auto &Match : Matches)
  28. Nodes.insert(Match.getNodeAs<Node>(ID));
  29. }
  30. } // namespace
  31. // Finds all DeclRefExprs where a const method is called on VarDecl or VarDecl
  32. // is the a const reference or value argument to a CallExpr or CXXConstructExpr.
  33. SmallPtrSet<const DeclRefExpr *, 16>
  34. constReferenceDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt,
  35. ASTContext &Context) {
  36. auto DeclRefToVar =
  37. declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef");
  38. auto ConstMethodCallee = callee(cxxMethodDecl(isConst()));
  39. // Match method call expressions where the variable is referenced as the this
  40. // implicit object argument and operator call expression for member operators
  41. // where the variable is the 0-th argument.
  42. auto Matches = match(
  43. findAll(expr(anyOf(cxxMemberCallExpr(ConstMethodCallee, on(DeclRefToVar)),
  44. cxxOperatorCallExpr(ConstMethodCallee,
  45. hasArgument(0, DeclRefToVar))))),
  46. Stmt, Context);
  47. SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
  48. extractNodesByIdTo(Matches, "declRef", DeclRefs);
  49. auto ConstReferenceOrValue =
  50. qualType(anyOf(matchers::isReferenceToConst(),
  51. unless(anyOf(referenceType(), pointerType(),
  52. substTemplateTypeParmType()))));
  53. auto ConstReferenceOrValueOrReplaced = qualType(anyOf(
  54. ConstReferenceOrValue,
  55. substTemplateTypeParmType(hasReplacementType(ConstReferenceOrValue))));
  56. auto UsedAsConstRefOrValueArg = forEachArgumentWithParam(
  57. DeclRefToVar, parmVarDecl(hasType(ConstReferenceOrValueOrReplaced)));
  58. Matches = match(findAll(invocation(UsedAsConstRefOrValueArg)), Stmt, Context);
  59. extractNodesByIdTo(Matches, "declRef", DeclRefs);
  60. // References and pointers to const assignments.
  61. Matches =
  62. match(findAll(declStmt(
  63. has(varDecl(hasType(qualType(matchers::isReferenceToConst())),
  64. hasInitializer(ignoringImpCasts(DeclRefToVar)))))),
  65. Stmt, Context);
  66. extractNodesByIdTo(Matches, "declRef", DeclRefs);
  67. Matches =
  68. match(findAll(declStmt(has(varDecl(
  69. hasType(qualType(matchers::isPointerToConst())),
  70. hasInitializer(ignoringImpCasts(unaryOperator(
  71. hasOperatorName("&"), hasUnaryOperand(DeclRefToVar)))))))),
  72. Stmt, Context);
  73. extractNodesByIdTo(Matches, "declRef", DeclRefs);
  74. return DeclRefs;
  75. }
  76. bool isOnlyUsedAsConst(const VarDecl &Var, const Stmt &Stmt,
  77. ASTContext &Context) {
  78. // Collect all DeclRefExprs to the loop variable and all CallExprs and
  79. // CXXConstructExprs where the loop variable is used as argument to a const
  80. // reference parameter.
  81. // If the difference is empty it is safe for the loop variable to be a const
  82. // reference.
  83. auto AllDeclRefs = allDeclRefExprs(Var, Stmt, Context);
  84. auto ConstReferenceDeclRefs = constReferenceDeclRefExprs(Var, Stmt, Context);
  85. return isSetDifferenceEmpty(AllDeclRefs, ConstReferenceDeclRefs);
  86. }
  87. SmallPtrSet<const DeclRefExpr *, 16>
  88. allDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context) {
  89. auto Matches = match(
  90. findAll(declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef")),
  91. Stmt, Context);
  92. SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
  93. extractNodesByIdTo(Matches, "declRef", DeclRefs);
  94. return DeclRefs;
  95. }
  96. SmallPtrSet<const DeclRefExpr *, 16>
  97. allDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl, ASTContext &Context) {
  98. auto Matches = match(
  99. decl(forEachDescendant(
  100. declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef"))),
  101. Decl, Context);
  102. SmallPtrSet<const DeclRefExpr *, 16> DeclRefs;
  103. extractNodesByIdTo(Matches, "declRef", DeclRefs);
  104. return DeclRefs;
  105. }
  106. bool isCopyConstructorArgument(const DeclRefExpr &DeclRef, const Decl &Decl,
  107. ASTContext &Context) {
  108. auto UsedAsConstRefArg = forEachArgumentWithParam(
  109. declRefExpr(equalsNode(&DeclRef)),
  110. parmVarDecl(hasType(matchers::isReferenceToConst())));
  111. auto Matches = match(
  112. decl(hasDescendant(
  113. cxxConstructExpr(UsedAsConstRefArg, hasDeclaration(cxxConstructorDecl(
  114. isCopyConstructor())))
  115. .bind("constructExpr"))),
  116. Decl, Context);
  117. return !Matches.empty();
  118. }
  119. bool isCopyAssignmentArgument(const DeclRefExpr &DeclRef, const Decl &Decl,
  120. ASTContext &Context) {
  121. auto UsedAsConstRefArg = forEachArgumentWithParam(
  122. declRefExpr(equalsNode(&DeclRef)),
  123. parmVarDecl(hasType(matchers::isReferenceToConst())));
  124. auto Matches = match(
  125. decl(hasDescendant(
  126. cxxOperatorCallExpr(UsedAsConstRefArg, hasOverloadedOperatorName("="),
  127. callee(cxxMethodDecl(isCopyAssignmentOperator())))
  128. .bind("operatorCallExpr"))),
  129. Decl, Context);
  130. return !Matches.empty();
  131. }
  132. } // namespace clang::tidy::utils::decl_ref_expr