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/CompilerInstance.h" 14 #include "clang/Frontend/FrontendAction.h" 15 #include "clang/Frontend/FrontendActions.h" 16 #include "clang/Tooling/CompilationDatabase.h" 17 #include "clang/Tooling/Tooling.h" 18 #include "gtest/gtest.h" 19 #include <string> 20 21 namespace clang { 22 namespace tooling { 23 24 namespace { 25 /// Takes an ast consumer and returns it from CreateASTConsumer. This only 26 /// works with single translation unit compilations. 27 class TestAction : public clang::ASTFrontendAction { 28 public: 29 /// Takes ownership of TestConsumer. 30 explicit TestAction(clang::ASTConsumer *TestConsumer) 31 : TestConsumer(TestConsumer) {} 32 33 protected: 34 virtual clang::ASTConsumer* CreateASTConsumer( 35 clang::CompilerInstance& compiler, StringRef dummy) { 36 /// TestConsumer will be deleted by the framework calling us. 37 return TestConsumer; 38 } 39 40 private: 41 clang::ASTConsumer * const TestConsumer; 42 }; 43 44 class FindTopLevelDeclConsumer : public clang::ASTConsumer { 45 public: 46 explicit FindTopLevelDeclConsumer(bool *FoundTopLevelDecl) 47 : FoundTopLevelDecl(FoundTopLevelDecl) {} 48 virtual bool HandleTopLevelDecl(clang::DeclGroupRef DeclGroup) { 49 *FoundTopLevelDecl = true; 50 return true; 51 } 52 private: 53 bool * const FoundTopLevelDecl; 54 }; 55 } // end namespace 56 57 TEST(runToolOnCode, FindsNoTopLevelDeclOnEmptyCode) { 58 bool FoundTopLevelDecl = false; 59 EXPECT_TRUE(runToolOnCode( 60 new TestAction(new FindTopLevelDeclConsumer(&FoundTopLevelDecl)), "")); 61 #if !defined(_MSC_VER) 62 EXPECT_FALSE(FoundTopLevelDecl); 63 #else 64 // FIXME: LangOpts.MicrosoftExt appends "class type_info;" 65 EXPECT_TRUE(FoundTopLevelDecl); 66 #endif 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 } // end namespace 87 88 TEST(runToolOnCode, FindsClassDecl) { 89 bool FoundClassDeclX = false; 90 EXPECT_TRUE(runToolOnCode(new TestAction( 91 new FindClassDeclXConsumer(&FoundClassDeclX)), "class X;")); 92 EXPECT_TRUE(FoundClassDeclX); 93 94 FoundClassDeclX = false; 95 EXPECT_TRUE(runToolOnCode(new TestAction( 96 new FindClassDeclXConsumer(&FoundClassDeclX)), "class Y;")); 97 EXPECT_FALSE(FoundClassDeclX); 98 } 99 100 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromType) { 101 OwningPtr<FrontendActionFactory> Factory( 102 newFrontendActionFactory<SyntaxOnlyAction>()); 103 OwningPtr<FrontendAction> Action(Factory->create()); 104 EXPECT_TRUE(Action.get() != NULL); 105 } 106 107 struct IndependentFrontendActionCreator { 108 ASTConsumer *newASTConsumer() { 109 return new FindTopLevelDeclConsumer(NULL); 110 } 111 }; 112 113 TEST(newFrontendActionFactory, CreatesFrontendActionFactoryFromFactoryType) { 114 IndependentFrontendActionCreator Creator; 115 OwningPtr<FrontendActionFactory> Factory( 116 newFrontendActionFactory(&Creator)); 117 OwningPtr<FrontendAction> Action(Factory->create()); 118 EXPECT_TRUE(Action.get() != NULL); 119 } 120 121 TEST(ToolInvocation, TestMapVirtualFile) { 122 clang::FileManager Files((clang::FileSystemOptions())); 123 std::vector<std::string> Args; 124 Args.push_back("tool-executable"); 125 Args.push_back("-Idef"); 126 Args.push_back("-fsyntax-only"); 127 Args.push_back("test.cpp"); 128 clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, &Files); 129 Invocation.mapVirtualFile("test.cpp", "#include <abc>\n"); 130 Invocation.mapVirtualFile("def/abc", "\n"); 131 EXPECT_TRUE(Invocation.run()); 132 } 133 134 TEST(ToolInvocation, TestVirtualModulesCompilation) { 135 // FIXME: Currently, this only tests that we don't exit with an error if a 136 // mapped module.map is found on the include path. In the future, expand this 137 // test to run a full modules enabled compilation, so we make sure we can 138 // rerun modules compilations with a virtual file system. 139 clang::FileManager Files((clang::FileSystemOptions())); 140 std::vector<std::string> Args; 141 Args.push_back("tool-executable"); 142 Args.push_back("-Idef"); 143 Args.push_back("-fsyntax-only"); 144 Args.push_back("test.cpp"); 145 clang::tooling::ToolInvocation Invocation(Args, new SyntaxOnlyAction, &Files); 146 Invocation.mapVirtualFile("test.cpp", "#include <abc>\n"); 147 Invocation.mapVirtualFile("def/abc", "\n"); 148 // Add a module.map file in the include directory of our header, so we trigger 149 // the module.map header search logic. 150 Invocation.mapVirtualFile("def/module.map", "\n"); 151 EXPECT_TRUE(Invocation.run()); 152 } 153 154 struct VerifyEndCallback : public SourceFileCallbacks { 155 VerifyEndCallback() : BeginCalled(0), EndCalled(0), Matched(false) {} 156 virtual bool handleBeginSource(CompilerInstance &CI, 157 StringRef Filename) LLVM_OVERRIDE { 158 ++BeginCalled; 159 return true; 160 } 161 virtual void handleEndSource() { 162 ++EndCalled; 163 } 164 ASTConsumer *newASTConsumer() { 165 return new FindTopLevelDeclConsumer(&Matched); 166 } 167 unsigned BeginCalled; 168 unsigned EndCalled; 169 bool Matched; 170 }; 171 172 #if !defined(_WIN32) 173 TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) { 174 VerifyEndCallback EndCallback; 175 176 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 177 std::vector<std::string> Sources; 178 Sources.push_back("/a.cc"); 179 Sources.push_back("/b.cc"); 180 ClangTool Tool(Compilations, Sources); 181 182 Tool.mapVirtualFile("/a.cc", "void a() {}"); 183 Tool.mapVirtualFile("/b.cc", "void b() {}"); 184 185 Tool.run(newFrontendActionFactory(&EndCallback, &EndCallback)); 186 187 EXPECT_TRUE(EndCallback.Matched); 188 EXPECT_EQ(2u, EndCallback.BeginCalled); 189 EXPECT_EQ(2u, EndCallback.EndCalled); 190 } 191 #endif 192 193 struct SkipBodyConsumer : public clang::ASTConsumer { 194 /// Skip the 'skipMe' function. 195 virtual bool shouldSkipFunctionBody(Decl *D) { 196 FunctionDecl *F = dyn_cast<FunctionDecl>(D); 197 return F && F->getNameAsString() == "skipMe"; 198 } 199 }; 200 201 struct SkipBodyAction : public clang::ASTFrontendAction { 202 virtual ASTConsumer *CreateASTConsumer(CompilerInstance &Compiler, 203 StringRef) { 204 Compiler.getFrontendOpts().SkipFunctionBodies = true; 205 return new SkipBodyConsumer; 206 } 207 }; 208 209 TEST(runToolOnCode, TestSkipFunctionBody) { 210 EXPECT_TRUE(runToolOnCode(new SkipBodyAction, 211 "int skipMe() { an_error_here }")); 212 EXPECT_FALSE(runToolOnCode(new SkipBodyAction, 213 "int skipMeNot() { an_error_here }")); 214 } 215 216 struct CheckSyntaxOnlyAdjuster: public ArgumentsAdjuster { 217 bool &Found; 218 bool &Ran; 219 220 CheckSyntaxOnlyAdjuster(bool &Found, bool &Ran) : Found(Found), Ran(Ran) { } 221 222 virtual CommandLineArguments 223 Adjust(const CommandLineArguments &Args) LLVM_OVERRIDE { 224 Ran = true; 225 for (unsigned I = 0, E = Args.size(); I != E; ++I) { 226 if (Args[I] == "-fsyntax-only") { 227 Found = true; 228 break; 229 } 230 } 231 return Args; 232 } 233 }; 234 235 TEST(ClangToolTest, ArgumentAdjusters) { 236 FixedCompilationDatabase Compilations("/", std::vector<std::string>()); 237 238 ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); 239 Tool.mapVirtualFile("/a.cc", "void a() {}"); 240 241 bool Found = false; 242 bool Ran = false; 243 Tool.appendArgumentsAdjuster(new CheckSyntaxOnlyAdjuster(Found, Ran)); 244 Tool.run(newFrontendActionFactory<SyntaxOnlyAction>()); 245 EXPECT_TRUE(Ran); 246 EXPECT_TRUE(Found); 247 248 Ran = Found = false; 249 Tool.clearArgumentsAdjusters(); 250 Tool.appendArgumentsAdjuster(new CheckSyntaxOnlyAdjuster(Found, Ran)); 251 Tool.appendArgumentsAdjuster(new ClangSyntaxOnlyAdjuster()); 252 Tool.run(newFrontendActionFactory<SyntaxOnlyAction>()); 253 EXPECT_TRUE(Ran); 254 EXPECT_FALSE(Found); 255 } 256 257 } // end namespace tooling 258 } // end namespace clang 259