diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -654,8 +654,11 @@ {"executeCommandProvider", llvm::json::Object{ {"commands", - {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND, - ExecuteCommandParams::CLANGD_APPLY_TWEAK}}, + { + ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND, + ExecuteCommandParams::CLANGD_APPLY_TWEAK, + ExecuteCommandParams::CLANGD_APPLY_AUGMENTED_FIX, + }}, }}, {"typeHierarchyProvider", true}, {"memoryUsageProvider", true}, // clangd extension diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -195,6 +195,7 @@ Inputs.Opts = std::move(Opts); Inputs.Index = Index; Inputs.ClangTidyProvider = ClangTidyProvider; + Inputs.Modules = Modules; bool NewFile = WorkScheduler.update(File, Inputs, WantDiags); // If we loaded Foo.h, we want to make sure Foo.cpp is indexed. if (NewFile && BackgroundIdx) 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 @@ -16,6 +16,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_COMPILER_H #include "GlobalCompilationDatabase.h" +#include "Module.h" #include "TidyProvider.h" #include "index/Index.h" #include "support/ThreadsafeFS.h" @@ -54,6 +55,7 @@ const SymbolIndex *Index = nullptr; ParseOptions Opts = ParseOptions(); TidyProviderRef ClangTidyProvider = {}; + ModuleSet *Modules = nullptr; }; /// Builds compiler invocation that could be used to build AST or preamble. diff --git a/clang-tools-extra/clangd/Diagnostics.h b/clang-tools-extra/clangd/Diagnostics.h --- a/clang-tools-extra/clangd/Diagnostics.h +++ b/clang-tools-extra/clangd/Diagnostics.h @@ -9,7 +9,9 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_DIAGNOSTICS_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_DIAGNOSTICS_H +#include "Module.h" #include "Protocol.h" +#include "SourceCode.h" #include "support/Path.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/LangOptions.h" @@ -30,6 +32,20 @@ } // namespace tidy namespace clangd { +/// Can be used to provide additional fixes to diagnostics. +/// Returned codeaction might just contain a command instead to postpone +/// computation of expensive edits or calculate them outside the critical path. +/// This command will be invoked via executeCommand later on, if user requests. +class DiagnosticsAugmentationModule : public Module { +public: + /// Returns an action that can be invoked to fix the diagnostic. + virtual llvm::Optional + augmentDiag(DiagnosticsEngine::Level L, const clang::Diagnostic &Diag) = 0; + + // FIXME: Lift Tweak::Effect out of Tweak.h and use it here. + virtual llvm::Expected executeCommand(llvm::json::Value Args) = 0; +}; + struct ClangdDiagnosticOptions { /// If true, Clangd uses an LSP extension to embed the fixes with the /// diagnostics that are sent to the client. @@ -145,12 +161,16 @@ /// diagnostics, such as promoting warnings to errors, or ignoring /// diagnostics. void setLevelAdjuster(LevelAdjuster Adjuster) { this->Adjuster = Adjuster; } + void setAugmenters(llvm::ArrayRef Modules) { + this->Modules = Modules; + } private: void flushLastDiag(); DiagFixer Fixer = nullptr; LevelAdjuster Adjuster = nullptr; + llvm::ArrayRef Modules = {}; std::vector Output; llvm::Optional LangOpts; llvm::Optional LastDiag; diff --git a/clang-tools-extra/clangd/Diagnostics.cpp b/clang-tools-extra/clangd/Diagnostics.cpp --- a/clang-tools-extra/clangd/Diagnostics.cpp +++ b/clang-tools-extra/clangd/Diagnostics.cpp @@ -746,6 +746,10 @@ LastDiag->Fixes.insert(LastDiag->Fixes.end(), ExtraFixes.begin(), ExtraFixes.end()); } + for (auto *Module : Modules) { + if (auto Action = Module->augmentDiag(DiagLevel, Info)) + LastDiag->Actions.emplace_back(std::move(*Action)); + } } else { // Handle a note to an existing diagnostic. diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -375,6 +375,10 @@ }); Clang->setExternalSemaSource(FixIncludes->unresolvedNameRecorder()); } + if (Inputs.Modules) { + ASTDiags.setAugmenters( + Inputs.Modules->getModules()); + } IncludeStructure Includes; // If we are using a preamble, copy existing includes. diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp --- a/clang-tools-extra/clangd/Preamble.cpp +++ b/clang-tools-extra/clangd/Preamble.cpp @@ -326,6 +326,10 @@ trace::Span Tracer("BuildPreamble"); SPAN_ATTACH(Tracer, "File", FileName); StoreDiags PreambleDiagnostics; + if (Inputs.Modules) { + PreambleDiagnostics.setAugmenters( + Inputs.Modules->getModules()); + } llvm::IntrusiveRefCntPtr PreambleDiagsEngine = CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &PreambleDiagnostics, false); diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -914,6 +914,15 @@ bool fromJSON(const llvm::json::Value &, TweakArgs &, llvm::json::Path); llvm::json::Value toJSON(const TweakArgs &A); +/// Arguments for applyAugmentedFix command. These are send as code actions from +/// the server in response to diagnostics. +struct AugmentedDiagArgs { + /// ID of the module that generated the action. + std::string moduleID; + /// Augmenters arguments are opaque and should be parsed by them. + llvm::json::Value args; +}; + /// Exact commands are not specified in the protocol so we define the /// ones supported by Clangd here. The protocol specifies the command arguments /// to be "any[]" but to make this safer and more manageable, each command we @@ -927,6 +936,9 @@ const static llvm::StringLiteral CLANGD_APPLY_FIX_COMMAND; // Command to apply the code action. Uses TweakArgs as argument. const static llvm::StringLiteral CLANGD_APPLY_TWEAK; + // Command to apply a fix generated by a DiagnosticsAugmentationModule. Uses + // AugmentedDiagArgs as argument. + const static llvm::StringLiteral CLANGD_APPLY_AUGMENTED_FIX; /// The command identifier, e.g. CLANGD_APPLY_FIX_COMMAND std::string command; @@ -934,6 +946,7 @@ // Arguments llvm::Optional workspaceEdit; llvm::Optional tweakArgs; + llvm::Optional augmentDiagArgs; }; bool fromJSON(const llvm::json::Value &, ExecuteCommandParams &, llvm::json::Path); diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -659,6 +659,8 @@ "clangd.applyFix"; const llvm::StringLiteral ExecuteCommandParams::CLANGD_APPLY_TWEAK = "clangd.applyTweak"; +const llvm::StringLiteral ExecuteCommandParams::CLANGD_APPLY_AUGMENTED_FIX = + "clangd.applyAugmentedFix"; bool fromJSON(const llvm::json::Value &Params, ExecuteCommandParams &R, llvm::json::Path P) { diff --git a/clang-tools-extra/clangd/test/initialize-params.test b/clang-tools-extra/clangd/test/initialize-params.test --- a/clang-tools-extra/clangd/test/initialize-params.test +++ b/clang-tools-extra/clangd/test/initialize-params.test @@ -67,7 +67,8 @@ # CHECK-NEXT: "executeCommandProvider": { # CHECK-NEXT: "commands": [ # CHECK-NEXT: "clangd.applyFix", -# CHECK-NEXT: "clangd.applyTweak" +# CHECK-NEXT: "clangd.applyTweak", +# CHECK-NEXT: "clangd.applyAugmentedFix" # CHECK-NEXT: ] # CHECK-NEXT: }, # CHECK-NEXT: "hoverProvider": true,