| //===- unittest/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp -------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "TestVisitor.h" |
| #include "clang/AST/LexicallyOrderedRecursiveASTVisitor.h" |
| #include <stack> |
| |
| using namespace clang; |
| |
| namespace { |
| |
| class DummyMatchVisitor; |
| |
| class LexicallyOrderedDeclVisitor |
| : public LexicallyOrderedRecursiveASTVisitor<LexicallyOrderedDeclVisitor> { |
| public: |
| LexicallyOrderedDeclVisitor(DummyMatchVisitor &Matcher, |
| const SourceManager &SM, bool EmitDeclIndices, |
| bool EmitStmtIndices) |
| : LexicallyOrderedRecursiveASTVisitor(SM), Matcher(Matcher), |
| EmitDeclIndices(EmitDeclIndices), EmitStmtIndices(EmitStmtIndices) {} |
| |
| bool TraverseDecl(Decl *D) { |
| TraversalStack.push_back(D); |
| LexicallyOrderedRecursiveASTVisitor::TraverseDecl(D); |
| TraversalStack.pop_back(); |
| return true; |
| } |
| |
| bool TraverseStmt(Stmt *S); |
| |
| bool VisitNamedDecl(const NamedDecl *D); |
| bool VisitDeclRefExpr(const DeclRefExpr *D); |
| |
| private: |
| DummyMatchVisitor &Matcher; |
| bool EmitDeclIndices, EmitStmtIndices; |
| unsigned Index = 0; |
| llvm::SmallVector<Decl *, 8> TraversalStack; |
| }; |
| |
| class DummyMatchVisitor : public ExpectedLocationVisitor<DummyMatchVisitor> { |
| bool EmitDeclIndices, EmitStmtIndices; |
| |
| public: |
| DummyMatchVisitor(bool EmitDeclIndices = false, bool EmitStmtIndices = false) |
| : EmitDeclIndices(EmitDeclIndices), EmitStmtIndices(EmitStmtIndices) {} |
| bool VisitTranslationUnitDecl(TranslationUnitDecl *TU) { |
| const ASTContext &Context = TU->getASTContext(); |
| const SourceManager &SM = Context.getSourceManager(); |
| LexicallyOrderedDeclVisitor SubVisitor(*this, SM, EmitDeclIndices, |
| EmitStmtIndices); |
| SubVisitor.TraverseDecl(TU); |
| return false; |
| } |
| |
| template <class T> void match(StringRef Path, const T *D) { |
| Match(Path, D->getBeginLoc()); |
| } |
| }; |
| |
| bool LexicallyOrderedDeclVisitor::TraverseStmt(Stmt *S) { |
| Matcher.match("overridden TraverseStmt", S); |
| return LexicallyOrderedRecursiveASTVisitor::TraverseStmt(S); |
| } |
| |
| bool LexicallyOrderedDeclVisitor::VisitNamedDecl(const NamedDecl *D) { |
| std::string Path; |
| llvm::raw_string_ostream OS(Path); |
| assert(TraversalStack.back() == D); |
| for (const Decl *D : TraversalStack) { |
| if (isa<TranslationUnitDecl>(D)) { |
| OS << "/"; |
| continue; |
| } |
| if (const auto *ND = dyn_cast<NamedDecl>(D)) |
| OS << ND->getNameAsString(); |
| else |
| OS << "???"; |
| if (isa<DeclContext>(D) || isa<TemplateDecl>(D)) |
| OS << "/"; |
| } |
| if (EmitDeclIndices) |
| OS << "@" << Index++; |
| Matcher.match(OS.str(), D); |
| return true; |
| } |
| |
| bool LexicallyOrderedDeclVisitor::VisitDeclRefExpr(const DeclRefExpr *D) { |
| std::string Name = D->getFoundDecl()->getNameAsString(); |
| llvm::raw_string_ostream OS(Name); |
| if (EmitStmtIndices) |
| OS << "@" << Index++; |
| Matcher.match(OS.str(), D); |
| return true; |
| } |
| |
| TEST(LexicallyOrderedRecursiveASTVisitor, VisitDeclsInImplementation) { |
| StringRef Source = R"( |
| @interface I |
| @end |
| @implementation I |
| |
| int nestedFunction() { } |
| |
| - (void) method{ } |
| |
| int anotherNestedFunction(int x) { |
| return x; |
| } |
| |
| int innerVariable = 0; |
| |
| @end |
| |
| int outerVariable = 0; |
| |
| @implementation I(Cat) |
| |
| void catF() { } |
| |
| @end |
| |
| void outerFunction() { } |
| )"; |
| DummyMatchVisitor Visitor; |
| Visitor.DisallowMatch("/nestedFunction/", 6, 1); |
| Visitor.ExpectMatch("/I/nestedFunction/", 6, 1); |
| Visitor.ExpectMatch("/I/method/", 8, 1); |
| Visitor.DisallowMatch("/anotherNestedFunction/", 10, 1); |
| Visitor.ExpectMatch("/I/anotherNestedFunction/", 10, 1); |
| Visitor.DisallowMatch("/innerVariable", 14, 1); |
| Visitor.ExpectMatch("/I/innerVariable", 14, 1); |
| Visitor.ExpectMatch("/outerVariable", 18, 1); |
| Visitor.DisallowMatch("/catF/", 22, 1); |
| Visitor.ExpectMatch("/Cat/catF/", 22, 1); |
| Visitor.ExpectMatch("/outerFunction/", 26, 1); |
| EXPECT_TRUE(Visitor.runOver(Source, DummyMatchVisitor::Lang_OBJC)); |
| } |
| |
| TEST(LexicallyOrderedRecursiveASTVisitor, VisitMacroDeclsInImplementation) { |
| StringRef Source = R"( |
| @interface I |
| @end |
| |
| void outerFunction() { } |
| |
| #define MACRO_F(x) void nestedFunction##x() { } |
| |
| @implementation I |
| |
| MACRO_F(1) |
| |
| @end |
| |
| MACRO_F(2) |
| )"; |
| DummyMatchVisitor Visitor; |
| Visitor.ExpectMatch("/outerFunction/", 5, 1); |
| Visitor.ExpectMatch("/I/nestedFunction1/", 7, 20); |
| Visitor.ExpectMatch("/nestedFunction2/", 7, 20); |
| EXPECT_TRUE(Visitor.runOver(Source, DummyMatchVisitor::Lang_OBJC)); |
| } |
| |
| TEST(LexicallyOrderedRecursiveASTVisitor, VisitTemplateDecl) { |
| StringRef Source = R"( |
| template <class T> T f(); |
| template <class U, class = void> class Class {}; |
| )"; |
| DummyMatchVisitor Visitor(/*EmitIndices=*/true); |
| Visitor.ExpectMatch("/f/T@1", 2, 11); |
| Visitor.ExpectMatch("/f/f/@2", 2, 20); |
| Visitor.ExpectMatch("/Class/U@4", 3, 11); |
| Visitor.ExpectMatch("/Class/@5", 3, 20); |
| Visitor.ExpectMatch("/Class/Class/@6", 3, 34); |
| EXPECT_TRUE(Visitor.runOver(Source)); |
| } |
| |
| TEST(LexicallyOrderedRecursiveASTVisitor, VisitCXXOperatorCallExpr) { |
| StringRef Source = R"( |
| struct S { |
| S &operator+(S&); |
| S *operator->(); |
| S &operator++(); |
| S operator++(int); |
| void operator()(int, int); |
| void operator[](int); |
| void f(); |
| }; |
| S a, b, c; |
| |
| void test() { |
| a = b + c; |
| a->f(); |
| a(1, 2); |
| b[0]; |
| ++a; |
| b++; |
| } |
| )"; |
| DummyMatchVisitor Visitor(/*EmitDeclIndices=*/false, |
| /*EmitStmtIndices=*/true); |
| // There are two overloaded operators that start at this point |
| // This makes sure they are both traversed using the overridden |
| // TraverseStmt, as the traversal is implemented by us for |
| // CXXOperatorCallExpr. |
| Visitor.ExpectMatch("overridden TraverseStmt", 14, 3, 2); |
| Visitor.ExpectMatch("a@0", 14, 3); |
| Visitor.ExpectMatch("operator=@1", 14, 5); |
| Visitor.ExpectMatch("b@2", 14, 7); |
| Visitor.ExpectMatch("operator+@3", 14, 9); |
| Visitor.ExpectMatch("c@4", 14, 11); |
| Visitor.ExpectMatch("operator->@6", 15, 4); |
| Visitor.ExpectMatch("operator()@8", 16, 4); |
| Visitor.ExpectMatch("operator[]@10", 17, 4); |
| Visitor.ExpectMatch("operator++@11", 18, 3); |
| Visitor.ExpectMatch("operator++@14", 19, 4); |
| EXPECT_TRUE(Visitor.runOver(Source)); |
| } |
| |
| } // end anonymous namespace |