199 lines · cpp
1//===- unittest/AST/RecursiveASTVisitorTest.cpp ---------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/AST/RecursiveASTVisitor.h"10#include "clang/AST/ASTConsumer.h"11#include "clang/AST/ASTContext.h"12#include "clang/AST/Attr.h"13#include "clang/AST/Decl.h"14#include "clang/AST/TypeLoc.h"15#include "clang/Frontend/FrontendAction.h"16#include "clang/Tooling/Tooling.h"17#include "llvm/ADT/FunctionExtras.h"18#include "llvm/ADT/STLExtras.h"19#include "gmock/gmock.h"20#include "gtest/gtest.h"21#include <cassert>22 23using namespace clang;24using ::testing::ElementsAre;25 26namespace {27class ProcessASTAction : public clang::ASTFrontendAction {28public:29 ProcessASTAction(llvm::unique_function<void(clang::ASTContext &)> Process)30 : Process(std::move(Process)) {31 assert(this->Process);32 }33 34 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,35 StringRef InFile) {36 class Consumer : public ASTConsumer {37 public:38 Consumer(llvm::function_ref<void(ASTContext &CTx)> Process)39 : Process(Process) {}40 41 void HandleTranslationUnit(ASTContext &Ctx) override { Process(Ctx); }42 43 private:44 llvm::function_ref<void(ASTContext &CTx)> Process;45 };46 47 return std::make_unique<Consumer>(Process);48 }49 50private:51 llvm::unique_function<void(clang::ASTContext &)> Process;52};53 54enum class VisitEvent {55 StartTraverseFunction,56 EndTraverseFunction,57 StartTraverseAttr,58 EndTraverseAttr,59 StartTraverseEnum,60 EndTraverseEnum,61 StartTraverseTypedefType,62 EndTraverseTypedefType,63 StartTraverseObjCInterface,64 EndTraverseObjCInterface,65 StartTraverseObjCProtocol,66 EndTraverseObjCProtocol,67 StartTraverseObjCProtocolLoc,68 EndTraverseObjCProtocolLoc,69};70 71class CollectInterestingEvents72 : public RecursiveASTVisitor<CollectInterestingEvents> {73public:74 bool TraverseFunctionDecl(FunctionDecl *D) {75 Events.push_back(VisitEvent::StartTraverseFunction);76 bool Ret = RecursiveASTVisitor::TraverseFunctionDecl(D);77 Events.push_back(VisitEvent::EndTraverseFunction);78 79 return Ret;80 }81 82 bool TraverseAttr(Attr *A) {83 Events.push_back(VisitEvent::StartTraverseAttr);84 bool Ret = RecursiveASTVisitor::TraverseAttr(A);85 Events.push_back(VisitEvent::EndTraverseAttr);86 87 return Ret;88 }89 90 bool TraverseEnumDecl(EnumDecl *D) {91 Events.push_back(VisitEvent::StartTraverseEnum);92 bool Ret = RecursiveASTVisitor::TraverseEnumDecl(D);93 Events.push_back(VisitEvent::EndTraverseEnum);94 95 return Ret;96 }97 98 bool TraverseTypedefTypeLoc(TypedefTypeLoc TL, bool TraverseQualifier) {99 Events.push_back(VisitEvent::StartTraverseTypedefType);100 bool Ret =101 RecursiveASTVisitor::TraverseTypedefTypeLoc(TL, TraverseQualifier);102 Events.push_back(VisitEvent::EndTraverseTypedefType);103 104 return Ret;105 }106 107 bool TraverseObjCInterfaceDecl(ObjCInterfaceDecl *ID) {108 Events.push_back(VisitEvent::StartTraverseObjCInterface);109 bool Ret = RecursiveASTVisitor::TraverseObjCInterfaceDecl(ID);110 Events.push_back(VisitEvent::EndTraverseObjCInterface);111 112 return Ret;113 }114 115 bool TraverseObjCProtocolDecl(ObjCProtocolDecl *PD) {116 Events.push_back(VisitEvent::StartTraverseObjCProtocol);117 bool Ret = RecursiveASTVisitor::TraverseObjCProtocolDecl(PD);118 Events.push_back(VisitEvent::EndTraverseObjCProtocol);119 120 return Ret;121 }122 123 bool TraverseObjCProtocolLoc(ObjCProtocolLoc ProtocolLoc) {124 Events.push_back(VisitEvent::StartTraverseObjCProtocolLoc);125 bool Ret = RecursiveASTVisitor::TraverseObjCProtocolLoc(ProtocolLoc);126 Events.push_back(VisitEvent::EndTraverseObjCProtocolLoc);127 128 return Ret;129 }130 131 std::vector<VisitEvent> takeEvents() && { return std::move(Events); }132 133private:134 std::vector<VisitEvent> Events;135};136 137std::vector<VisitEvent> collectEvents(llvm::StringRef Code,138 const Twine &FileName = "input.cc") {139 CollectInterestingEvents Visitor;140 clang::tooling::runToolOnCode(141 std::make_unique<ProcessASTAction>(142 [&](clang::ASTContext &Ctx) { Visitor.TraverseAST(Ctx); }),143 Code, FileName);144 return std::move(Visitor).takeEvents();145}146} // namespace147 148TEST(RecursiveASTVisitorTest, AttributesInsideDecls) {149 /// Check attributes are traversed inside TraverseFunctionDecl.150 llvm::StringRef Code = R"cpp(151__attribute__((annotate("something"))) int foo() { return 10; }152 )cpp";153 154 EXPECT_THAT(collectEvents(Code),155 ElementsAre(VisitEvent::StartTraverseFunction,156 VisitEvent::StartTraverseAttr,157 VisitEvent::EndTraverseAttr,158 VisitEvent::EndTraverseFunction));159}160 161TEST(RecursiveASTVisitorTest, EnumDeclWithBase) {162 // Check enum and its integer base is visited.163 llvm::StringRef Code = R"cpp(164 typedef int Foo;165 enum Bar : Foo;166 )cpp";167 168 EXPECT_THAT(collectEvents(Code),169 ElementsAre(VisitEvent::StartTraverseEnum,170 VisitEvent::StartTraverseTypedefType,171 VisitEvent::EndTraverseTypedefType,172 VisitEvent::EndTraverseEnum));173}174 175TEST(RecursiveASTVisitorTest, InterfaceDeclWithProtocols) {176 // Check interface and its protocols are visited.177 llvm::StringRef Code = R"cpp(178 @protocol Foo179 @end180 @protocol Bar181 @end182 183 @interface SomeObject <Foo, Bar>184 @end185 )cpp";186 187 EXPECT_THAT(collectEvents(Code, "input.m"),188 ElementsAre(VisitEvent::StartTraverseObjCProtocol,189 VisitEvent::EndTraverseObjCProtocol,190 VisitEvent::StartTraverseObjCProtocol,191 VisitEvent::EndTraverseObjCProtocol,192 VisitEvent::StartTraverseObjCInterface,193 VisitEvent::StartTraverseObjCProtocolLoc,194 VisitEvent::EndTraverseObjCProtocolLoc,195 VisitEvent::StartTraverseObjCProtocolLoc,196 VisitEvent::EndTraverseObjCProtocolLoc,197 VisitEvent::EndTraverseObjCInterface));198}199