steveire created this revision.
steveire added a reviewer: aaron.ballman.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
steveire requested review of this revision.

Update the ASTNodeTraverser to dump only nodes spelled in source.  There
are only a few which need to be handled, but Decl nodes for which
isImplicit() is true are handled together.

Update the RAV instances used in ASTMatchFinder to ignore the nodes too.
As with handling of template instantiations, it is necessary to allow
the RAV to process the implicit nodes because they need to be visitable
before the first traverse() matcher is encountered.  An exception to
this is in the MatchChildASTVisitor, because we sometimes wish to make a
node matchable but make its children not-matchable.  This is the case
for defaulted CXXMethodDecls for example.

This change accounts for handling nodes not spelled in source when using
direct matching of nodes, and when using the has() and hasDescendant()
matchers.  Other matchers such as
cxxRecordDecl(hasMethod(cxxMethodDecl())) still succeed for
compiler-generated methods for example after this change.  Updating the
implementations of hasMethod() and other matchers is for a follow-up
patch.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D90982

Files:
  clang/include/clang/AST/ASTNodeTraverser.h
  clang/include/clang/ASTMatchers/ASTMatchersInternal.h
  clang/lib/ASTMatchers/ASTMatchFinder.cpp
  clang/lib/ASTMatchers/ASTMatchersInternal.cpp
  clang/unittests/AST/ASTTraverserTest.cpp
  clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp

Index: clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===================================================================
--- clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -2331,6 +2331,250 @@
   }
 }
 
