|  | //===--- Compiler.cpp --------------------------------------------*- C++-*-===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "Compiler.h" | 
|  | #include "support/Logger.h" | 
|  | #include "clang/Basic/TargetInfo.h" | 
|  | #include "clang/Frontend/CompilerInvocation.h" | 
|  | #include "clang/Lex/PreprocessorOptions.h" | 
|  | #include "clang/Serialization/PCHContainerOperations.h" | 
|  | #include "llvm/ADT/StringRef.h" | 
|  |  | 
|  | namespace clang { | 
|  | namespace clangd { | 
|  |  | 
|  | void IgnoreDiagnostics::log(DiagnosticsEngine::Level DiagLevel, | 
|  | const clang::Diagnostic &Info) { | 
|  | // FIXME: format lazily, in case vlog is off. | 
|  | llvm::SmallString<64> Message; | 
|  | Info.FormatDiagnostic(Message); | 
|  |  | 
|  | llvm::SmallString<64> Location; | 
|  | if (Info.hasSourceManager() && Info.getLocation().isValid()) { | 
|  | auto &SourceMgr = Info.getSourceManager(); | 
|  | auto Loc = SourceMgr.getFileLoc(Info.getLocation()); | 
|  | llvm::raw_svector_ostream OS(Location); | 
|  | Loc.print(OS, SourceMgr); | 
|  | OS << ":"; | 
|  | } | 
|  |  | 
|  | clangd::vlog("Ignored diagnostic. {0}{1}", Location, Message); | 
|  | } | 
|  |  | 
|  | void IgnoreDiagnostics::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, | 
|  | const clang::Diagnostic &Info) { | 
|  | IgnoreDiagnostics::log(DiagLevel, Info); | 
|  | } | 
|  |  | 
|  | static bool AllowCrashPragmasForTest = false; | 
|  | void allowCrashPragmasForTest() { AllowCrashPragmasForTest = true; } | 
|  |  | 
|  | void disableUnsupportedOptions(CompilerInvocation &CI) { | 
|  | // Disable "clang -verify" diagnostics, they are rarely useful in clangd, and | 
|  | // our compiler invocation set-up doesn't seem to work with it (leading | 
|  | // assertions in VerifyDiagnosticConsumer). | 
|  | CI.getDiagnosticOpts().VerifyDiagnostics = false; | 
|  | CI.getDiagnosticOpts().ShowColors = false; | 
|  |  | 
|  | // Disable any dependency outputting, we don't want to generate files or write | 
|  | // to stdout/stderr. | 
|  | CI.getDependencyOutputOpts().ShowIncludesDest = ShowIncludesDestination::None; | 
|  | CI.getDependencyOutputOpts().OutputFile.clear(); | 
|  | CI.getDependencyOutputOpts().HeaderIncludeOutputFile.clear(); | 
|  | CI.getDependencyOutputOpts().DOTOutputFile.clear(); | 
|  | CI.getDependencyOutputOpts().ModuleDependencyOutputDir.clear(); | 
|  |  | 
|  | // Disable any pch generation/usage operations. Since serialized preamble | 
|  | // format is unstable, using an incompatible one might result in unexpected | 
|  | // behaviours, including crashes. | 
|  | CI.getPreprocessorOpts().ImplicitPCHInclude.clear(); | 
|  | CI.getPreprocessorOpts().PrecompiledPreambleBytes = {0, false}; | 
|  | CI.getPreprocessorOpts().PCHThroughHeader.clear(); | 
|  | CI.getPreprocessorOpts().PCHWithHdrStop = false; | 
|  | CI.getPreprocessorOpts().PCHWithHdrStopCreate = false; | 
|  | // Don't crash on `#pragma clang __debug parser_crash` | 
|  | if (!AllowCrashPragmasForTest) | 
|  | CI.getPreprocessorOpts().DisablePragmaDebugCrash = true; | 
|  |  | 
|  | // Always default to raw container format as clangd doesn't registry any other | 
|  | // and clang dies when faced with unknown formats. | 
|  | CI.getHeaderSearchOpts().ModuleFormat = | 
|  | PCHContainerOperations().getRawReader().getFormats().front().str(); | 
|  |  | 
|  | CI.getFrontendOpts().Plugins.clear(); | 
|  | CI.getFrontendOpts().AddPluginActions.clear(); | 
|  | CI.getFrontendOpts().PluginArgs.clear(); | 
|  | CI.getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly; | 
|  | CI.getFrontendOpts().ActionName.clear(); | 
|  |  | 
|  | // These options mostly affect codegen, and aren't relevant to clangd. And | 
|  | // clang will die immediately when these files are not existed. | 
|  | // Disable these uninteresting options to make clangd more robust. | 
|  | CI.getLangOpts().NoSanitizeFiles.clear(); | 
|  | CI.getLangOpts().XRayAttrListFiles.clear(); | 
|  | CI.getLangOpts().ProfileListFiles.clear(); | 
|  | CI.getLangOpts().XRayAlwaysInstrumentFiles.clear(); | 
|  | CI.getLangOpts().XRayNeverInstrumentFiles.clear(); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<CompilerInvocation> | 
|  | buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, | 
|  | std::vector<std::string> *CC1Args) { | 
|  | llvm::ArrayRef<std::string> Argv = Inputs.CompileCommand.CommandLine; | 
|  | if (Argv.empty()) | 
|  | return nullptr; | 
|  | std::vector<const char *> ArgStrs; | 
|  | ArgStrs.reserve(Argv.size() + 1); | 
|  | // In asserts builds, CompilerInvocation redundantly reads/parses cc1 args as | 
|  | // a sanity test. This is not useful to clangd, and costs 10% of test time. | 
|  | // To avoid mismatches between assert/production builds, disable it always. | 
|  | ArgStrs = {Argv.front().c_str(), "-Xclang", "-no-round-trip-args"}; | 
|  | for (const auto &S : Argv.drop_front()) | 
|  | ArgStrs.push_back(S.c_str()); | 
|  |  | 
|  | CreateInvocationOptions CIOpts; | 
|  | CIOpts.VFS = Inputs.TFS->view(Inputs.CompileCommand.Directory); | 
|  | CIOpts.CC1Args = CC1Args; | 
|  | CIOpts.RecoverOnError = true; | 
|  | CIOpts.Diags = | 
|  | CompilerInstance::createDiagnostics(new DiagnosticOptions, &D, false); | 
|  | CIOpts.ProbePrecompiled = false; | 
|  | std::unique_ptr<CompilerInvocation> CI = createInvocation(ArgStrs, CIOpts); | 
|  | if (!CI) | 
|  | return nullptr; | 
|  | // createInvocationFromCommandLine sets DisableFree. | 
|  | CI->getFrontendOpts().DisableFree = false; | 
|  | CI->getLangOpts().CommentOpts.ParseAllComments = true; | 
|  | CI->getLangOpts().RetainCommentsFromSystemHeaders = true; | 
|  |  | 
|  | disableUnsupportedOptions(*CI); | 
|  | return CI; | 
|  | } | 
|  |  | 
|  | std::unique_ptr<CompilerInstance> | 
|  | prepareCompilerInstance(std::unique_ptr<clang::CompilerInvocation> CI, | 
|  | const PrecompiledPreamble *Preamble, | 
|  | std::unique_ptr<llvm::MemoryBuffer> Buffer, | 
|  | llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, | 
|  | DiagnosticConsumer &DiagsClient) { | 
|  | assert(VFS && "VFS is null"); | 
|  | assert(!CI->getPreprocessorOpts().RetainRemappedFileBuffers && | 
|  | "Setting RetainRemappedFileBuffers to true will cause a memory leak " | 
|  | "of ContentsBuffer"); | 
|  |  | 
|  | // NOTE: we use Buffer.get() when adding remapped files, so we have to make | 
|  | // sure it will be released if no error is emitted. | 
|  | if (Preamble) { | 
|  | Preamble->OverridePreamble(*CI, VFS, Buffer.get()); | 
|  | } else { | 
|  | CI->getPreprocessorOpts().addRemappedFile( | 
|  | CI->getFrontendOpts().Inputs[0].getFile(), Buffer.get()); | 
|  | } | 
|  |  | 
|  | auto Clang = std::make_unique<CompilerInstance>( | 
|  | std::make_shared<PCHContainerOperations>()); | 
|  | Clang->setInvocation(std::move(CI)); | 
|  | Clang->createDiagnostics(&DiagsClient, false); | 
|  |  | 
|  | if (auto VFSWithRemapping = createVFSFromCompilerInvocation( | 
|  | Clang->getInvocation(), Clang->getDiagnostics(), VFS)) | 
|  | VFS = VFSWithRemapping; | 
|  | Clang->createFileManager(VFS); | 
|  |  | 
|  | if (!Clang->createTarget()) | 
|  | return nullptr; | 
|  |  | 
|  | // RemappedFileBuffers will handle the lifetime of the Buffer pointer, | 
|  | // release it. | 
|  | Buffer.release(); | 
|  | return Clang; | 
|  | } | 
|  |  | 
|  | } // namespace clangd | 
|  | } // namespace clang |