#include #include #include namespace clice { static void adjustInvocation(clang::CompilerInvocation& invocation) { auto& frontOpts = invocation.getFrontendOpts(); frontOpts.DisableFree = false; clang::LangOptions& langOpts = invocation.getLangOpts(); langOpts.CommentOpts.ParseAllComments = true; langOpts.RetainCommentsFromSystemHeaders = true; // FIXME: add more. } Compiler::Compiler(llvm::StringRef filepath, llvm::StringRef content, llvm::ArrayRef args, clang::DiagnosticConsumer* consumer, llvm::IntrusiveRefCntPtr vfs) : filepath(filepath), content(content) { // FIXME: figure out should we use createInvocation? clang::CreateInvocationOptions options; auto invocation = clang::createInvocation(args, options); /// FIXME: use a thread safe for every thread. instance = std::make_unique( std::make_shared()); adjustInvocation(*invocation); instance->setInvocation(std::move(invocation)); // FIXME: customize DiagnosticConsumer if(consumer) { instance->createDiagnostics(*vfs, consumer, true); } else { instance->createDiagnostics( *vfs, new clang::TextDiagnosticPrinter(llvm::outs(), new clang::DiagnosticOptions()), true); } } bool Compiler::applyPCH(llvm::StringRef filepath, std::uint32_t bound, bool endAtStart) { // FIXME: check reuseable? auto& preproc = instance->getPreprocessorOpts(); preproc.UsePredefines = false; preproc.ImplicitPCHInclude = filepath; preproc.PrecompiledPreambleBytes.first = bound; preproc.PrecompiledPreambleBytes.second = endAtStart; preproc.DisablePCHOrModuleValidation = clang::DisableValidationForModuleKind::PCH; return true; } bool Compiler::applyPCM(llvm::StringRef filepath, llvm::StringRef name) { // FIXME: check reuseable? instance->getHeaderSearchOpts().PrebuiltModuleFiles.try_emplace(name.str(), filepath); return true; } void Compiler::buildAST() { action = std::make_unique(); instance->getFrontendOpts().DisableFree = false; ExecuteAction(); m_Resolver = std::make_unique(instance->getSema()); } void Compiler::generatePCH(llvm::StringRef outpath, std::uint32_t bound, bool endAtStart) { content = content.substr(0, bound); instance->getFrontendOpts().OutputFile = outpath; instance->getFrontendOpts().ProgramAction = clang::frontend::GeneratePCH; instance->getPreprocessorOpts().PrecompiledPreambleBytes = {0, false}; instance->getPreprocessorOpts().GeneratePreamble = true; instance->getLangOpts().CompilingPCH = true; action = std::make_unique(); ExecuteAction(); } void Compiler::generatePCM(llvm::StringRef outpath) { instance->getFrontendOpts().OutputFile = outpath; action = std::make_unique(); ExecuteAction(); } void Compiler::codeCompletion(llvm::StringRef filepath, std::uint32_t line, std::uint32_t column, clang::CodeCompleteConsumer* consumer) { auto& location = instance->getFrontendOpts().CodeCompletionAt; location.FileName = filepath; location.Line = line; location.Column = column; auto buffer = llvm::MemoryBuffer::getMemBufferCopy(content); instance->getPreprocessorOpts().addRemappedFile(filepath, buffer.release()); instance->setCodeCompletionConsumer(consumer); action = std::make_unique(); if(!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { llvm::errs() << "Failed to begin source file\n"; std::terminate(); } /// instance->getASTContext().setExternalSource(nullptr); if(auto error = action->Execute()) { llvm::errs() << "Failed to execute action: " << error << "\n"; std::terminate(); } } void Compiler::ExecuteAction() { { auto buffer = llvm::MemoryBuffer::getMemBufferCopy(content); instance->getPreprocessorOpts().addRemappedFile(filepath, buffer.release()); } if(auto VFSWithRemapping = createVFSFromCompilerInvocation(instance->getInvocation(), instance->getDiagnostics(), llvm::vfs::getRealFileSystem())) { instance->createFileManager(VFSWithRemapping); } if(!instance->createTarget()) { llvm::errs() << "Failed to create target\n"; std::terminate(); } if(!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { llvm::errs() << "Failed to begin source file\n"; std::terminate(); } auto& preproc = instance->getPreprocessor(); // FIXME: add PPCallbacks to collect information. // Beacuse CompilerInstance may create new Preprocessor in `BeginSourceFile`, // So we must need to create TokenCollector here. clang::syntax::TokenCollector collector{preproc}; // FIXME: clang-tidy, include-fixer, etc? if(auto error = action->Execute()) { llvm::errs() << "Failed to execute action: " << error << "\n"; std::terminate(); } // Build TokenBuffer and index expanded tokens for improving performance. buffer = std::make_unique(std::move(collector).consume()); buffer->indexExpandedTokens(); } Compiler::~Compiler() { if(action) { action->EndSourceFile(); } } static auto createInstance(llvm::ArrayRef args) { auto instance = std::make_unique(); /// TODO: Figure out `CreateInvocationOptions`. clang::CreateInvocationOptions options = {}; instance->setInvocation(clang::createInvocation(args, options)); /// TODO: use a thread safe filesystem and our customized `DiagnosticConsumer`. instance->createDiagnostics( *llvm::vfs::getRealFileSystem(), new clang::TextDiagnosticPrinter(llvm::outs(), new clang::DiagnosticOptions()), true); adjustInvocation(instance->getInvocation()); return instance; } static llvm::Expected ExecuteAction(std::unique_ptr instance, clang::frontend::ActionKind kind) { std::unique_ptr action; if(kind == clang::frontend::ActionKind::ParseSyntaxOnly) { action = std::make_unique(); } else if(kind == clang::frontend::ActionKind::GeneratePCH) { action = std::make_unique(); } else if(kind == clang::frontend::ActionKind::GenerateReducedModuleInterface) { action = std::make_unique(); } else { llvm::errs() << "Unsupported action kind\n"; std::terminate(); } if(!instance->createTarget()) { return error("Failed to create target"); } if(!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { return error("Failed to begin source file"); } // FIXME: clang-tidy, include-fixer, etc? // `BeginSourceFile` may create new preprocessor, so all operations related to preprocessor // should be done after `BeginSourceFile`. auto& PP = instance->getPreprocessor(); clang::syntax::TokenCollector collector{PP}; if(auto error = action->Execute()) { return clice::error("Failed to execute action, because {} ", error); } auto tokBuf = std::make_unique(std::move(collector).consume()); tokBuf->indexExpandedTokens(); return ASTInfo(std::move(action), std::move(instance), std::move(tokBuf)); } llvm::Expected buildAST(llvm::StringRef path, llvm::StringRef content, llvm::ArrayRef args, Preamble* preamble) { auto instance = createInstance(args); auto& PPOpts = instance->getPreprocessorOpts(); auto buffer = llvm::MemoryBuffer::getMemBufferCopy(content); /// FIXME: Check PPOpts.RetainRemappedFileBuffers. PPOpts.addRemappedFile(path, buffer.release()); if(preamble) { auto& [pch, bounds, pcms] = *preamble; if(bounds.Size != 0) { PPOpts.UsePredefines = false; PPOpts.ImplicitPCHInclude = std::move(pch); PPOpts.PrecompiledPreambleBytes.first = bounds.Size; PPOpts.PrecompiledPreambleBytes.second = bounds.PreambleEndsAtStartOfLine; PPOpts.DisablePCHOrModuleValidation = clang::DisableValidationForModuleKind::PCH; } for(auto& [name, path]: pcms) { auto& HSOpts = instance->getHeaderSearchOpts(); HSOpts.PrebuiltModuleFiles.try_emplace(std::move(name), std::move(path)); } } return ExecuteAction(std::move(instance), clang::frontend::ActionKind::ParseSyntaxOnly); } llvm::Expected buildPCH(llvm::StringRef path, llvm::StringRef content, llvm::StringRef outpath, llvm::StringRef mainpath, std::size_t index, llvm::ArrayRef args) { auto instance = createInstance(args); clang::PreambleBounds bounds = {0, false}; if(mainpath == path) { /// If mainpath is equal to path, just tokenize the content to get preamble bounds. bounds = clang::Lexer::ComputePreamble(content, {}, false); } else { /// FIXME: if the mainpath is not equal to path, we need to preprocess the mainpath to get /// the preamble bounds. std::terminate(); } instance->getFrontendOpts().OutputFile = outpath; instance->getFrontendOpts().ProgramAction = clang::frontend::GeneratePCH; instance->getPreprocessorOpts().PrecompiledPreambleBytes = {0, false}; instance->getPreprocessorOpts().GeneratePreamble = true; instance->getLangOpts().CompilingPCH = true; auto buffer = llvm::MemoryBuffer::getMemBufferCopy(content.substr(0, bounds.Size)); instance->getPreprocessorOpts().addRemappedFile(path, buffer.release()); if(auto info = ExecuteAction(std::move(instance), clang::frontend::ActionKind::GeneratePCH)) { return PCHInfo(std::move(*info), outpath, mainpath, content, bounds); } else { return info.takeError(); } } llvm::Expected buildPCH(llvm::StringRef path, llvm::StringRef content, llvm::StringRef outpath, llvm::ArrayRef args) { return buildPCH(path, content, outpath, path, 0, args); } } // namespace clice