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