+TEST(Traversal, traverseNoImplicit) {
+  StringRef Code = R"cpp(
+struct NonTrivial {
+    NonTrivial() {}
+    NonTrivial(NonTrivial&) {}
+    NonTrivial& operator=(NonTrivial&) { return *this; }
+
+    ~NonTrivial() {}
+};
+
+struct NoSpecialMethods {
+    NonTrivial nt;
+};
+
+struct ContainsArray {
+    NonTrivial arr[2];
+    ContainsArray& operator=(ContainsArray &other) = default;
+};
+
+void copyIt()
+{
+    NoSpecialMethods nc1;
+    NoSpecialMethods nc2(nc1);
+    nc2 = nc1;
+
+    ContainsArray ca;
+    ContainsArray ca2;
+    ca2 = ca;
+}
+
+struct HasCtorInits : NoSpecialMethods, NonTrivial
+{
+  int m_i;
+  NonTrivial m_nt;
+  HasCtorInits() : NoSpecialMethods(), m_i(42) {}
+};
+
+)cpp";
+  {
+    auto M = cxxRecordDecl(hasName("NoSpecialMethods"),
+                           has(cxxRecordDecl(hasName("NoSpecialMethods"))));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+
+    M = cxxRecordDecl(hasName("NoSpecialMethods"),
+                      has(cxxConstructorDecl(isCopyConstructor())));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+
+    M = cxxRecordDecl(hasName("NoSpecialMethods"),
+                      has(cxxMethodDecl(isCopyAssignmentOperator())));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+
+    M = cxxRecordDecl(hasName("NoSpecialMethods"),
+                      has(cxxConstructorDecl(isDefaultConstructor())));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+
+    M = cxxRecordDecl(hasName("NoSpecialMethods"), has(cxxDestructorDecl()));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+  }
+  {
+    // Compiler generates a forStmt to copy the array
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, forStmt())));
+    EXPECT_FALSE(
+        matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, forStmt())));
+  }
+  {
+    // The defaulted method declaration can be matched, but not its
+    // definition, in IgnoreUnlessSpelledInSource mode
+    auto MDecl = cxxMethodDecl(ofClass(cxxRecordDecl(hasName("ContainsArray"))),
+                               isCopyAssignmentOperator(), isDefaulted());
+
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, MDecl)));
+    EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, MDecl)));
+
+    auto MDef = cxxMethodDecl(MDecl, has(compoundStmt()));
+
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, MDef)));
+    EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, MDef)));
+
+    // The parameter of the defaulted method can still be matched.
+    auto MParm =
+        cxxMethodDecl(MDecl, hasParameter(0, parmVarDecl(hasName("other"))));
+
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, MParm)));
+    EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, MParm)));
+  }
+  {
+    auto M =
+        cxxConstructorDecl(hasName("HasCtorInits"),
+                           has(cxxCtorInitializer(forField(hasName("m_i")))));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+  }
+  {
+    auto M =
+        cxxConstructorDecl(hasName("HasCtorInits"),
+                           has(cxxCtorInitializer(forField(hasName("m_nt")))));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+  }
+  {
+    auto M = cxxConstructorDecl(
+        hasName("HasCtorInits"),
+        has(cxxCtorInitializer(withInitializer(cxxConstructExpr(hasDeclaration(
+            cxxConstructorDecl(hasName("NoSpecialMethods"))))))));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+  }
+  {
+    auto M = cxxConstructorDecl(
+        hasName("HasCtorInits"),
+        has(cxxCtorInitializer(withInitializer(cxxConstructExpr(
+            hasDeclaration(cxxConstructorDecl(hasName("NonTrivial"))))))));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+  }
+
+  Code = R"cpp(
+  void rangeFor()
+  {
+    int arr[2];
+    for (auto i : arr)
+    {
+
+    }
+  }
+  )cpp";
+  {
+    auto M = cxxForRangeStmt(has(binaryOperator(hasOperatorName("!="))));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+  }
+  {
+    auto M =
+        cxxForRangeStmt(hasDescendant(binaryOperator(hasOperatorName("+"))));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+  }
+  {
+    auto M =
+        cxxForRangeStmt(hasDescendant(unaryOperator(hasOperatorName("++"))));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+  }
+  {
+    auto M = cxxForRangeStmt(has(declStmt()));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+  }
+  {
+    auto M =
+        cxxForRangeStmt(hasLoopVariable(varDecl(hasName("i"))),
+                        hasRangeInit(declRefExpr(to(varDecl(hasName("arr"))))));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+  }
+  {
+    auto M = cxxForRangeStmt(unless(hasInitStatement(stmt())));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+  }
+
+  Code = R"cpp(
+  void rangeFor()
+  {
+    int arr[2];
+    for (auto& a = arr; auto i : a)
+    {
+
+    }
+  }
+  )cpp";
+  {
+    auto M = cxxForRangeStmt(has(binaryOperator(hasOperatorName("!="))));
+    EXPECT_TRUE(
+        matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+    EXPECT_FALSE(
+        matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+                             true, {"-std=c++20"}));
+  }
+  {
+    auto M =
+        cxxForRangeStmt(hasDescendant(binaryOperator(hasOperatorName("+"))));
+    EXPECT_TRUE(
+        matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+    EXPECT_FALSE(
+        matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+                             true, {"-std=c++20"}));
+  }
+  {
+    auto M =
+        cxxForRangeStmt(hasDescendant(unaryOperator(hasOperatorName("++"))));
+    EXPECT_TRUE(
+        matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+    EXPECT_FALSE(
+        matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+                             true, {"-std=c++20"}));
+  }
+  {
+    auto M = cxxForRangeStmt(has(declStmt()));
+    EXPECT_TRUE(
+        matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+    EXPECT_FALSE(
+        matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+                             true, {"-std=c++20"}));
+  }
+  {
+    auto M = cxxForRangeStmt(
+        hasInitStatement(declStmt(hasSingleDecl(varDecl(
+            hasName("a"),
+            hasInitializer(declRefExpr(to(varDecl(hasName("arr"))))))))),
+        hasLoopVariable(varDecl(hasName("i"))),
+        hasRangeInit(declRefExpr(to(varDecl(hasName("a"))))));
+    EXPECT_TRUE(
+        matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++20"}));
+    EXPECT_TRUE(
+        matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+                             true, {"-std=c++20"}));
+  }
+  Code = R"cpp(
+void hasDefaultArg(int i, int j = 0)
+{
+}
+void callDefaultArg()
+{
+  hasDefaultArg(42);
+}
+)cpp";
+  {
+    auto M = callExpr(has(integerLiteral(equals(42))));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+  }
+  {
+    auto M = callExpr(has(cxxDefaultArgExpr()));
+    EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
+    EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
+  }
+}
+
 template <typename MatcherT>
 bool matcherTemplateWithBinding(StringRef Code, const MatcherT &M) {
   return matchAndVerifyResultTrue(
Index: clang/unittests/AST/ASTTraverserTest.cpp
===================================================================
--- clang/unittests/AST/ASTTraverserTest.cpp
+++ clang/unittests/AST/ASTTraverserTest.cpp
@@ -35,6 +35,10 @@
   }
 
   void Visit(const Stmt *S) {
+    if (!S) {
+      OS << "<<<NULL>>>";
+      return;
+    }
     OS << S->getStmtClassName();
     if (auto *E = dyn_cast<DeclRefExpr>(S)) {
       OS << " '" << E->getDecl()->getDeclName() << "'";
@@ -51,7 +55,14 @@
     OS << C->getCommentKindName();
   }
 
-  void Visit(const CXXCtorInitializer *Init) { OS << "CXXCtorInitializer"; }
+  void Visit(const CXXCtorInitializer *Init) {
+    OS << "CXXCtorInitializer";
+    if (const auto *F = Init->getAnyMember()) {
+      OS << " '" << F->getNameAsString() << "'";
+    } else if (auto const *TSI = Init->getTypeSourceInfo()) {
+      OS << " '" << TSI->getType().getAsString() << "'";
+    }
+  }
 
   void Visit(const Attr *A) {
     switch (A->getKind()) {
@@ -226,7 +237,7 @@
 
   verifyWithDynNode(Init,
                     R"cpp(
-CXXCtorInitializer
+CXXCtorInitializer 'm_number'
 `-IntegerLiteral
 )cpp");
 
@@ -1050,6 +1061,411 @@
   }
 }
 
+TEST(Traverse, IgnoreUnlessSpelledInSourceImplicit) {
+  {
+    auto AST = buildASTFromCode(R"cpp(
+int i = 0;
+)cpp");
+    const auto *TUDecl = AST->getASTContext().getTranslationUnitDecl();
+
+    EXPECT_EQ(dumpASTString(TK_AsIs, TUDecl),
+              R"cpp(
+TranslationUnitDecl
+|-TypedefDecl '__int128_t'
+| `-BuiltinType
+|-TypedefDecl '__uint128_t'
+| `-BuiltinType
+|-TypedefDecl '__NSConstantString'
+| `-RecordType
+|-TypedefDecl '__builtin_ms_va_list'
+| `-PointerType
+|   `-BuiltinType
+|-TypedefDecl '__builtin_va_list'
+| `-ConstantArrayType
+|   `-RecordType
+`-VarDecl 'i'
+  `-IntegerLiteral
+)cpp");
+
+    EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource, TUDecl),
+              R"cpp(
+TranslationUnitDecl
+`-VarDecl 'i'
+  `-IntegerLiteral
+)cpp");
+  }
+
+  auto AST2 = buildASTFromCodeWithArgs(R"cpp(
+struct Simple {
+};
+struct Other {
+};
+
+struct Record : Simple, Other {
+  Record() : Simple(), m_i(42) {}
+private:
+  int m_i;
+  int m_i2 = 42;
+  Simple m_s;
+};
+
+struct NonTrivial {
+    NonTrivial() {}
+    NonTrivial(NonTrivial&) {}
+    NonTrivial& operator=(NonTrivial&) { return *this; }
+
+    ~NonTrivial() {}
+};
+
+struct ContainsArray {
+    NonTrivial arr[2];
+    int irr[2];
+    ContainsArray& operator=(ContainsArray &) = default;
+};
+
+void copyIt()
+{
+    ContainsArray ca;
+    ContainsArray ca2;
+    ca2 = ca;
+}
+
+void forLoop()
+{
+    int arr[2];
+    for (auto i : arr)
+    {
+
+    }
+    for (auto& a = arr; auto i : a)
+    {
+
+    }
+}
+
+struct DefaultedAndDeleted {
+  NonTrivial nt;
+  DefaultedAndDeleted() = default;
+  ~DefaultedAndDeleted() = default;
+  DefaultedAndDeleted(DefaultedAndDeleted &) = default;
+  DefaultedAndDeleted& operator=(DefaultedAndDeleted &) = default;
+  DefaultedAndDeleted(DefaultedAndDeleted &&) = delete;
+  DefaultedAndDeleted& operator=(DefaultedAndDeleted &&) = delete;
+};
+
+void copyIt2()
+{
+    DefaultedAndDeleted ca;
+    DefaultedAndDeleted ca2;
+    ca2 = ca;
+}
+
+void hasDefaultArg(int i, int j = 0)
+{
+}
+void callDefaultArg()
+{
+  hasDefaultArg(42);
+}
+)cpp",
+                                       {"-std=c++20"});
+
+  {
+    auto BN = ast_matchers::match(
+        cxxRecordDecl(hasName("Record"), unless(isImplicit())).bind("rec"),
+        AST2->getASTContext());
+    EXPECT_EQ(BN.size(), 1u);
+
+    EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")),
+              R"cpp(
+CXXRecordDecl 'Record'
+|-CXXRecordDecl 'Record'
+|-CXXConstructorDecl 'Record'
+| |-CXXCtorInitializer 'struct Simple'
+| | `-CXXConstructExpr
+| |-CXXCtorInitializer 'struct Other'
+| | `-CXXConstructExpr
+| |-CXXCtorInitializer 'm_i'
+| | `-IntegerLiteral
+| |-CXXCtorInitializer 'm_i2'
+| | `-CXXDefaultInitExpr
+| |-CXXCtorInitializer 'm_s'
+| | `-CXXConstructExpr
+| `-CompoundStmt
+|-AccessSpecDecl
+|-FieldDecl 'm_i'
+|-FieldDecl 'm_i2'
+| `-IntegerLiteral
+`-FieldDecl 'm_s'
+)cpp");
+
+    EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
+                            BN[0].getNodeAs<Decl>("rec")),
+              R"cpp(
+CXXRecordDecl 'Record'
+|-CXXConstructorDecl 'Record'
+| |-CXXCtorInitializer 'struct Simple'
+| | `-CXXConstructExpr
+| |-CXXCtorInitializer 'm_i'
+| | `-IntegerLiteral
+| `-CompoundStmt
+|-AccessSpecDecl
+|-FieldDecl 'm_i'
+|-FieldDecl 'm_i2'
+| `-IntegerLiteral
+`-FieldDecl 'm_s'
+)cpp");
+  }
+  {
+    auto BN = ast_matchers::match(
+        cxxRecordDecl(hasName("ContainsArray"), unless(isImplicit()))
+            .bind("rec"),
+        AST2->getASTContext());
+    EXPECT_EQ(BN.size(), 1u);
+
+    EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")),
+              R"cpp(
+CXXRecordDecl 'ContainsArray'
+|-CXXRecordDecl 'ContainsArray'
+|-FieldDecl 'arr'
+|-FieldDecl 'irr'
+|-CXXMethodDecl 'operator='
+| |-ParmVarDecl ''
+| `-CompoundStmt
+|   |-ForStmt
+|   | |-DeclStmt
+|   | | `-VarDecl '__i0'
+|   | |   `-IntegerLiteral
+|   | |-<<<NULL>>>
+|   | |-BinaryOperator
+|   | | |-ImplicitCastExpr
+|   | | | `-DeclRefExpr '__i0'
+|   | | `-IntegerLiteral
+|   | |-UnaryOperator
+|   | | `-DeclRefExpr '__i0'
+|   | `-CXXMemberCallExpr
+|   |   |-MemberExpr
+|   |   | `-ArraySubscriptExpr
+|   |   |   |-ImplicitCastExpr
+|   |   |   | `-MemberExpr
+|   |   |   |   `-CXXThisExpr
+|   |   |   `-ImplicitCastExpr
+|   |   |     `-DeclRefExpr '__i0'
+|   |   `-ArraySubscriptExpr
+|   |     |-ImplicitCastExpr
+|   |     | `-MemberExpr
+|   |     |   `-DeclRefExpr ''
+|   |     `-ImplicitCastExpr
+|   |       `-DeclRefExpr '__i0'
+|   |-CallExpr
+|   | |-ImplicitCastExpr
+|   | | `-DeclRefExpr '__builtin_memcpy'
+|   | |-ImplicitCastExpr
+|   | | `-UnaryOperator
+|   | |   `-MemberExpr
+|   | |     `-CXXThisExpr
+|   | |-ImplicitCastExpr
+|   | | `-UnaryOperator
+|   | |   `-MemberExpr
+|   | |     `-DeclRefExpr ''
+|   | `-IntegerLiteral
+|   `-ReturnStmt
+|     `-UnaryOperator
+|       `-CXXThisExpr
+|-CXXConstructorDecl 'ContainsArray'
+| `-ParmVarDecl ''
+|-CXXDestructorDecl '~ContainsArray'
+| `-CompoundStmt
+`-CXXConstructorDecl 'ContainsArray'
+  |-CXXCtorInitializer 'arr'
+  | `-CXXConstructExpr
+  `-CompoundStmt
+)cpp");
+
+    EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
+                            BN[0].getNodeAs<Decl>("rec")),
+              R"cpp(
+CXXRecordDecl 'ContainsArray'
+|-FieldDecl 'arr'
+|-FieldDecl 'irr'
+`-CXXMethodDecl 'operator='
+  `-ParmVarDecl ''
+)cpp");
+  }
+  {
+    auto BN = ast_matchers::match(functionDecl(hasName("forLoop")).bind("func"),
+                                  AST2->getASTContext());
+    EXPECT_EQ(BN.size(), 1u);
+
+    EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("func")),
+              R"cpp(
+FunctionDecl 'forLoop'
+`-CompoundStmt
+  |-DeclStmt
+  | `-VarDecl 'arr'
+  |-CXXForRangeStmt
+  | |-<<<NULL>>>
+  | |-DeclStmt
+  | | `-VarDecl '__range1'
+  | |   `-DeclRefExpr 'arr'
+  | |-DeclStmt
+  | | `-VarDecl '__begin1'
+  | |   `-ImplicitCastExpr
+  | |     `-DeclRefExpr '__range1'
+  | |-DeclStmt
+  | | `-VarDecl '__end1'
+  | |   `-BinaryOperator
+  | |     |-ImplicitCastExpr
+  | |     | `-DeclRefExpr '__range1'
+  | |     `-IntegerLiteral
+  | |-BinaryOperator
+  | | |-ImplicitCastExpr
+  | | | `-DeclRefExpr '__begin1'
+  | | `-ImplicitCastExpr
+  | |   `-DeclRefExpr '__end1'
+  | |-UnaryOperator
+  | | `-DeclRefExpr '__begin1'
+  | |-DeclStmt
+  | | `-VarDecl 'i'
+  | |   `-ImplicitCastExpr
+  | |     `-UnaryOperator
+  | |       `-ImplicitCastExpr
+  | |         `-DeclRefExpr '__begin1'
+  | `-CompoundStmt
+  `-CXXForRangeStmt
+    |-DeclStmt
+    | `-VarDecl 'a'
+    |   `-DeclRefExpr 'arr'
+    |-DeclStmt
+    | `-VarDecl '__range1'
+    |   `-DeclRefExpr 'a'
+    |-DeclStmt
+    | `-VarDecl '__begin1'
+    |   `-ImplicitCastExpr
+    |     `-DeclRefExpr '__range1'
+    |-DeclStmt
+    | `-VarDecl '__end1'
+    |   `-BinaryOperator
+    |     |-ImplicitCastExpr
+    |     | `-DeclRefExpr '__range1'
+    |     `-IntegerLiteral
+    |-BinaryOperator
+    | |-ImplicitCastExpr
+    | | `-DeclRefExpr '__begin1'
+    | `-ImplicitCastExpr
+    |   `-DeclRefExpr '__end1'
+    |-UnaryOperator
+    | `-DeclRefExpr '__begin1'
+    |-DeclStmt
+    | `-VarDecl 'i'
+    |   `-ImplicitCastExpr
+    |     `-UnaryOperator
+    |       `-ImplicitCastExpr
+    |         `-DeclRefExpr '__begin1'
+    `-CompoundStmt
+)cpp");
+
+    EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
+                            BN[0].getNodeAs<Decl>("func")),
+              R"cpp(
+FunctionDecl 'forLoop'
+`-CompoundStmt
+  |-DeclStmt
+  | `-VarDecl 'arr'
+  |-CXXForRangeStmt
+  | |-<<<NULL>>>
+  | |-VarDecl 'i'
+  | |-DeclRefExpr 'arr'
+  | `-CompoundStmt
+  `-CXXForRangeStmt
+    |-DeclStmt
+    | `-VarDecl 'a'
+    |   `-DeclRefExpr 'arr'
+    |-VarDecl 'i'
+    |-DeclRefExpr 'a'
+    `-CompoundStmt
+)cpp");
+  }
+  {
+    auto BN = ast_matchers::match(
+        cxxRecordDecl(hasName("DefaultedAndDeleted"), unless(isImplicit()))
+            .bind("rec"),
+        AST2->getASTContext());
+    EXPECT_EQ(BN.size(), 1u);
+
+    EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("rec")),
+              R"cpp(
+CXXRecordDecl 'DefaultedAndDeleted'
+|-CXXRecordDecl 'DefaultedAndDeleted'
+|-FieldDecl 'nt'
+|-CXXConstructorDecl 'DefaultedAndDeleted'
+| |-CXXCtorInitializer 'nt'
+| | `-CXXConstructExpr
+| `-CompoundStmt
+|-CXXDestructorDecl '~DefaultedAndDeleted'
+| `-CompoundStmt
+|-CXXConstructorDecl 'DefaultedAndDeleted'
+| `-ParmVarDecl ''
+|-CXXMethodDecl 'operator='
+| |-ParmVarDecl ''
+| `-CompoundStmt
+|   |-CXXMemberCallExpr
+|   | |-MemberExpr
+|   | | `-MemberExpr
+|   | |   `-CXXThisExpr
+|   | `-MemberExpr
+|   |   `-DeclRefExpr ''
+|   `-ReturnStmt
+|     `-UnaryOperator
+|       `-CXXThisExpr
+|-CXXConstructorDecl 'DefaultedAndDeleted'
+| `-ParmVarDecl ''
+`-CXXMethodDecl 'operator='
+  `-ParmVarDecl ''
+)cpp");
+
+    EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
+                            BN[0].getNodeAs<Decl>("rec")),
+              R"cpp(
+CXXRecordDecl 'DefaultedAndDeleted'
+|-FieldDecl 'nt'
+|-CXXConstructorDecl 'DefaultedAndDeleted'
+|-CXXDestructorDecl '~DefaultedAndDeleted'
+|-CXXConstructorDecl 'DefaultedAndDeleted'
+| `-ParmVarDecl ''
+|-CXXMethodDecl 'operator='
+| `-ParmVarDecl ''
+|-CXXConstructorDecl 'DefaultedAndDeleted'
+| `-ParmVarDecl ''
+`-CXXMethodDecl 'operator='
+  `-ParmVarDecl ''
+)cpp");
+  }
+  {
+    auto BN = ast_matchers::match(
+        callExpr(callee(functionDecl(hasName("hasDefaultArg"))))
+            .bind("funcCall"),
+        AST2->getASTContext());
+    EXPECT_EQ(BN.size(), 1u);
+
+    EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<CallExpr>("funcCall")),
+              R"cpp(
+CallExpr
+|-ImplicitCastExpr
+| `-DeclRefExpr 'hasDefaultArg'
+|-IntegerLiteral
+`-CXXDefaultArgExpr
+)cpp");
+    EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
+                            BN[0].getNodeAs<CallExpr>("funcCall")),
+              R"cpp(
+CallExpr
+|-DeclRefExpr 'hasDefaultArg'
+`-IntegerLiteral
+)cpp");
+  }
+}
+
 TEST(Traverse, IgnoreUnlessSpelledInSourceTemplateInstantiations) {
 
   auto AST = buildASTFromCode(R"cpp(
@@ -1108,7 +1524,6 @@
 ClassTemplateDecl 'TemplStruct'
 |-TemplateTypeParmDecl 'T'
 `-CXXRecordDecl 'TemplStruct'
