165 lines · cpp
1//===--- Compiler.cpp --------------------------------------------*- C++-*-===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "Compiler.h"10#include "support/Logger.h"11#include "clang/Basic/TargetInfo.h"12#include "clang/Driver/CreateInvocationFromArgs.h"13#include "clang/Frontend/CompilerInvocation.h"14#include "clang/Lex/PreprocessorOptions.h"15#include "clang/Serialization/PCHContainerOperations.h"16#include "llvm/ADT/StringRef.h"17 18namespace clang {19namespace clangd {20 21void IgnoreDiagnostics::log(DiagnosticsEngine::Level DiagLevel,22 const clang::Diagnostic &Info) {23 // FIXME: format lazily, in case vlog is off.24 llvm::SmallString<64> Message;25 Info.FormatDiagnostic(Message);26 27 llvm::SmallString<64> Location;28 if (Info.hasSourceManager() && Info.getLocation().isValid()) {29 auto &SourceMgr = Info.getSourceManager();30 auto Loc = SourceMgr.getFileLoc(Info.getLocation());31 llvm::raw_svector_ostream OS(Location);32 Loc.print(OS, SourceMgr);33 OS << ":";34 }35 36 clangd::vlog("Ignored diagnostic. {0}{1}", Location, Message);37}38 39void IgnoreDiagnostics::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,40 const clang::Diagnostic &Info) {41 IgnoreDiagnostics::log(DiagLevel, Info);42}43 44static bool AllowCrashPragmasForTest = false;45void allowCrashPragmasForTest() { AllowCrashPragmasForTest = true; }46 47void disableUnsupportedOptions(CompilerInvocation &CI) {48 // Disable "clang -verify" diagnostics, they are rarely useful in clangd, and49 // our compiler invocation set-up doesn't seem to work with it (leading50 // assertions in VerifyDiagnosticConsumer).51 CI.getDiagnosticOpts().VerifyDiagnostics = false;52 CI.getDiagnosticOpts().ShowColors = false;53 54 // Disable any dependency outputting, we don't want to generate files or write55 // to stdout/stderr.56 CI.getDependencyOutputOpts().ShowIncludesDest = ShowIncludesDestination::None;57 CI.getDependencyOutputOpts().OutputFile.clear();58 CI.getDependencyOutputOpts().HeaderIncludeOutputFile.clear();59 CI.getDependencyOutputOpts().DOTOutputFile.clear();60 CI.getDependencyOutputOpts().ModuleDependencyOutputDir.clear();61 62 // Disable any pch generation/usage operations. Since serialized preamble63 // format is unstable, using an incompatible one might result in unexpected64 // behaviours, including crashes.65 CI.getPreprocessorOpts().ImplicitPCHInclude.clear();66 CI.getPreprocessorOpts().PrecompiledPreambleBytes = {0, false};67 CI.getPreprocessorOpts().PCHThroughHeader.clear();68 CI.getPreprocessorOpts().PCHWithHdrStop = false;69 CI.getPreprocessorOpts().PCHWithHdrStopCreate = false;70 // Don't crash on `#pragma clang __debug parser_crash`71 if (!AllowCrashPragmasForTest)72 CI.getPreprocessorOpts().DisablePragmaDebugCrash = true;73 74 // Always default to raw container format as clangd doesn't registry any other75 // and clang dies when faced with unknown formats.76 CI.getHeaderSearchOpts().ModuleFormat =77 PCHContainerOperations().getRawReader().getFormats().front().str();78 79 CI.getFrontendOpts().Plugins.clear();80 CI.getFrontendOpts().AddPluginActions.clear();81 CI.getFrontendOpts().PluginArgs.clear();82 CI.getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;83 CI.getFrontendOpts().ActionName.clear();84 85 // These options mostly affect codegen, and aren't relevant to clangd. And86 // clang will die immediately when these files are not existed.87 // Disable these uninteresting options to make clangd more robust.88 CI.getLangOpts().NoSanitizeFiles.clear();89 CI.getLangOpts().XRayAttrListFiles.clear();90 CI.getLangOpts().ProfileListFiles.clear();91 CI.getLangOpts().XRayAlwaysInstrumentFiles.clear();92 CI.getLangOpts().XRayNeverInstrumentFiles.clear();93}94 95std::unique_ptr<CompilerInvocation>96buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D,97 std::vector<std::string> *CC1Args) {98 llvm::ArrayRef<std::string> Argv = Inputs.CompileCommand.CommandLine;99 if (Argv.empty())100 return nullptr;101 std::vector<const char *> ArgStrs;102 ArgStrs.reserve(Argv.size() + 1);103 // In asserts builds, CompilerInvocation redundantly reads/parses cc1 args as104 // a sanity test. This is not useful to clangd, and costs 10% of test time.105 // To avoid mismatches between assert/production builds, disable it always.106 ArgStrs = {Argv.front().c_str(), "-Xclang", "-no-round-trip-args"};107 for (const auto &S : Argv.drop_front())108 ArgStrs.push_back(S.c_str());109 110 CreateInvocationOptions CIOpts;111 CIOpts.VFS = Inputs.TFS->view(Inputs.CompileCommand.Directory);112 CIOpts.CC1Args = CC1Args;113 CIOpts.RecoverOnError = true;114 DiagnosticOptions DiagOpts;115 CIOpts.Diags =116 CompilerInstance::createDiagnostics(*CIOpts.VFS, DiagOpts, &D, false);117 CIOpts.ProbePrecompiled = false;118 std::unique_ptr<CompilerInvocation> CI = createInvocation(ArgStrs, CIOpts);119 if (!CI)120 return nullptr;121 // createInvocationFromCommandLine sets DisableFree.122 CI->getFrontendOpts().DisableFree = false;123 CI->getLangOpts().CommentOpts.ParseAllComments = true;124 CI->getLangOpts().RetainCommentsFromSystemHeaders = true;125 126 disableUnsupportedOptions(*CI);127 return CI;128}129 130std::unique_ptr<CompilerInstance>131prepareCompilerInstance(std::unique_ptr<clang::CompilerInvocation> CI,132 const PrecompiledPreamble *Preamble,133 std::unique_ptr<llvm::MemoryBuffer> Buffer,134 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,135 DiagnosticConsumer &DiagsClient) {136 assert(VFS && "VFS is null");137 assert(!CI->getPreprocessorOpts().RetainRemappedFileBuffers &&138 "Setting RetainRemappedFileBuffers to true will cause a memory leak "139 "of ContentsBuffer");140 141 // NOTE: we use Buffer.get() when adding remapped files, so we have to make142 // sure it will be released if no error is emitted.143 if (Preamble) {144 Preamble->OverridePreamble(*CI, VFS, Buffer.get());145 } else {146 CI->getPreprocessorOpts().addRemappedFile(147 CI->getFrontendOpts().Inputs[0].getFile(), Buffer.get());148 }149 150 auto Clang = std::make_unique<CompilerInstance>(std::move(CI));151 Clang->createVirtualFileSystem(VFS, &DiagsClient);152 Clang->createDiagnostics(&DiagsClient, false);153 Clang->createFileManager();154 if (!Clang->createTarget())155 return nullptr;156 157 // RemappedFileBuffers will handle the lifetime of the Buffer pointer,158 // release it.159 Buffer.release();160 return Clang;161}162 163} // namespace clangd164} // namespace clang165