227 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			227 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- 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
 |