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