1 //===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "clang/AST/ASTConsumer.h" 11 #include "clang/AST/DeclCXX.h" 12 #include "clang/AST/DeclGroup.h" 13 #include "clang/Frontend/CompilerInstance.h" 14 #include "clang/Frontend/FrontendAction.h" 15 #include "clang/Frontend/FrontendActions.h" 16 #include "clang/Tooling/CompilationDatabase.h" 17 #include "clang/Tooling/Tooling.h" 18 #include "gtest/gtest.h" 19 #include <string> 20 21 namespace clang { 22 namespace tooling { 23 24 namespace { 25 /// Takes an ast consumer and returns it from CreateASTConsumer. This only 26 /// works with single translation unit compilations. 27 class TestAction : public clang::ASTFrontendAction { 28 public: 29 /// Takes ownership of TestConsumer. 30 explicit TestAction(clang::ASTConsumer *TestConsumer) 31 : TestConsumer(TestConsumer) {} 32 33 protected: 34 virtual clang::ASTConsumer* CreateASTConsumer( 35 clang::CompilerInstance& compiler, StringRef dummy) { 36 /// TestConsumer will be deleted by the framework calling us. 37 return TestConsumer; 38 } 39 40 private: 41 clang::ASTConsumer * const TestConsumer; 42 }; 43 44 class FindTopLevelDeclConsumer : public clang::ASTConsumer { 45 public: 46 explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl) 47 : FoundTopLevelDecl(FoundTopLevelDecl) {} 48 virtual bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) { 49 *FoundTopLevelDecl = true; 50 return true; 51 } 52 private: 53 bool * const FoundTopLevelDecl; 54 }; 55 } // end namespace 56 57 TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) { 58 bool FoundTopLevelDecl = false; 59 EXPECT_TRUE(runToolOnCode( 60 new TestAction(new FindTopLevelDeclConsumer(&FoundTopLevelDecl)), "")); 61 #if !defined(_MSC_VER) 62 EXPECT_FALSE(FoundTopLevelDecl); 63 #else 64 // FIXME: LangOpts.MicrosoftExt appends "class type_info;" 65 EXPECT_TRUE(FoundTopLevelDecl); 66 #endif 67 } 68 69 namespace { 70 class FindClassDeclXConsumer : public clang::ASTConsumer { 71 public: 72 FindClassDeclXConsumer(bool *FoundClassDeclX) 73 : FoundClassDeclX(FoundClassDeclX) {} 74 virtual bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) { 75 if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>( 76 *GroupRef.begin())) { 77 if (Record->getName() == "X") { 78 *FoundClassDeclX = true; 79 } 80 } 81 return true; 82 } 83 private: 84 bool *FoundClassDeclX; 85 }; 86 } // end namespace 87 88 TEST(runToolOnCode, FindsClassDecl) { 89 bool FoundClassDeclX = false; 90 EXPECT_TRUE(runToolOnCode(new TestAction( 91 new FindClassDeclXConsumer(&FoundClassDeclX)), "class X;")); 92 EXPECT_TRUE(FoundClassDeclX); 93 94 FoundClassDeclX = false; 95 EXPECT_TRUE(runToolOnCode(new TestAction( 96 new FindClassDeclXConsumer(&FoundClassDeclX)), "class Y;")); 97 EXPECT_FALSE(FoundClassDeclX); 98 } 99 100 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) { 101 OwningPtr<FrontendActionFactory> Factory( 102 newFrontendActionFactory<SyntaxOnlyAction>()); 103 OwningPtr<FrontendAction> Action(Factory->create()); 104 EXPECT_TRUE(Action.get() != NULL); 105 } 106 107 struct IndependentFrontendActionCreator { 108 ASTConsumer *newASTConsumer() { 109 return new FindTopLevelDeclConsumer(NULL); 110 } 111 }; 112 113 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) { 114 IndependentFrontendActionCreator Creator; 115 OwningPtr<FrontendActionFactory> Factory( 116 newFrontendActionFactory(&Creator)); 117 OwningPtr<FrontendAction> Action(Factory->create()); 118 EXPECT_TRUE(Action.get() != NULL); 119 } 120 121 TEST(ToolInvocation, TestMapVirtualFile) { 122 clang::FileManager Files((clang::FileSystemOptions())); 123 std::vector<std::string> Args; 124 Args.push_back("tool-executable"); 125 Args.push_back("-Idef"); 126 Args.push_back("-fsyntax-only"); 127 Args.push_back("test.cpp"); 128 clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, &Files); 129 Invocation.mapVirtualFile("test.cpp", "#include <abc>\n"); 130 Invocation.mapVirtualFile("def/abc", "\n"); 131 EXPECT_TRUE(Invocation.run()); 132 } 133 134 struct VerifyEndCallback : public SourceFileCallbacks { 135 VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {} 136 virtual bool handleBeginSource(CompilerInstance &CI, 137 StringRef Filename) LLVM_OVERRIDE { 138 ++BeginCalled; 139 return true; 140 } 141 virtual void handleEndSource() { 142 ++EndCalled; 143 } 144 ASTConsumer *newASTConsumer() { 145 return new FindTopLevelDeclConsumer(&Matched); 146 } 147 unsigned BeginCalled; 148 unsigned EndCalled; 149 bool Matched; 150 }; 151 152 #if !defined(_WIN32) 153 TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) { 154 VerifyEndCallback EndCallback; 155 156 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 157 std::vector<std::string> Sources; 158 Sources.push_back("/a.cc"); 159 Sources.push_back("/b.cc"); 160 ClangTool Tool(Compilations, Sources); 161 162 Tool.mapVirtualFile("/a.cc", "void a() {}"); 163 Tool.mapVirtualFile("/b.cc", "void b() {}"); 164 165 Tool.run(newFrontendActionFactory(&EndCallback, &EndCallback)); 166 167 EXPECT_TRUE(EndCallback.Matched); 168 EXPECT_EQ(2u, EndCallback.BeginCalled); 169 EXPECT_EQ(2u, EndCallback.EndCalled); 170 } 171 #endif 172 173 struct SkipBodyConsumer : public clang::ASTConsumer { 174 /// Skip the 'skipMe' function. 175 virtual bool shouldSkipFunctionBody(Decl *D) { 176 FunctionDecl *F = dyn_cast<FunctionDecl>(D); 177 return F && F->getNameAsString() == "skipMe"; 178 } 179 }; 180 181 struct SkipBodyAction : public clang::ASTFrontendAction { 182 virtual ASTConsumer *CreateASTConsumer(CompilerInstance &Compiler, 183 StringRef) { 184 Compiler.getFrontendOpts().SkipFunctionBodies = true; 185 return new SkipBodyConsumer; 186 } 187 }; 188 189 TEST(runToolOnCode, TestSkipFunctionBody) { 190 EXPECT_TRUE(runToolOnCode(new SkipBodyAction, 191 "int skipMe() { an_error_here }")); 192 EXPECT_FALSE(runToolOnCode(new SkipBodyAction, 193 "int skipMeNot() { an_error_here }")); 194 } 195 196 struct CheckSyntaxOnlyAdjuster: public ArgumentsAdjuster { 197 bool &Found; 198 bool &Ran; 199 200 CheckSyntaxOnlyAdjuster(bool &Found, bool &Ran) : Found(Found), Ran(Ran) { } 201 202 virtual CommandLineArguments 203 Adjust(const CommandLineArguments &Args) LLVM_OVERRIDE { 204 Ran = true; 205 for (unsigned I = 0, E = Args.size(); I != E; ++I) { 206 if (Args[I] == "-fsyntax-only") { 207 Found = true; 208 break; 209 } 210 } 211 return Args; 212 } 213 }; 214 215 TEST(ClangToolTest, ArgumentAdjusters) { 216 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 217 218 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 219 Tool.mapVirtualFile("/a.cc", "void a() {}"); 220 221 bool Found = false; 222 bool Ran = false; 223 Tool.appendArgumentsAdjuster(new CheckSyntaxOnlyAdjuster(Found, Ran)); 224 Tool.run(newFrontendActionFactory<SyntaxOnlyAction>()); 225 EXPECT_TRUE(Ran); 226 EXPECT_TRUE(Found); 227 228 Ran = Found = false; 229 Tool.clearArgumentsAdjusters(); 230 Tool.appendArgumentsAdjuster(new CheckSyntaxOnlyAdjuster(Found, Ran)); 231 Tool.appendArgumentsAdjuster(new ClangSyntaxOnlyAdjuster()); 232 Tool.run(newFrontendActionFactory<SyntaxOnlyAction>()); 233 EXPECT_TRUE(Ran); 234 EXPECT_FALSE(Found); 235 } 236 237 } // end namespace tooling 238 } // end namespace clang 239