diff --git a/clang-tools-extra/clangd/CompileCommands.h b/clang-tools-extra/clangd/CompileCommands.h --- a/clang-tools-extra/clangd/CompileCommands.h +++ b/clang-tools-extra/clangd/CompileCommands.h @@ -26,10 +26,6 @@ // - forcing the use of clangd's builtin headers rather than clang's // - resolving argv0 as cc1 expects // - injecting -isysroot flags on mac as the system clang does -// FIXME: This is currently not used in all code paths that create invocations. -// Make use of these adjusters and buildCompilerInvocation in clangd-indexer as -// well. It should be possible to hook it up by overriding RunInvocation in -// FrontendActionFactory. struct CommandMangler { // Absolute path to clang. llvm::Optional ClangPath; diff --git a/clang-tools-extra/clangd/Compiler.h b/clang-tools-extra/clangd/Compiler.h --- a/clang-tools-extra/clangd/Compiler.h +++ b/clang-tools-extra/clangd/Compiler.h @@ -61,6 +61,12 @@ FeatureModuleSet *FeatureModules = nullptr; }; +/// Clears \p CI from options that are not supported by clangd, like codegen or +/// plugins. This should be combined with CommandMangler::adjust, which provides +/// similar functionality for options that needs to be stripped from compile +/// flags. +void disableUnsupportedOptions(CompilerInvocation &CI); + /// Builds compiler invocation that could be used to build AST or preamble. std::unique_ptr buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, diff --git a/clang-tools-extra/clangd/Compiler.cpp b/clang-tools-extra/clangd/Compiler.cpp --- a/clang-tools-extra/clangd/Compiler.cpp +++ b/clang-tools-extra/clangd/Compiler.cpp @@ -9,6 +9,7 @@ #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" @@ -41,6 +42,44 @@ IgnoreDiagnostics::log(DiagLevel, Info); } +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` + 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().getFormat().str(); + + CI.getFrontendOpts().Plugins.clear(); + CI.getFrontendOpts().AddPluginActions.clear(); + CI.getFrontendOpts().PluginArgs.clear(); + CI.getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly; + CI.getFrontendOpts().ActionName.clear(); +} + std::unique_ptr buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, std::vector *CC1Args) { @@ -60,43 +99,8 @@ CI->getFrontendOpts().DisableFree = false; CI->getLangOpts()->CommentOpts.ParseAllComments = true; CI->getLangOpts()->RetainCommentsFromSystemHeaders = true; - // 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` - 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().getFormat().str(); - - CI->getFrontendOpts().Plugins.clear(); - CI->getFrontendOpts().AddPluginActions.clear(); - CI->getFrontendOpts().PluginArgs.clear(); - CI->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly; - CI->getFrontendOpts().ActionName.clear(); + disableUnsupportedOptions(*CI); return CI; } diff --git a/clang-tools-extra/clangd/indexer/IndexerMain.cpp b/clang-tools-extra/clangd/indexer/IndexerMain.cpp --- a/clang-tools-extra/clangd/indexer/IndexerMain.cpp +++ b/clang-tools-extra/clangd/indexer/IndexerMain.cpp @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +#include "CompileCommands.h" +#include "Compiler.h" #include "index/IndexAction.h" #include "index/Merge.h" #include "index/Ref.h" @@ -23,6 +25,7 @@ #include "clang/Tooling/Tooling.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Signals.h" +#include namespace clang { namespace clangd { @@ -82,6 +85,15 @@ /*IncludeGraphCallback=*/nullptr); } + bool runInvocation(std::shared_ptr Invocation, + FileManager *Files, + std::shared_ptr PCHContainerOps, + DiagnosticConsumer *DiagConsumer) override { + disableUnsupportedOptions(*Invocation); + return tooling::FrontendActionFactory::runInvocation( + std::move(Invocation), Files, std::move(PCHContainerOps), DiagConsumer); + } + // Awkward: we write the result in the destructor, because the executor // takes ownership so it's the easiest way to get our data back out. ~IndexActionFactory() { @@ -135,7 +147,8 @@ clang::clangd::IndexFileIn Data; auto Err = Executor->get()->execute( std::make_unique(Data), - clang::tooling::getStripPluginsAdjuster()); + clang::tooling::ArgumentsAdjuster( + clang::clangd::CommandMangler::detect())); if (Err) { clang::clangd::elog("{0}", std::move(Err)); } diff --git a/clang-tools-extra/clangd/test/indexer.test b/clang-tools-extra/clangd/test/indexer.test new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/test/indexer.test @@ -0,0 +1,9 @@ +# RUN: rm -rf %t.cpp +# RUN: touch %t.cpp +# +# Make sure compile flags are adjusted for clangd. `--save-temps` creates a +# `.ii` file and `-verify` triggers extra diagnostics generation. Clangd should +# strip those. +# RUN: clangd-indexer %t.cpp -- -Xclang -verify --save-temps -- 2>&1 | FileCheck %s +# CHECK-NOT: error: no expected directives found: consider use of 'expected-no-diagnostics' +# RUN: not ls %t.ii