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