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 24namespace clang { 25namespace tooling { 26 27namespace { 28/// Takes an ast consumer and returns it from CreateASTConsumer. This only 29/// works with single translation unit compilations. 30class 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 47class 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 60TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) { 61 bool FoundTopLevelDecl = false; 62 EXPECT_TRUE(runToolOnCode( 63 new TestAction(new FindTopLevelDeclConsumer(&FoundTopLevelDecl)), "")); 64 EXPECT_FALSE(FoundTopLevelDecl); 65} 66 67namespace { 68class 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}; 84bool 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 98TEST(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 110TEST(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 = buildASTFromCode("class Y;"); 116 ASSERT_TRUE(AST.get()); 117 EXPECT_FALSE(FindClassDeclX(AST.get())); 118} 119 120TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) { 121 std::unique_ptr<FrontendActionFactory> Factory( 122 newFrontendActionFactory<SyntaxOnlyAction>()); 123 std::unique_ptr<FrontendAction> Action(Factory->create()); 124 EXPECT_TRUE(Action.get() != nullptr); 125} 126 127struct IndependentFrontendActionCreator { 128 ASTConsumer *newASTConsumer() { 129 return new FindTopLevelDeclConsumer(nullptr); 130 } 131}; 132 133TEST(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() != nullptr); 139} 140 141TEST(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.get()); 151 Invocation.mapVirtualFile("test.cpp", "#include <abc>\n"); 152 Invocation.mapVirtualFile("def/abc", "\n"); 153 EXPECT_TRUE(Invocation.run()); 154} 155 156TEST(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.get()); 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 178struct 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) 197TEST(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 std::unique_ptr<FrontendActionFactory> Action( 210 newFrontendActionFactory(&EndCallback, &EndCallback)); 211 Tool.run(Action.get()); 212 213 EXPECT_TRUE(EndCallback.Matched); 214 EXPECT_EQ(2u, EndCallback.BeginCalled); 215 EXPECT_EQ(2u, EndCallback.EndCalled); 216} 217#endif 218 219struct SkipBodyConsumer : public clang::ASTConsumer { 220 /// Skip the 'skipMe' function. 221 virtual bool shouldSkipFunctionBody(Decl *D) { 222 FunctionDecl *F = dyn_cast<FunctionDecl>(D); 223 return F && F->getNameAsString() == "skipMe"; 224 } 225}; 226 227struct SkipBodyAction : public clang::ASTFrontendAction { 228 virtual ASTConsumer *CreateASTConsumer(CompilerInstance &Compiler, 229 StringRef) { 230 Compiler.getFrontendOpts().SkipFunctionBodies = true; 231 return new SkipBodyConsumer; 232 } 233}; 234 235TEST(runToolOnCode, TestSkipFunctionBody) { 236 EXPECT_TRUE(runToolOnCode(new SkipBodyAction, 237 "int skipMe() { an_error_here }")); 238 EXPECT_FALSE(runToolOnCode(new SkipBodyAction, 239 "int skipMeNot() { an_error_here }")); 240} 241 242TEST(runToolOnCodeWithArgs, TestNoDepFile) { 243 llvm::SmallString<32> DepFilePath; 244 ASSERT_FALSE( 245 llvm::sys::fs::createTemporaryFile("depfile", "d", DepFilePath)); 246 std::vector<std::string> Args; 247 Args.push_back("-MMD"); 248 Args.push_back("-MT"); 249 Args.push_back(DepFilePath.str()); 250 Args.push_back("-MF"); 251 Args.push_back(DepFilePath.str()); 252 EXPECT_TRUE(runToolOnCodeWithArgs(new SkipBodyAction, "", Args)); 253 EXPECT_FALSE(llvm::sys::fs::exists(DepFilePath.str())); 254 EXPECT_FALSE(llvm::sys::fs::remove(DepFilePath.str())); 255} 256 257struct CheckSyntaxOnlyAdjuster: public ArgumentsAdjuster { 258 bool &Found; 259 bool &Ran; 260 261 CheckSyntaxOnlyAdjuster(bool &Found, bool &Ran) : Found(Found), Ran(Ran) { } 262 263 virtual CommandLineArguments 264 Adjust(const CommandLineArguments &Args) override { 265 Ran = true; 266 for (unsigned I = 0, E = Args.size(); I != E; ++I) { 267 if (Args[I] == "-fsyntax-only") { 268 Found = true; 269 break; 270 } 271 } 272 return Args; 273 } 274}; 275 276TEST(ClangToolTest, ArgumentAdjusters) { 277 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 278 279 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 280 Tool.mapVirtualFile("/a.cc", "void a() {}"); 281 282 std::unique_ptr<FrontendActionFactory> Action( 283 newFrontendActionFactory<SyntaxOnlyAction>()); 284 285 bool Found = false; 286 bool Ran = false; 287 Tool.appendArgumentsAdjuster(new CheckSyntaxOnlyAdjuster(Found, Ran)); 288 Tool.run(Action.get()); 289 EXPECT_TRUE(Ran); 290 EXPECT_TRUE(Found); 291 292 Ran = Found = false; 293 Tool.clearArgumentsAdjusters(); 294 Tool.appendArgumentsAdjuster(new CheckSyntaxOnlyAdjuster(Found, Ran)); 295 Tool.appendArgumentsAdjuster(new ClangSyntaxOnlyAdjuster()); 296 Tool.run(Action.get()); 297 EXPECT_TRUE(Ran); 298 EXPECT_FALSE(Found); 299} 300 301#ifndef LLVM_ON_WIN32 302TEST(ClangToolTest, BuildASTs) { 303 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 304 305 std::vector<std::string> Sources; 306 Sources.push_back("/a.cc"); 307 Sources.push_back("/b.cc"); 308 ClangTool Tool(Compilations, Sources); 309 310 Tool.mapVirtualFile("/a.cc", "void a() {}"); 311 Tool.mapVirtualFile("/b.cc", "void b() {}"); 312 313 std::vector<std::unique_ptr<ASTUnit>> ASTs; 314 EXPECT_EQ(0, Tool.buildASTs(ASTs)); 315 EXPECT_EQ(2u, ASTs.size()); 316} 317 318struct TestDiagnosticConsumer : public DiagnosticConsumer { 319 TestDiagnosticConsumer() : NumDiagnosticsSeen(0) {} 320 virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 321 const Diagnostic &Info) { 322 ++NumDiagnosticsSeen; 323 } 324 unsigned NumDiagnosticsSeen; 325}; 326 327TEST(ClangToolTest, InjectDiagnosticConsumer) { 328 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 329 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 330 Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); 331 TestDiagnosticConsumer Consumer; 332 Tool.setDiagnosticConsumer(&Consumer); 333 std::unique_ptr<FrontendActionFactory> Action( 334 newFrontendActionFactory<SyntaxOnlyAction>()); 335 Tool.run(Action.get()); 336 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 337} 338 339TEST(ClangToolTest, InjectDiagnosticConsumerInBuildASTs) { 340 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 341 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 342 Tool.mapVirtualFile("/a.cc", "int x = undeclared;"); 343 TestDiagnosticConsumer Consumer; 344 Tool.setDiagnosticConsumer(&Consumer); 345 std::vector<std::unique_ptr<ASTUnit>> ASTs; 346 Tool.buildASTs(ASTs); 347 EXPECT_EQ(1u, ASTs.size()); 348 EXPECT_EQ(1u, Consumer.NumDiagnosticsSeen); 349} 350#endif 351 352} // end namespace tooling 353} // end namespace clang 354