//===- 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 using namespace clang; namespace { class DummyMatchVisitor; class LexicallyOrderedDeclVisitor : public LexicallyOrderedRecursiveASTVisitor { 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 TraversalStack; }; class DummyMatchVisitor : public ExpectedLocationVisitor { 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 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(D)) { OS << "/"; continue; } if (const auto *ND = dyn_cast(D)) OS << ND->getNameAsString(); else OS << "???"; if (isa(D) || isa(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 T f(); template 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