-  |-CXXRecordDecl 'TemplStruct'
   |-CXXConstructorDecl 'TemplStruct<T>'
   | `-CompoundStmt
   |-CXXDestructorDecl '~TemplStruct<T>'
@@ -1183,6 +1598,47 @@
   | `-CompoundStmt
   |-AccessSpecDecl
   `-FieldDecl 'm_t'
+)cpp");
+  }
+  {
+    auto BN = ast_matchers::match(
+        classTemplateSpecializationDecl(
+            hasTemplateArgument(
+                0, templateArgument(refersToType(asString("_Bool")))))
+            .bind("templSpec"),
+        AST->getASTContext());
+    EXPECT_EQ(BN.size(), 1u);
+
+    EXPECT_EQ(dumpASTString(TK_AsIs, BN[0].getNodeAs<Decl>("templSpec")),
+              R"cpp(
+ClassTemplateSpecializationDecl 'TemplStruct'
+|-TemplateArgument type _Bool
+| `-BuiltinType
+|-CXXRecordDecl 'TemplStruct'
+|-CXXConstructorDecl 'TemplStruct'
+| `-CompoundStmt
+|-CXXDestructorDecl '~TemplStruct'
+| `-CompoundStmt
+|-CXXMethodDecl 'foo'
+| `-CompoundStmt
+|-AccessSpecDecl
+`-FieldDecl 'm_t'
+)cpp");
+
+    EXPECT_EQ(dumpASTString(TK_IgnoreUnlessSpelledInSource,
+                            BN[0].getNodeAs<Decl>("templSpec")),
+              R"cpp(
+ClassTemplateSpecializationDecl 'TemplStruct'
+|-TemplateArgument type _Bool
+| `-BuiltinType
+|-CXXConstructorDecl 'TemplStruct'
+| `-CompoundStmt
+|-CXXDestructorDecl '~TemplStruct'
+| `-CompoundStmt
+|-CXXMethodDecl 'foo'
+| `-CompoundStmt
+|-AccessSpecDecl
+`-FieldDecl 'm_t'
 )cpp");
   }
   {
Index: clang/lib/ASTMatchers/ASTMatchersInternal.cpp
===================================================================
--- clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -286,7 +286,7 @@
 
   if (Finder->getASTContext().getParentMapContext().getTraversalKind() ==
           TK_IgnoreUnlessSpelledInSource &&
-      Finder->IsMatchingInTemplateInstantiationNotSpelledInSource())
+      Finder->IsMatchingInASTNodeNotSpelledInSource())
     return false;
 
   auto N =
@@ -311,7 +311,7 @@
 
   if (Finder->getASTContext().getParentMapContext().getTraversalKind() ==
           TK_IgnoreUnlessSpelledInSource &&
-      Finder->IsMatchingInTemplateInstantiationNotSpelledInSource())
+      Finder->IsMatchingInASTNodeNotSpelledInSource())
     return false;
 
   auto N =
Index: clang/lib/ASTMatchers/ASTMatchFinder.cpp
===================================================================
--- clang/lib/ASTMatchers/ASTMatchFinder.cpp
+++ clang/lib/ASTMatchers/ASTMatchFinder.cpp
@@ -95,9 +95,12 @@
   // matching the descendants.
   MatchChildASTVisitor(const DynTypedMatcher *Matcher, ASTMatchFinder *Finder,
                        BoundNodesTreeBuilder *Builder, int MaxDepth,
-                       TraversalKind Traversal, ASTMatchFinder::BindKind Bind)
+                       TraversalKind Traversal, bool IgnoreImplicitChildren,
+                       ASTMatchFinder::BindKind Bind)
       : Matcher(Matcher), Finder(Finder), Builder(Builder), CurrentDepth(0),
-        MaxDepth(MaxDepth), Traversal(Traversal), Bind(Bind), Matches(false) {}
+        MaxDepth(MaxDepth), Traversal(Traversal),
+        IgnoreImplicitChildren(IgnoreImplicitChildren), Bind(Bind),
+        Matches(false) {}
 
   // Returns true if a match is found in the subtree rooted at the
   // given AST node. This is done via a set of mutually recursive
@@ -145,6 +148,12 @@
   // They are public only to allow CRTP to work. They are *not *part
   // of the public API of this class.
   bool TraverseDecl(Decl *DeclNode) {
+
+    if (DeclNode && DeclNode->isImplicit() &&
+        Finder->getASTContext().getParentMapContext().getTraversalKind() ==
+            TK_IgnoreUnlessSpelledInSource)
+      return baseTraverse(*DeclNode);
+
     ScopedIncrement ScopedDepth(&CurrentDepth);
     return (DeclNode == nullptr) || traverse(*DeclNode);
   }
@@ -178,6 +187,10 @@
     Stmt *StmtToTraverse = getStmtToTraverse(StmtNode);
     if (!StmtToTraverse)
       return true;
+
+    if (IgnoreImplicitChildren && isa<CXXDefaultArgExpr>(StmtNode))
+      return true;
+
     if (!match(*StmtToTraverse))
       return false;
     return VisitorBase::TraverseStmt(StmtToTraverse, Queue);
@@ -268,7 +281,7 @@
   }
 
   bool shouldVisitTemplateInstantiations() const { return true; }
-  bool shouldVisitImplicitCode() const { return true; }
+  bool shouldVisitImplicitCode() const { return !IgnoreImplicitChildren; }
 
 private:
   // Used for updating the depth during traversal.
@@ -363,6 +376,7 @@
   int CurrentDepth;
   const int MaxDepth;
   const TraversalKind Traversal;
+  const bool IgnoreImplicitChildren;
   const ASTMatchFinder::BindKind Bind;
   bool Matches;
 };
@@ -500,19 +514,18 @@
                           const DynTypedMatcher &Matcher,
                           BoundNodesTreeBuilder *Builder, int MaxDepth,
                           TraversalKind Traversal, BindKind Bind) {
-    bool ScopedTraversal = TraversingTemplateInstantiationNotSpelledInSource;
+    bool ScopedTraversal = TraversingASTNodeNotSpelledInSource ||
+                           TraversingASTChildrenNotSpelledInSource;
 
-    if (const auto *CTSD = Node.get<ClassTemplateSpecializationDecl>()) {
-      int SK = CTSD->getSpecializationKind();
-      if (SK == TSK_ExplicitInstantiationDeclaration ||
-          SK == TSK_ExplicitInstantiationDefinition)
-        ScopedTraversal = true;
-    }
+    bool IgnoreImplicitChildren = false;
+
+    if (getASTContext().getParentMapContext().getTraversalKind() != TK_AsIs)
+      IgnoreImplicitChildren = true;
 
-    TemplateInstantiationNotSpelledInSourceScope RAII(this, ScopedTraversal);
+    ASTNodeNotSpelledInSourceScope RAII(this, ScopedTraversal);
 
-    MatchChildASTVisitor Visitor(
-      &Matcher, this, Builder, MaxDepth, Traversal, Bind);
+    MatchChildASTVisitor Visitor(&Matcher, this, Builder, MaxDepth, Traversal,
+                                 IgnoreImplicitChildren, Bind);
     return Visitor.findMatch(Node);
   }
 
@@ -595,38 +608,53 @@
   bool shouldVisitTemplateInstantiations() const { return true; }
   bool shouldVisitImplicitCode() const { return true; }
 
-  bool IsMatchingInTemplateInstantiationNotSpelledInSource() const override {
-    return TraversingTemplateInstantiationNotSpelledInSource;
+  bool IsMatchingInASTNodeNotSpelledInSource() const override {
+    return TraversingASTNodeNotSpelledInSource;
   }
 
   bool TraverseTemplateInstantiations(ClassTemplateDecl *D) {
-    TemplateInstantiationNotSpelledInSourceScope RAII(this, true);
+    ASTNodeNotSpelledInSourceScope RAII(this, true);
     return RecursiveASTVisitor<MatchASTVisitor>::TraverseTemplateInstantiations(
         D);
   }
 
   bool TraverseTemplateInstantiations(VarTemplateDecl *D) {
-    TemplateInstantiationNotSpelledInSourceScope RAII(this, true);
+    ASTNodeNotSpelledInSourceScope RAII(this, true);
     return RecursiveASTVisitor<MatchASTVisitor>::TraverseTemplateInstantiations(
         D);
   }
 
   bool TraverseTemplateInstantiations(FunctionTemplateDecl *D) {
-    TemplateInstantiationNotSpelledInSourceScope RAII(this, true);
+    ASTNodeNotSpelledInSourceScope RAII(this, true);
     return RecursiveASTVisitor<MatchASTVisitor>::TraverseTemplateInstantiations(
         D);
   }
 
 private:
-  bool TraversingTemplateInstantiationNotSpelledInSource = false;
+  bool TraversingASTNodeNotSpelledInSource = false;
+  bool TraversingASTChildrenNotSpelledInSource = false;
+
+  struct ASTNodeNotSpelledInSourceScope {
+    ASTNodeNotSpelledInSourceScope(MatchASTVisitor *V, bool B)
+        : MV(V), MB(V->TraversingASTNodeNotSpelledInSource) {
+      V->TraversingASTNodeNotSpelledInSource = B;
+    }
+    ~ASTNodeNotSpelledInSourceScope() {
+      MV->TraversingASTNodeNotSpelledInSource = MB;
+    }
 
-  struct TemplateInstantiationNotSpelledInSourceScope {
-    TemplateInstantiationNotSpelledInSourceScope(MatchASTVisitor *V, bool B)
-        : MV(V), MB(V->TraversingTemplateInstantiationNotSpelledInSource) {
-      V->TraversingTemplateInstantiationNotSpelledInSource = B;
+  private:
+    MatchASTVisitor *MV;
+    bool MB;
+  };
+
+  struct ASTChildrenNotSpelledInSource {
+    ASTChildrenNotSpelledInSource(MatchASTVisitor *V, bool B)
+        : MV(V), MB(V->TraversingASTChildrenNotSpelledInSource) {
+      V->TraversingASTChildrenNotSpelledInSource = B;
     }
-    ~TemplateInstantiationNotSpelledInSourceScope() {
-      MV->TraversingTemplateInstantiationNotSpelledInSource = MB;
+    ~ASTChildrenNotSpelledInSource() {
+      MV->TraversingASTChildrenNotSpelledInSource = MB;
     }
 
   private:
@@ -1053,6 +1081,24 @@
   if (!DeclNode) {
     return true;
   }
+
+  auto ScopedTraversal =
+      TraversingASTNodeNotSpelledInSource || DeclNode->isImplicit();
+  auto ScopedChildren = TraversingASTChildrenNotSpelledInSource;
+
+  if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(DeclNode)) {
+    auto SK = CTSD->getSpecializationKind();
+    if (SK == TSK_ExplicitInstantiationDeclaration ||
+        SK == TSK_ExplicitInstantiationDefinition)
+      ScopedChildren = true;
+  } else if (const auto *FD = dyn_cast<FunctionDecl>(DeclNode)) {
+    if (FD->isDefaulted())
+      ScopedChildren = true;
+  }
+
+  ASTNodeNotSpelledInSourceScope RAII1(this, ScopedTraversal);
+  ASTChildrenNotSpelledInSource RAII2(this, ScopedChildren);
+
   match(*DeclNode);
   return RecursiveASTVisitor<MatchASTVisitor>::TraverseDecl(DeclNode);
 }
@@ -1061,6 +1107,13 @@
   if (!StmtNode) {
     return true;
   }
+  auto ScopedTraversal = TraversingASTNodeNotSpelledInSource ||
+                         TraversingASTChildrenNotSpelledInSource;
+
+  if (isa<CXXForRangeStmt>(StmtNode))
+    TraversingASTChildrenNotSpelledInSource = true;
+
+  ASTNodeNotSpelledInSourceScope RAII(this, ScopedTraversal);
   match(*StmtNode);
   return RecursiveASTVisitor<MatchASTVisitor>::TraverseStmt(StmtNode, Queue);
 }
Index: clang/include/clang/ASTMatchers/ASTMatchersInternal.h
===================================================================
--- clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -1060,7 +1060,7 @@
 
   virtual ASTContext &getASTContext() const = 0;
 
-  virtual bool IsMatchingInTemplateInstantiationNotSpelledInSource() const = 0;
+  virtual bool IsMatchingInASTNodeNotSpelledInSource() const = 0;
 
 protected:
   virtual bool matchesChildOf(const DynTypedNode &Node, ASTContext &Ctx,
Index: clang/include/clang/AST/ASTNodeTraverser.h
===================================================================
--- clang/include/clang/AST/ASTNodeTraverser.h
+++ clang/include/clang/AST/ASTNodeTraverser.h
@@ -85,6 +85,9 @@
   TraversalKind GetTraversalKind() const { return Traversal; }
 
   void Visit(const Decl *D) {
+    if (Traversal != TK_AsIs && D->isImplicit())
+      return;
+
     getNodeDelegate().AddChild([=] {
       getNodeDelegate().Visit(D);
       if (!D)
@@ -101,8 +104,14 @@
 
       // Decls within functions are visited by the body.
       if (!isa<FunctionDecl>(*D) && !isa<ObjCMethodDecl>(*D)) {
-        if (isa<ClassTemplateSpecializationDecl>(*D) && Traversal != TK_AsIs)
-          return;
+        if (Traversal != TK_AsIs) {
+          if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
+            int SK = CTSD->getSpecializationKind();
+            if (SK == TSK_ExplicitInstantiationDeclaration ||
+                SK == TSK_ExplicitInstantiationDefinition)
+              return;
+          }
+        }
         if (const auto *DC = dyn_cast<DeclContext>(D))
           dumpDeclContext(DC);
       }
@@ -138,7 +147,8 @@
       if (isa<DeclStmt>(S) || isa<GenericSelectionExpr>(S))
         return;
 
-      if (isa<LambdaExpr>(S) && Traversal == TK_IgnoreUnlessSpelledInSource)
+      if (Traversal == TK_IgnoreUnlessSpelledInSource &&
+          isa<LambdaExpr, CXXForRangeStmt, CallExpr>(S))
         return;
 
       for (const Stmt *SubStmt : S->children())
@@ -179,6 +189,8 @@
   }
 
   void Visit(const CXXCtorInitializer *Init) {
+    if (Traversal != TK_AsIs && !Init->isWritten())
+      return;
     getNodeDelegate().AddChild([=] {
       getNodeDelegate().Visit(Init);
       Visit(Init->getInit());
@@ -395,6 +407,9 @@
     if (const Expr *TRC = D->getTrailingRequiresClause())
       Visit(TRC);
 
+    if (Traversal != TK_AsIs && D->isDefaulted())
+      return;
+
     if (const auto *C = dyn_cast<CXXConstructorDecl>(D))
       for (const auto *I : C->inits())
         Visit(I);
@@ -411,6 +426,9 @@
   }
 
   void VisitVarDecl(const VarDecl *D) {
+    if (Traversal != TK_AsIs && D->isCXXForRangeDecl())
+      return;
+
     if (D->hasInit())
       Visit(D->getInit());
   }
@@ -711,6 +729,26 @@
       Visit(CatchParam);
   }
 
+  void VisitCXXForRangeStmt(const CXXForRangeStmt *Node) {
+    if (Traversal != TK_AsIs) {
+      Visit(Node->getInit());
+      Visit(Node->getLoopVariable());
+      Visit(Node->getRangeInit());
+      Visit(Node->getBody());
+    }
+  }
+
+  void VisitCallExpr(const CallExpr *Node) {
+    for (auto Child :
+         make_filter_range(Node->children(), [this](const Stmt *Child) {
+           if (Traversal == TK_AsIs)
+             return false;
+           return !isa<CXXDefaultArgExpr>(Child);
+         })) {
+      Visit(Child);
+    }
+  }
+
   void VisitExpressionTemplateArgument(const TemplateArgument &TA) {
     Visit(TA.getAsExpr());
   }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to