1 //===-- clang-import-test.cpp - ASTImporter/ExternalASTSource testbed -----===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "clang/AST/ASTContext.h" 10 #include "clang/AST/ASTImporter.h" 11 #include "clang/AST/DeclObjC.h" 12 #include "clang/AST/ExternalASTMerger.h" 13 #include "clang/Basic/Builtins.h" 14 #include "clang/Basic/FileManager.h" 15 #include "clang/Basic/IdentifierTable.h" 16 #include "clang/Basic/SourceLocation.h" 17 #include "clang/Basic/TargetInfo.h" 18 #include "clang/Basic/TargetOptions.h" 19 #include "clang/CodeGen/ModuleBuilder.h" 20 #include "clang/Driver/Types.h" 21 #include "clang/Frontend/ASTConsumers.h" 22 #include "clang/Frontend/CompilerInstance.h" 23 #include "clang/Frontend/MultiplexConsumer.h" 24 #include "clang/Frontend/TextDiagnosticBuffer.h" 25 #include "clang/Lex/Lexer.h" 26 #include "clang/Lex/Preprocessor.h" 27 #include "clang/Parse/ParseAST.h" 28 29 #include "llvm/IR/LLVMContext.h" 30 #include "llvm/IR/Module.h" 31 #include "llvm/Support/CommandLine.h" 32 #include "llvm/Support/Error.h" 33 #include "llvm/Support/Signals.h" 34 #include "llvm/Support/VirtualFileSystem.h" 35 #include "llvm/TargetParser/Host.h" 36 37 #include <memory> 38 #include <string> 39 40 using namespace clang; 41 42 static llvm::cl::opt<std::string> Expression( 43 "expression", llvm::cl::Required, 44 llvm::cl::desc("Path to a file containing the expression to parse")); 45 46 static llvm::cl::list<std::string> 47 Imports("import", 48 llvm::cl::desc("Path to a file containing declarations to import")); 49 50 static llvm::cl::opt<bool> 51 Direct("direct", llvm::cl::Optional, 52 llvm::cl::desc("Use the parsed declarations without indirection")); 53 54 static llvm::cl::opt<bool> UseOrigins( 55 "use-origins", llvm::cl::Optional, 56 llvm::cl::desc( 57 "Use DeclContext origin information for more accurate lookups")); 58 59 static llvm::cl::list<std::string> 60 ClangArgs("Xcc", 61 llvm::cl::desc("Argument to pass to the CompilerInvocation"), 62 llvm::cl::CommaSeparated); 63 64 static llvm::cl::opt<std::string> 65 Input("x", llvm::cl::Optional, 66 llvm::cl::desc("The language to parse (default: c++)"), 67 llvm::cl::init("c++")); 68 69 static llvm::cl::opt<bool> ObjCARC("objc-arc", llvm::cl::init(false), 70 llvm::cl::desc("Emable ObjC ARC")); 71 72 static llvm::cl::opt<bool> DumpAST("dump-ast", llvm::cl::init(false), 73 llvm::cl::desc("Dump combined AST")); 74 75 static llvm::cl::opt<bool> DumpIR("dump-ir", llvm::cl::init(false), 76 llvm::cl::desc("Dump IR from final parse")); 77 78 namespace init_convenience { 79 class TestDiagnosticConsumer : public DiagnosticConsumer { 80 private: 81 std::unique_ptr<TextDiagnosticBuffer> Passthrough; 82 const LangOptions *LangOpts = nullptr; 83 84 public: 85 TestDiagnosticConsumer() 86 : Passthrough(std::make_unique<TextDiagnosticBuffer>()) {} 87 88 void BeginSourceFile(const LangOptions &LangOpts, 89 const Preprocessor *PP = nullptr) override { 90 this->LangOpts = &LangOpts; 91 return Passthrough->BeginSourceFile(LangOpts, PP); 92 } 93 94 void EndSourceFile() override { 95 this->LangOpts = nullptr; 96 Passthrough->EndSourceFile(); 97 } 98 99 bool IncludeInDiagnosticCounts() const override { 100 return Passthrough->IncludeInDiagnosticCounts(); 101 } 102 103 private: 104 static void PrintSourceForLocation(const SourceLocation &Loc, 105 SourceManager &SM) { 106 const char *LocData = SM.getCharacterData(Loc, /*Invalid=*/nullptr); 107 unsigned LocColumn = 108 SM.getSpellingColumnNumber(Loc, /*Invalid=*/nullptr) - 1; 109 FileID FID = SM.getFileID(Loc); 110 llvm::MemoryBufferRef Buffer = SM.getBufferOrFake(FID, Loc); 111 112 assert(LocData >= Buffer.getBufferStart() && 113 LocData < Buffer.getBufferEnd()); 114 115 const char *LineBegin = LocData - LocColumn; 116 117 assert(LineBegin >= Buffer.getBufferStart()); 118 119 const char *LineEnd = nullptr; 120 121 for (LineEnd = LineBegin; *LineEnd != '\n' && *LineEnd != '\r' && 122 LineEnd < Buffer.getBufferEnd(); 123 ++LineEnd) 124 ; 125 126 llvm::StringRef LineString(LineBegin, LineEnd - LineBegin); 127 128 llvm::errs() << LineString << '\n'; 129 llvm::errs().indent(LocColumn); 130 llvm::errs() << '^'; 131 llvm::errs() << '\n'; 132 } 133 134 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 135 const Diagnostic &Info) override { 136 if (Info.hasSourceManager() && LangOpts) { 137 SourceManager &SM = Info.getSourceManager(); 138 139 if (Info.getLocation().isValid()) { 140 Info.getLocation().print(llvm::errs(), SM); 141 llvm::errs() << ": "; 142 } 143 144 SmallString<16> DiagText; 145 Info.FormatDiagnostic(DiagText); 146 llvm::errs() << DiagText << '\n'; 147 148 if (Info.getLocation().isValid()) { 149 PrintSourceForLocation(Info.getLocation(), SM); 150 } 151 152 for (const CharSourceRange &Range : Info.getRanges()) { 153 bool Invalid = true; 154 StringRef Ref = Lexer::getSourceText(Range, SM, *LangOpts, &Invalid); 155 if (!Invalid) { 156 llvm::errs() << Ref << '\n'; 157 } 158 } 159 } 160 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); 161 } 162 }; 163 164 std::unique_ptr<CompilerInstance> BuildCompilerInstance() { 165 auto Ins = std::make_unique<CompilerInstance>(); 166 auto DC = std::make_unique<TestDiagnosticConsumer>(); 167 const bool ShouldOwnClient = true; 168 Ins->createDiagnostics(*llvm::vfs::getRealFileSystem(), DC.release(), 169 ShouldOwnClient); 170 171 auto Inv = std::make_unique<CompilerInvocation>(); 172 173 std::vector<const char *> ClangArgv(ClangArgs.size()); 174 std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(), 175 [](const std::string &s) -> const char * { return s.data(); }); 176 CompilerInvocation::CreateFromArgs(*Inv, ClangArgv, Ins->getDiagnostics()); 177 178 { 179 using namespace driver::types; 180 ID Id = lookupTypeForTypeSpecifier(Input.c_str()); 181 assert(Id != TY_INVALID); 182 if (isCXX(Id)) { 183 Inv->getLangOpts().CPlusPlus = true; 184 Inv->getLangOpts().CPlusPlus11 = true; 185 Inv->getHeaderSearchOpts().UseLibcxx = true; 186 } 187 if (isObjC(Id)) { 188 Inv->getLangOpts().ObjC = 1; 189 } 190 } 191 Inv->getLangOpts().ObjCAutoRefCount = ObjCARC; 192 193 Inv->getLangOpts().Bool = true; 194 Inv->getLangOpts().WChar = true; 195 Inv->getLangOpts().Blocks = true; 196 Inv->getLangOpts().DebuggerSupport = true; 197 Inv->getLangOpts().SpellChecking = false; 198 Inv->getLangOpts().ThreadsafeStatics = false; 199 Inv->getLangOpts().AccessControl = false; 200 Inv->getLangOpts().DollarIdents = true; 201 Inv->getLangOpts().Exceptions = true; 202 Inv->getLangOpts().CXXExceptions = true; 203 // Needed for testing dynamic_cast. 204 Inv->getLangOpts().RTTI = true; 205 Inv->getCodeGenOpts().setDebugInfo(llvm::codegenoptions::FullDebugInfo); 206 Inv->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple(); 207 208 Ins->setInvocation(std::move(Inv)); 209 210 TargetInfo *TI = TargetInfo::CreateTargetInfo( 211 Ins->getDiagnostics(), Ins->getInvocation().TargetOpts); 212 Ins->setTarget(TI); 213 Ins->getTarget().adjust(Ins->getDiagnostics(), Ins->getLangOpts()); 214 Ins->createFileManager(); 215 Ins->createSourceManager(Ins->getFileManager()); 216 Ins->createPreprocessor(TU_Complete); 217 218 return Ins; 219 } 220 221 std::unique_ptr<ASTContext> 222 BuildASTContext(CompilerInstance &CI, SelectorTable &ST, Builtin::Context &BC) { 223 auto &PP = CI.getPreprocessor(); 224 auto AST = std::make_unique<ASTContext>( 225 CI.getLangOpts(), CI.getSourceManager(), 226 PP.getIdentifierTable(), ST, BC, PP.TUKind); 227 AST->InitBuiltinTypes(CI.getTarget()); 228 return AST; 229 } 230 231 std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI, 232 llvm::LLVMContext &LLVMCtx) { 233 StringRef ModuleName("$__module"); 234 return std::unique_ptr<CodeGenerator>(CreateLLVMCodeGen( 235 CI.getDiagnostics(), ModuleName, &CI.getVirtualFileSystem(), 236 CI.getHeaderSearchOpts(), CI.getPreprocessorOpts(), CI.getCodeGenOpts(), 237 LLVMCtx)); 238 } 239 } // namespace init_convenience 240 241 namespace { 242 243 /// A container for a CompilerInstance (possibly with an ExternalASTMerger 244 /// attached to its ASTContext). 245 /// 246 /// Provides an accessor for the DeclContext origins associated with the 247 /// ExternalASTMerger (or an empty list of origins if no ExternalASTMerger is 248 /// attached). 249 /// 250 /// This is the main unit of parsed source code maintained by clang-import-test. 251 struct CIAndOrigins { 252 using OriginMap = clang::ExternalASTMerger::OriginMap; 253 std::unique_ptr<CompilerInstance> CI; 254 255 ASTContext &getASTContext() { return CI->getASTContext(); } 256 FileManager &getFileManager() { return CI->getFileManager(); } 257 const OriginMap &getOriginMap() { 258 static const OriginMap EmptyOriginMap{}; 259 if (ExternalASTSource *Source = CI->getASTContext().getExternalSource()) 260 return static_cast<ExternalASTMerger *>(Source)->GetOrigins(); 261 return EmptyOriginMap; 262 } 263 DiagnosticConsumer &getDiagnosticClient() { 264 return CI->getDiagnosticClient(); 265 } 266 CompilerInstance &getCompilerInstance() { return *CI; } 267 }; 268 269 void AddExternalSource(CIAndOrigins &CI, 270 llvm::MutableArrayRef<CIAndOrigins> Imports) { 271 ExternalASTMerger::ImporterTarget Target( 272 {CI.getASTContext(), CI.getFileManager()}); 273 llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources; 274 for (CIAndOrigins &Import : Imports) 275 Sources.emplace_back(Import.getASTContext(), Import.getFileManager(), 276 Import.getOriginMap()); 277 auto ES = std::make_unique<ExternalASTMerger>(Target, Sources); 278 CI.getASTContext().setExternalSource(ES.release()); 279 CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage(); 280 } 281 282 CIAndOrigins BuildIndirect(CIAndOrigins &CI) { 283 CIAndOrigins IndirectCI{init_convenience::BuildCompilerInstance()}; 284 auto ST = std::make_unique<SelectorTable>(); 285 auto BC = std::make_unique<Builtin::Context>(); 286 std::unique_ptr<ASTContext> AST = init_convenience::BuildASTContext( 287 IndirectCI.getCompilerInstance(), *ST, *BC); 288 IndirectCI.getCompilerInstance().setASTContext(AST.release()); 289 AddExternalSource(IndirectCI, CI); 290 return IndirectCI; 291 } 292 293 llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI, 294 ASTConsumer &Consumer) { 295 SourceManager &SM = CI.getSourceManager(); 296 auto FE = CI.getFileManager().getFileRef(Path); 297 if (!FE) { 298 llvm::consumeError(FE.takeError()); 299 return llvm::make_error<llvm::StringError>( 300 llvm::Twine("No such file or directory: ", Path), std::error_code()); 301 } 302 SM.setMainFileID(SM.createFileID(*FE, SourceLocation(), SrcMgr::C_User)); 303 ParseAST(CI.getPreprocessor(), &Consumer, CI.getASTContext()); 304 return llvm::Error::success(); 305 } 306 307 llvm::Expected<CIAndOrigins> Parse(const std::string &Path, 308 llvm::MutableArrayRef<CIAndOrigins> Imports, 309 bool ShouldDumpAST, bool ShouldDumpIR) { 310 CIAndOrigins CI{init_convenience::BuildCompilerInstance()}; 311 auto ST = std::make_unique<SelectorTable>(); 312 auto BC = std::make_unique<Builtin::Context>(); 313 std::unique_ptr<ASTContext> AST = 314 init_convenience::BuildASTContext(CI.getCompilerInstance(), *ST, *BC); 315 CI.getCompilerInstance().setASTContext(AST.release()); 316 if (Imports.size()) 317 AddExternalSource(CI, Imports); 318 319 std::vector<std::unique_ptr<ASTConsumer>> ASTConsumers; 320 321 auto LLVMCtx = std::make_unique<llvm::LLVMContext>(); 322 ASTConsumers.push_back( 323 init_convenience::BuildCodeGen(CI.getCompilerInstance(), *LLVMCtx)); 324 auto &CG = *static_cast<CodeGenerator *>(ASTConsumers.back().get()); 325 326 if (ShouldDumpAST) 327 ASTConsumers.push_back(CreateASTDumper(nullptr /*Dump to stdout.*/, "", 328 true, false, false, false, 329 clang::ADOF_Default)); 330 331 CI.getDiagnosticClient().BeginSourceFile( 332 CI.getCompilerInstance().getLangOpts(), 333 &CI.getCompilerInstance().getPreprocessor()); 334 MultiplexConsumer Consumers(std::move(ASTConsumers)); 335 Consumers.Initialize(CI.getASTContext()); 336 337 if (llvm::Error PE = ParseSource(Path, CI.getCompilerInstance(), Consumers)) 338 return std::move(PE); 339 CI.getDiagnosticClient().EndSourceFile(); 340 if (ShouldDumpIR) 341 CG.GetModule()->print(llvm::outs(), nullptr); 342 if (CI.getDiagnosticClient().getNumErrors()) 343 return llvm::make_error<llvm::StringError>( 344 "Errors occurred while parsing the expression.", std::error_code()); 345 return std::move(CI); 346 } 347 348 void Forget(CIAndOrigins &CI, llvm::MutableArrayRef<CIAndOrigins> Imports) { 349 llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources; 350 for (CIAndOrigins &Import : Imports) 351 Sources.push_back({Import.getASTContext(), Import.getFileManager(), 352 Import.getOriginMap()}); 353 ExternalASTSource *Source = CI.CI->getASTContext().getExternalSource(); 354 auto *Merger = static_cast<ExternalASTMerger *>(Source); 355 Merger->RemoveSources(Sources); 356 } 357 358 } // end namespace 359 360 int main(int argc, const char **argv) { 361 const bool DisableCrashReporting = true; 362 llvm::sys::PrintStackTraceOnErrorSignal(argv[0], DisableCrashReporting); 363 llvm::cl::ParseCommandLineOptions(argc, argv); 364 std::vector<CIAndOrigins> ImportCIs; 365 for (auto I : Imports) { 366 llvm::Expected<CIAndOrigins> ImportCI = Parse(I, {}, false, false); 367 if (auto E = ImportCI.takeError()) { 368 llvm::errs() << "error: " << llvm::toString(std::move(E)) << "\n"; 369 exit(-1); 370 } 371 ImportCIs.push_back(std::move(*ImportCI)); 372 } 373 std::vector<CIAndOrigins> IndirectCIs; 374 if (!Direct || UseOrigins) { 375 for (auto &ImportCI : ImportCIs) { 376 CIAndOrigins IndirectCI = BuildIndirect(ImportCI); 377 IndirectCIs.push_back(std::move(IndirectCI)); 378 } 379 } 380 if (UseOrigins) 381 for (auto &ImportCI : ImportCIs) 382 IndirectCIs.push_back(std::move(ImportCI)); 383 llvm::Expected<CIAndOrigins> ExpressionCI = 384 Parse(Expression, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs, 385 DumpAST, DumpIR); 386 if (auto E = ExpressionCI.takeError()) { 387 llvm::errs() << "error: " << llvm::toString(std::move(E)) << "\n"; 388 exit(-1); 389 } 390 Forget(*ExpressionCI, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs); 391 return 0; 392 } 393