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