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/ASTUnit.h" 14 #include "clang/Frontend/CompilerInstance.h" 15 #include "clang/Frontend/FrontendAction.h" 16 #include "clang/Frontend/FrontendActions.h" 17 #include "clang/Tooling/CompilationDatabase.h" 18 #include "clang/Tooling/Tooling.h" 19 #include "llvm/ADT/STLExtras.h" 20 #include "gtest/gtest.h" 21 #include <string> 22 23 namespace clang { 24 namespace tooling { 25 26 namespace { 27 /// Takes an ast consumer and returns it from CreateASTConsumer. This only 28 /// works with single translation unit compilations. 29 class TestAction : public clang::ASTFrontendAction { 30 public: 31 /// Takes ownership of TestConsumer. 32 explicit TestAction(clang::ASTConsumer *TestConsumer) 33 : TestConsumer(TestConsumer) {} 34 35 protected: 36 virtual clang::ASTConsumer* CreateASTConsumer( 37 clang::CompilerInstance& compiler, StringRef dummy) { 38 /// TestConsumer will be deleted by the framework calling us. 39 return TestConsumer; 40 } 41 42 private: 43 clang::ASTConsumer * const TestConsumer; 44 }; 45 46 class FindTopLevelDeclConsumer : public clang::ASTConsumer { 47 public: 48 explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl) 49 : FoundTopLevelDecl(FoundTopLevelDecl) {} 50 virtual bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) { 51 *FoundTopLevelDecl = true; 52 return true; 53 } 54 private: 55 bool * const FoundTopLevelDecl; 56 }; 57 } // end namespace 58 59 TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) { 60 bool FoundTopLevelDecl = false; 61 EXPECT_TRUE(runToolOnCode( 62 new TestAction(new FindTopLevelDeclConsumer(&FoundTopLevelDecl)), "")); 63 EXPECT_FALSE(FoundTopLevelDecl); 64 } 65 66 namespace { 67 class FindClassDeclXConsumer : public clang::ASTConsumer { 68 public: 69 FindClassDeclXConsumer(bool *FoundClassDeclX) 70 : FoundClassDeclX(FoundClassDeclX) {} 71 virtual bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) { 72 if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>( 73 *GroupRef.begin())) { 74 if (Record->getName() == "X") { 75 *FoundClassDeclX = true; 76 } 77 } 78 return true; 79 } 80 private: 81 bool *FoundClassDeclX; 82 }; 83 bool FindClassDeclX(ASTUnit *AST) { 84 for (std::vector<Decl *>::iterator i = AST->top_level_begin(), 85 e = AST->top_level_end(); 86 i != e; ++i) { 87 if (CXXRecordDecl* Record = dyn_cast<clang::CXXRecordDecl>(*i)) { 88 if (Record->getName() == "X") { 89 return true; 90 } 91 } 92 } 93 return false; 94 } 95 } // end namespace 96 97 TEST(runToolOnCode, FindsClassDecl) { 98 bool FoundClassDeclX = false; 99 EXPECT_TRUE(runToolOnCode(new TestAction( 100 new FindClassDeclXConsumer(&FoundClassDeclX)), "class X;")); 101 EXPECT_TRUE(FoundClassDeclX); 102 103 FoundClassDeclX = false; 104 EXPECT_TRUE(runToolOnCode(new TestAction( 105 new FindClassDeclXConsumer(&FoundClassDeclX)), "class Y;")); 106 EXPECT_FALSE(FoundClassDeclX); 107 } 108 109 TEST(buildASTFromCode, FindsClassDecl) { 110 OwningPtr<ASTUnit> AST(buildASTFromCode("class X;")); 111 ASSERT_TRUE(AST.get()); 112 EXPECT_TRUE(FindClassDeclX(AST.get())); 113 114 AST.reset(buildASTFromCode("class Y;")); 115 ASSERT_TRUE(AST.get()); 116 EXPECT_FALSE(FindClassDeclX(AST.get())); 117 } 118 119 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) { 120 OwningPtr<FrontendActionFactory> Factory( 121 newFrontendActionFactory<SyntaxOnlyAction>()); 122 OwningPtr<FrontendAction> Action(Factory->create()); 123 EXPECT_TRUE(Action.get() != NULL); 124 } 125 126 struct IndependentFrontendActionCreator { 127 ASTConsumer *newASTConsumer() { 128 return new FindTopLevelDeclConsumer(NULL); 129 } 130 }; 131 132 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) { 133 IndependentFrontendActionCreator Creator; 134 OwningPtr<FrontendActionFactory> Factory( 135 newFrontendActionFactory(&Creator)); 136 OwningPtr<FrontendAction> Action(Factory->create()); 137 EXPECT_TRUE(Action.get() != NULL); 138 } 139 140 TEST(ToolInvocation, TestMapVirtualFile) { 141 IntrusiveRefCntPtr<clang::FileManager> Files( 142 new clang::FileManager(clang::FileSystemOptions())); 143 std::vector<std::string> Args; 144 Args.push_back("tool-executable"); 145 Args.push_back("-Idef"); 146 Args.push_back("-fsyntax-only"); 147 Args.push_back("test.cpp"); 148 clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, 149 Files.getPtr()); 150 Invocation.mapVirtualFile("test.cpp", "#include <abc>\n"); 151 Invocation.mapVirtualFile("def/abc", "\n"); 152 EXPECT_TRUE(Invocation.run()); 153 } 154 155 TEST(ToolInvocation, TestVirtualModulesCompilation) { 156 // FIXME: Currently, this only tests that we don't exit with an error if a 157 // mapped module.map is found on the include path. In the future, expand this 158 // test to run a full modules enabled compilation, so we make sure we can 159 // rerun modules compilations with a virtual file system. 160 IntrusiveRefCntPtr<clang::FileManager> Files( 161 new clang::FileManager(clang::FileSystemOptions())); 162 std::vector<std::string> Args; 163 Args.push_back("tool-executable"); 164 Args.push_back("-Idef"); 165 Args.push_back("-fsyntax-only"); 166 Args.push_back("test.cpp"); 167 clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, 168 Files.getPtr()); 169 Invocation.mapVirtualFile("test.cpp", "#include <abc>\n"); 170 Invocation.mapVirtualFile("def/abc", "\n"); 171 // Add a module.map file in the include directory of our header, so we trigger 172 // the module.map header search logic. 173 Invocation.mapVirtualFile("def/module.map", "\n"); 174 EXPECT_TRUE(Invocation.run()); 175 } 176 177 struct VerifyEndCallback : public SourceFileCallbacks { 178 VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {} 179 virtual bool handleBeginSource(CompilerInstance &CI, 180 StringRef Filename) LLVM_OVERRIDE { 181 ++BeginCalled; 182 return true; 183 } 184 virtual void handleEndSource() { 185 ++EndCalled; 186 } 187 ASTConsumer *newASTConsumer() { 188 return new FindTopLevelDeclConsumer(&Matched); 189 } 190 unsigned BeginCalled; 191 unsigned EndCalled; 192 bool Matched; 193 }; 194 195 #if !defined(_WIN32) 196 TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) { 197 VerifyEndCallback EndCallback; 198 199 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 200 std::vector<std::string> Sources; 201 Sources.push_back("/a.cc"); 202 Sources.push_back("/b.cc"); 203 ClangTool Tool(Compilations, Sources); 204 205 Tool.mapVirtualFile("/a.cc", "void a() {}"); 206 Tool.mapVirtualFile("/b.cc", "void b() {}"); 207 208 Tool.run(newFrontendActionFactory(&EndCallback, &EndCallback)); 209 210 EXPECT_TRUE(EndCallback.Matched); 211 EXPECT_EQ(2u, EndCallback.BeginCalled); 212 EXPECT_EQ(2u, EndCallback.EndCalled); 213 } 214 #endif 215 216 struct SkipBodyConsumer : public clang::ASTConsumer { 217 /// Skip the 'skipMe' function. 218 virtual bool shouldSkipFunctionBody(Decl *D) { 219 FunctionDecl *F = dyn_cast<FunctionDecl>(D); 220 return F && F->getNameAsString() == "skipMe"; 221 } 222 }; 223 224 struct SkipBodyAction : public clang::ASTFrontendAction { 225 virtual ASTConsumer *CreateASTConsumer(CompilerInstance &Compiler, 226 StringRef) { 227 Compiler.getFrontendOpts().SkipFunctionBodies = true; 228 return new SkipBodyConsumer; 229 } 230 }; 231 232 TEST(runToolOnCode, TestSkipFunctionBody) { 233 EXPECT_TRUE(runToolOnCode(new SkipBodyAction, 234 "int skipMe() { an_error_here }")); 235 EXPECT_FALSE(runToolOnCode(new SkipBodyAction, 236 "int skipMeNot() { an_error_here }")); 237 } 238 239 struct CheckSyntaxOnlyAdjuster: public ArgumentsAdjuster { 240 bool &Found; 241 bool &Ran; 242 243 CheckSyntaxOnlyAdjuster(bool &Found, bool &Ran) : Found(Found), Ran(Ran) { } 244 245 virtual CommandLineArguments 246 Adjust(const CommandLineArguments &Args) LLVM_OVERRIDE { 247 Ran = true; 248 for (unsigned I = 0, E = Args.size(); I != E; ++I) { 249 if (Args[I] == "-fsyntax-only") { 250 Found = true; 251 break; 252 } 253 } 254 return Args; 255 } 256 }; 257 258 TEST(ClangToolTest, ArgumentAdjusters) { 259 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 260 261 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 262 Tool.mapVirtualFile("/a.cc", "void a() {}"); 263 264 bool Found = false; 265 bool Ran = false; 266 Tool.appendArgumentsAdjuster(new CheckSyntaxOnlyAdjuster(Found, Ran)); 267 Tool.run(newFrontendActionFactory<SyntaxOnlyAction>()); 268 EXPECT_TRUE(Ran); 269 EXPECT_TRUE(Found); 270 271 Ran = Found = false; 272 Tool.clearArgumentsAdjusters(); 273 Tool.appendArgumentsAdjuster(new CheckSyntaxOnlyAdjuster(Found, Ran)); 274 Tool.appendArgumentsAdjuster(new ClangSyntaxOnlyAdjuster()); 275 Tool.run(newFrontendActionFactory<SyntaxOnlyAction>()); 276 EXPECT_TRUE(Ran); 277 EXPECT_FALSE(Found); 278 } 279 280 #ifndef _WIN32 281 TEST(ClangToolTest, BuildASTs) { 282 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 283 284 std::vector<std::string> Sources; 285 Sources.push_back("/a.cc"); 286 Sources.push_back("/b.cc"); 287 ClangTool Tool(Compilations, Sources); 288 289 Tool.mapVirtualFile("/a.cc", "void a() {}"); 290 Tool.mapVirtualFile("/b.cc", "void b() {}"); 291 292 std::vector<ASTUnit *> ASTs; 293 EXPECT_EQ(0, Tool.buildASTs(ASTs)); 294 EXPECT_EQ(2u, ASTs.size()); 295 296 llvm::DeleteContainerPointers(ASTs); 297 } 298 299 struct TestDiagnosticConsumer : public DiagnosticConsumer { 300 TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {} 301 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 302 const Diagnostic &Info) { 303 ++NumDiagnosticsSeen; 304 } 305 unsigned NumDiagnosticsSeen; 306 }; 307 308 TEST(ClangToolTest, InjectDiagnosticConsumer) { 309 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 310 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 311 Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); 312 TestDiagnosticConsumer Consumer; 313 Tool.setDiagnosticConsumer(&Consumer); 314 Tool.run(newFrontendActionFactory<SyntaxOnlyAction>()); 315 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 316 } 317 318 TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) { 319 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 320 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 321 Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); 322 TestDiagnosticConsumer Consumer; 323 Tool.setDiagnosticConsumer(&Consumer); 324 std::vector<ASTUnit*> ASTs; 325 Tool.buildASTs(ASTs); 326 EXPECT_EQ(1u, ASTs.size()); 327 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 328 } 329 #endif 330 331 } // end namespace tooling 332 } // end namespace clang 333