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