<div dir="ltr"><div>Hi Mikael,</div><div><br></div>I suspect an undefined value creeps in for protocol capabilities, given that we have responses in different formats.<div>I'll investigate, thanks for the report!</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Jan 30, 2019 at 12:05 PM Mikael Holmén <<a href="mailto:mikael.holmen@ericsson.com">mikael.holmen@ericsson.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi Ilya,<br>
<br>
I've no idea why, but when I compile this commit with gcc (7.4.0) the <br>
test fixits-command.test fails, and the output from clangd is rather <br>
different from when I compile it with clang (3.6.0).<br>
<br>
So I run<br>
<br>
build-all/bin/clangd -lit-test < <br>
/data/repo/master/llvm-master/tools/clang/tools/extra/test/clangd/fixits-command.test<br>
<br>
and the first diff that I think is significant is that with gcc it says<br>
<br>
E[09:44:29.470] error while getting semantic code actions: -32602: <br>
trying to get AST for non-added document<br>
<br>
Before this patch the output from gcc and clang built clangd is identical.<br>
<br>
Attaching logs with gcc and clang.<br>
<br>
Any idea what the problem is?<br>
<br>
Regards,<br>
Mikael<br>
<br>
On 1/29/19 3:17 PM, Ilya Biryukov via cfe-commits wrote:<br>
> Author: ibiryukov<br>
> Date: Tue Jan 29 06:17:36 2019<br>
> New Revision: 352494<br>
> <br>
> URL: <a href="http://llvm.org/viewvc/llvm-project?rev=352494&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=352494&view=rev</a><br>
> Log:<br>
> [clangd] Interfaces for writing code tweaks<br>
> <br>
> Summary:<br>
> The code tweaks are an implementation of mini-refactorings exposed<br>
> via the LSP code actions. They run in two stages:<br>
>    - Stage 1. Decides whether the action is available to the user and<br>
>      collects all the information required to finish the action.<br>
>      Should be cheap, since this will run over all the actions known to<br>
>      clangd on each textDocument/codeAction request from the client.<br>
> <br>
>    - Stage 2. Uses information from stage 1 to produce the actual edits<br>
>      that the code action should perform. This stage can be expensive and<br>
>      will only run if the user chooses to perform the specified action in<br>
>      the UI.<br>
> <br>
> One unfortunate consequence of this change is increased latency of<br>
> processing the textDocument/codeAction requests, which now wait for an<br>
> AST. However, we cannot avoid this with what we have available in the LSP<br>
> today.<br>
> <br>
> Reviewers: kadircet, ioeric, hokein, sammccall<br>
> <br>
> Reviewed By: sammccall<br>
> <br>
> Subscribers: mgrang, mgorny, MaskRay, jkorous, arphaman, cfe-commits<br>
> <br>
> Differential Revision: <a href="https://reviews.llvm.org/D56267" rel="noreferrer" target="_blank">https://reviews.llvm.org/D56267</a><br>
> <br>
> Added:<br>
>      clang-tools-extra/trunk/clangd/refactor/<br>
>      clang-tools-extra/trunk/clangd/refactor/Tweak.cpp<br>
>      clang-tools-extra/trunk/clangd/refactor/Tweak.h<br>
>      clang-tools-extra/trunk/clangd/refactor/tweaks/<br>
>      clang-tools-extra/trunk/clangd/refactor/tweaks/CMakeLists.txt<br>
>      clang-tools-extra/trunk/clangd/refactor/tweaks/Dummy.cpp<br>
> Modified:<br>
>      clang-tools-extra/trunk/clangd/CMakeLists.txt<br>
>      clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp<br>
>      clang-tools-extra/trunk/clangd/ClangdServer.cpp<br>
>      clang-tools-extra/trunk/clangd/ClangdServer.h<br>
>      clang-tools-extra/trunk/clangd/Protocol.cpp<br>
>      clang-tools-extra/trunk/clangd/Protocol.h<br>
>      clang-tools-extra/trunk/clangd/SourceCode.cpp<br>
>      clang-tools-extra/trunk/clangd/SourceCode.h<br>
>      clang-tools-extra/trunk/clangd/tool/CMakeLists.txt<br>
>      clang-tools-extra/trunk/test/clangd/fixits-command.test<br>
>      clang-tools-extra/trunk/test/clangd/initialize-params.test<br>
> <br>
> Modified: clang-tools-extra/trunk/clangd/CMakeLists.txt<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CMakeLists.txt?rev=352494&r1=352493&r2=352494&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CMakeLists.txt?rev=352494&r1=352493&r2=352494&view=diff</a><br>
> ==============================================================================<br>
> --- clang-tools-extra/trunk/clangd/CMakeLists.txt (original)<br>
> +++ clang-tools-extra/trunk/clangd/CMakeLists.txt Tue Jan 29 06:17:36 2019<br>
> @@ -71,6 +71,8 @@ add_clang_library(clangDaemon<br>
>     index/dex/PostingList.cpp<br>
>     index/dex/Trigram.cpp<br>
>   <br>
> +  refactor/Tweak.cpp<br>
> +<br>
>     LINK_LIBS<br>
>     clangAST<br>
>     clangASTMatchers<br>
> @@ -108,6 +110,7 @@ add_clang_library(clangDaemon<br>
>     ${CLANGD_ATOMIC_LIB}<br>
>     )<br>
>   <br>
> +add_subdirectory(refactor/tweaks)<br>
>   if( LLVM_LIB_FUZZING_ENGINE OR LLVM_USE_SANITIZE_COVERAGE )<br>
>     add_subdirectory(fuzzer)<br>
>   endif()<br>
> <br>
> Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=352494&r1=352493&r2=352494&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=352494&r1=352493&r2=352494&view=diff</a><br>
> ==============================================================================<br>
> --- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original)<br>
> +++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Tue Jan 29 06:17:36 2019<br>
> @@ -8,11 +8,14 @@<br>
>   <br>
>   #include "ClangdLSPServer.h"<br>
>   #include "Diagnostics.h"<br>
> +#include "Protocol.h"<br>
>   #include "SourceCode.h"<br>
>   #include "Trace.h"<br>
>   #include "URI.h"<br>
> +#include "clang/Tooling/Core/Replacement.h"<br>
>   #include "llvm/ADT/ScopeExit.h"<br>
>   #include "llvm/Support/Errc.h"<br>
> +#include "llvm/Support/Error.h"<br>
>   #include "llvm/Support/FormatVariadic.h"<br>
>   #include "llvm/Support/Path.h"<br>
>   #include "llvm/Support/ScopedPrinter.h"<br>
> @@ -30,6 +33,28 @@ public:<br>
>     }<br>
>   };<br>
>   <br>
> +/// Transforms a tweak into a code action that would apply it if executed.<br>
> +/// EXPECTS: T.prepare() was called and returned true.<br>
> +CodeAction toCodeAction(const ClangdServer::TweakRef &T, const URIForFile &File,<br>
> +                        Range Selection) {<br>
> +  CodeAction CA;<br>
> +  CA.title = T.Title;<br>
> +  CA.kind = CodeAction::REFACTOR_KIND;<br>
> +  // This tweak may have an expensive second stage, we only run it if the user<br>
> +  // actually chooses it in the UI. We reply with a command that would run the<br>
> +  // corresponding tweak.<br>
> +  // FIXME: for some tweaks, computing the edits is cheap and we could send them<br>
> +  //        directly.<br>
> +  CA.command.emplace();<br>
> +  CA.command->title = T.Title;<br>
> +  CA.command->command = Command::CLANGD_APPLY_TWEAK;<br>
> +  CA.command->tweakArgs.emplace();<br>
> +  CA.command->tweakArgs->file = File;<br>
> +  CA.command->tweakArgs->tweakID = <a href="http://T.ID" rel="noreferrer" target="_blank">T.ID</a>;<br>
> +  CA.command->tweakArgs->selection = Selection;<br>
> +  return CA;<br>
> +};<br>
> +<br>
>   void adjustSymbolKinds(llvm::MutableArrayRef<DocumentSymbol> Syms,<br>
>                          SymbolKindBitset Kinds) {<br>
>     for (auto &S : Syms) {<br>
> @@ -338,7 +363,9 @@ void ClangdLSPServer::onInitialize(const<br>
>               {"referencesProvider", true},<br>
>               {"executeCommandProvider",<br>
>                llvm::json::Object{<br>
> -                 {"commands", {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND}},<br>
> +                 {"commands",<br>
> +                  {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND,<br>
> +                   ExecuteCommandParams::CLANGD_APPLY_TWEAK}},<br>
>                }},<br>
>           }}}});<br>
>   }<br>
> @@ -400,7 +427,7 @@ void ClangdLSPServer::onFileEvent(const<br>
>   <br>
>   void ClangdLSPServer::onCommand(const ExecuteCommandParams &Params,<br>
>                                   Callback<llvm::json::Value> Reply) {<br>
> -  auto ApplyEdit = [&](WorkspaceEdit WE) {<br>
> +  auto ApplyEdit = [this](WorkspaceEdit WE) {<br>
>       ApplyWorkspaceEditParams Edit;<br>
>       Edit.edit = std::move(WE);<br>
>       // Ideally, we would wait for the response and if there is no error, we<br>
> @@ -420,6 +447,31 @@ void ClangdLSPServer::onCommand(const Ex<br>
>   <br>
>       Reply("Fix applied.");<br>
>       ApplyEdit(*Params.workspaceEdit);<br>
> +  } else if (Params.command == ExecuteCommandParams::CLANGD_APPLY_TWEAK &&<br>
> +             Params.tweakArgs) {<br>
> +    auto Code = DraftMgr.getDraft(Params.tweakArgs->file.file());<br>
> +    if (!Code)<br>
> +      return Reply(llvm::createStringError(<br>
> +          llvm::inconvertibleErrorCode(),<br>
> +          "trying to apply a code action for a non-added file"));<br>
> +<br>
> +    auto Action = [ApplyEdit](decltype(Reply) Reply, URIForFile File,<br>
> +                              std::string Code,<br>
> +                              llvm::Expected<tooling::Replacements> R) {<br>
> +      if (!R)<br>
> +        return Reply(R.takeError());<br>
> +<br>
> +      WorkspaceEdit WE;<br>
> +      WE.changes.emplace();<br>
> +      (*WE.changes)[File.uri()] = replacementsToEdits(Code, *R);<br>
> +<br>
> +      Reply("Fix applied.");<br>
> +      ApplyEdit(std::move(WE));<br>
> +    };<br>
> +    Server->applyTweak(Params.tweakArgs->file.file(),<br>
> +                       Params.tweakArgs->selection, Params.tweakArgs->tweakID,<br>
> +                       Bind(Action, std::move(Reply), Params.tweakArgs->file,<br>
> +                            std::move(*Code)));<br>
>     } else {<br>
>       // We should not get here because ExecuteCommandParams would not have<br>
>       // parsed in the first place and this handler should not be called. But if<br>
> @@ -601,28 +653,53 @@ static llvm::Optional<Command> asCommand<br>
>   <br>
>   void ClangdLSPServer::onCodeAction(const CodeActionParams &Params,<br>
>                                      Callback<llvm::json::Value> Reply) {<br>
> -  auto Code = DraftMgr.getDraft(Params.textDocument.uri.file());<br>
> +  URIForFile File = Params.textDocument.uri;<br>
> +  auto Code = DraftMgr.getDraft(File.file());<br>
>     if (!Code)<br>
>       return Reply(llvm::make_error<LSPError>(<br>
>           "onCodeAction called for non-added file", ErrorCode::InvalidParams));<br>
>     // We provide a code action for Fixes on the specified diagnostics.<br>
> -  std::vector<CodeAction> Actions;<br>
> +  std::vector<CodeAction> FixIts;<br>
>     for (const Diagnostic &D : Params.context.diagnostics) {<br>
> -    for (auto &F : getFixes(Params.textDocument.uri.file(), D)) {<br>
> -      Actions.push_back(toCodeAction(F, Params.textDocument.uri));<br>
> -      Actions.back().diagnostics = {D};<br>
> +    for (auto &F : getFixes(File.file(), D)) {<br>
> +      FixIts.push_back(toCodeAction(F, Params.textDocument.uri));<br>
> +      FixIts.back().diagnostics = {D};<br>
>       }<br>
>     }<br>
>   <br>
> -  if (SupportsCodeAction)<br>
> -    Reply(llvm::json::Array(Actions));<br>
> -  else {<br>
> -    std::vector<Command> Commands;<br>
> -    for (const auto &Action : Actions)<br>
> -      if (auto Command = asCommand(Action))<br>
> -        Commands.push_back(std::move(*Command));<br>
> -    Reply(llvm::json::Array(Commands));<br>
> -  }<br>
> +  // Now enumerate the semantic code actions.<br>
> +  auto ConsumeActions =<br>
> +      [this](decltype(Reply) Reply, URIForFile File, std::string Code,<br>
> +             Range Selection, std::vector<CodeAction> FixIts,<br>
> +             llvm::Expected<std::vector<ClangdServer::TweakRef>> Tweaks) {<br>
> +        if (!Tweaks) {<br>
> +          auto Err = Tweaks.takeError();<br>
> +          if (Err.isA<CancelledError>())<br>
> +            return Reply(std::move(Err)); // do no logging, this is expected.<br>
> +          elog("error while getting semantic code actions: {0}",<br>
> +               std::move(Err));<br>
> +          return Reply(llvm::json::Array(FixIts));<br>
> +        }<br>
> +<br>
> +        std::vector<CodeAction> Actions = std::move(FixIts);<br>
> +        Actions.reserve(Actions.size() + Tweaks->size());<br>
> +        for (const auto &T : *Tweaks)<br>
> +          Actions.push_back(toCodeAction(T, File, Selection));<br>
> +<br>
> +        if (SupportsCodeAction)<br>
> +          return Reply(llvm::json::Array(Actions));<br>
> +        std::vector<Command> Commands;<br>
> +        for (const auto &Action : Actions) {<br>
> +          if (auto Command = asCommand(Action))<br>
> +            Commands.push_back(std::move(*Command));<br>
> +        }<br>
> +        return Reply(llvm::json::Array(Commands));<br>
> +      };<br>
> +<br>
> +  Server->enumerateTweaks(File.file(), Params.range,<br>
> +                          Bind(ConsumeActions, std::move(Reply),<br>
> +                               std::move(File), std::move(*Code), Params.range,<br>
> +                               std::move(FixIts)));<br>
>   }<br>
>   <br>
>   void ClangdLSPServer::onCompletion(const CompletionParams &Params,<br>
> <br>
> Modified: clang-tools-extra/trunk/clangd/ClangdServer.cpp<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=352494&r1=352493&r2=352494&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=352494&r1=352493&r2=352494&view=diff</a><br>
> ==============================================================================<br>
> --- clang-tools-extra/trunk/clangd/ClangdServer.cpp (original)<br>
> +++ clang-tools-extra/trunk/clangd/ClangdServer.cpp Tue Jan 29 06:17:36 2019<br>
> @@ -16,11 +16,13 @@<br>
>   #include "XRefs.h"<br>
>   #include "index/FileIndex.h"<br>
>   #include "index/Merge.h"<br>
> +#include "refactor/Tweak.h"<br>
>   #include "clang/Format/Format.h"<br>
>   #include "clang/Frontend/CompilerInstance.h"<br>
>   #include "clang/Frontend/CompilerInvocation.h"<br>
>   #include "clang/Lex/Preprocessor.h"<br>
>   #include "clang/Tooling/CompilationDatabase.h"<br>
> +#include "clang/Tooling/Core/Replacement.h"<br>
>   #include "clang/Tooling/Refactoring/RefactoringResultConsumer.h"<br>
>   #include "clang/Tooling/Refactoring/Rename/RenamingAction.h"<br>
>   #include "llvm/ADT/ArrayRef.h"<br>
> @@ -28,10 +30,12 @@<br>
>   #include "llvm/ADT/ScopeExit.h"<br>
>   #include "llvm/ADT/StringRef.h"<br>
>   #include "llvm/Support/Errc.h"<br>
> +#include "llvm/Support/Error.h"<br>
>   #include "llvm/Support/FileSystem.h"<br>
>   #include "llvm/Support/Path.h"<br>
>   #include "llvm/Support/raw_ostream.h"<br>
>   #include <future><br>
> +#include <memory><br>
>   #include <mutex><br>
>   <br>
>   namespace clang {<br>
> @@ -325,6 +329,56 @@ void ClangdServer::rename(PathRef File,<br>
>         "Rename", File, Bind(Action, File.str(), NewName.str(), std::move(CB)));<br>
>   }<br>
>   <br>
> +void ClangdServer::enumerateTweaks(PathRef File, Range Sel,<br>
> +                                   Callback<std::vector<TweakRef>> CB) {<br>
> +  auto Action = [Sel](decltype(CB) CB, std::string File,<br>
> +                      Expected<InputsAndAST> InpAST) {<br>
> +    if (!InpAST)<br>
> +      return CB(InpAST.takeError());<br>
> +<br>
> +    auto &AST = InpAST->AST;<br>
> +    auto CursorLoc = sourceLocationInMainFile(<br>
> +        AST.getASTContext().getSourceManager(), Sel.start);<br>
> +    if (!CursorLoc)<br>
> +      return CB(CursorLoc.takeError());<br>
> +    Tweak::Selection Inputs = {InpAST->Inputs.Contents, InpAST->AST,<br>
> +                               *CursorLoc};<br>
> +<br>
> +    std::vector<TweakRef> Res;<br>
> +    for (auto &T : prepareTweaks(Inputs))<br>
> +      Res.push_back({T->id(), T->title()});<br>
> +    CB(std::move(Res));<br>
> +  };<br>
> +<br>
> +  WorkScheduler.runWithAST("EnumerateTweaks", File,<br>
> +                           Bind(Action, std::move(CB), File.str()));<br>
> +}<br>
> +<br>
> +void ClangdServer::applyTweak(PathRef File, Range Sel, TweakID ID,<br>
> +                              Callback<tooling::Replacements> CB) {<br>
> +  auto Action = [ID, Sel](decltype(CB) CB, std::string File,<br>
> +                          Expected<InputsAndAST> InpAST) {<br>
> +    if (!InpAST)<br>
> +      return CB(InpAST.takeError());<br>
> +<br>
> +    auto &AST = InpAST->AST;<br>
> +    auto CursorLoc = sourceLocationInMainFile(<br>
> +        AST.getASTContext().getSourceManager(), Sel.start);<br>
> +    if (!CursorLoc)<br>
> +      return CB(CursorLoc.takeError());<br>
> +    Tweak::Selection Inputs = {InpAST->Inputs.Contents, InpAST->AST,<br>
> +                               *CursorLoc};<br>
> +<br>
> +    auto A = prepareTweak(ID, Inputs);<br>
> +    if (!A)<br>
> +      return CB(A.takeError());<br>
> +    // FIXME: run formatter on top of resulting replacements.<br>
> +    return CB((*A)->apply(Inputs));<br>
> +  };<br>
> +  WorkScheduler.runWithAST("ApplyTweak", File,<br>
> +                           Bind(Action, std::move(CB), File.str()));<br>
> +}<br>
> +<br>
>   void ClangdServer::dumpAST(PathRef File,<br>
>                              llvm::unique_function<void(std::string)> Callback) {<br>
>     auto Action = [](decltype(Callback) Callback,<br>
> <br>
> Modified: clang-tools-extra/trunk/clangd/ClangdServer.h<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.h?rev=352494&r1=352493&r2=352494&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.h?rev=352494&r1=352493&r2=352494&view=diff</a><br>
> ==============================================================================<br>
> --- clang-tools-extra/trunk/clangd/ClangdServer.h (original)<br>
> +++ clang-tools-extra/trunk/clangd/ClangdServer.h Tue Jan 29 06:17:36 2019<br>
> @@ -21,8 +21,10 @@<br>
>   #include "index/Background.h"<br>
>   #include "index/FileIndex.h"<br>
>   #include "index/Index.h"<br>
> +#include "refactor/Tweak.h"<br>
>   #include "clang/Tooling/CompilationDatabase.h"<br>
>   #include "clang/Tooling/Core/Replacement.h"<br>
> +#include "llvm/ADT/FunctionExtras.h"<br>
>   #include "llvm/ADT/IntrusiveRefCntPtr.h"<br>
>   #include "llvm/ADT/Optional.h"<br>
>   #include "llvm/ADT/StringRef.h"<br>
> @@ -211,6 +213,18 @@ public:<br>
>     void rename(PathRef File, Position Pos, llvm::StringRef NewName,<br>
>                 Callback<std::vector<tooling::Replacement>> CB);<br>
>   <br>
> +  struct TweakRef {<br>
> +    TweakID ID;        /// ID to pass for applyTweak.<br>
> +    std::string Title; /// A single-line message to show in the UI.<br>
> +  };<br>
> +  /// Enumerate the code tweaks available to the user at a specified point.<br>
> +  void enumerateTweaks(PathRef File, Range Sel,<br>
> +                       Callback<std::vector<TweakRef>> CB);<br>
> +<br>
> +  /// Apply the code tweak with a specified \p ID.<br>
> +  void applyTweak(PathRef File, Range Sel, TweakID ID,<br>
> +                  Callback<tooling::Replacements> CB);<br>
> +<br>
>     /// Only for testing purposes.<br>
>     /// Waits until all requests to worker thread are finished and dumps AST for<br>
>     /// \p File. \p File must be in the list of added documents.<br>
> <br>
> Modified: clang-tools-extra/trunk/clangd/Protocol.cpp<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.cpp?rev=352494&r1=352493&r2=352494&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.cpp?rev=352494&r1=352493&r2=352494&view=diff</a><br>
> ==============================================================================<br>
> --- clang-tools-extra/trunk/clangd/Protocol.cpp (original)<br>
> +++ clang-tools-extra/trunk/clangd/Protocol.cpp Tue Jan 29 06:17:36 2019<br>
> @@ -421,6 +421,9 @@ bool fromJSON(const llvm::json::Value &P<br>
>   <br>
>   const llvm::StringLiteral ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND =<br>
>       "clangd.applyFix";<br>
> +const llvm::StringLiteral ExecuteCommandParams::CLANGD_APPLY_TWEAK =<br>
> +    "clangd.applyTweak";<br>
> +<br>
>   bool fromJSON(const llvm::json::Value &Params, ExecuteCommandParams &R) {<br>
>     llvm::json::ObjectMapper O(Params);<br>
>     if (!O || !O.map("command", R.command))<br>
> @@ -431,6 +434,8 @@ bool fromJSON(const llvm::json::Value &P<br>
>       return Args && Args->size() == 1 &&<br>
>              fromJSON(Args->front(), R.workspaceEdit);<br>
>     }<br>
> +  if (R.command == ExecuteCommandParams::CLANGD_APPLY_TWEAK)<br>
> +    return Args && Args->size() == 1 && fromJSON(Args->front(), R.tweakArgs);<br>
>     return false; // Unrecognized command.<br>
>   }<br>
>   <br>
> @@ -497,10 +502,13 @@ llvm::json::Value toJSON(const Command &<br>
>     auto Cmd = llvm::json::Object{{"title", C.title}, {"command", C.command}};<br>
>     if (C.workspaceEdit)<br>
>       Cmd["arguments"] = {*C.workspaceEdit};<br>
> +  if (C.tweakArgs)<br>
> +    Cmd["arguments"] = {*C.tweakArgs};<br>
>     return std::move(Cmd);<br>
>   }<br>
>   <br>
>   const llvm::StringLiteral CodeAction::QUICKFIX_KIND = "quickfix";<br>
> +const llvm::StringLiteral CodeAction::REFACTOR_KIND = "refactor";<br>
>   <br>
>   llvm::json::Value toJSON(const CodeAction &CA) {<br>
>     auto CodeAction = llvm::json::Object{{"title", CA.title}};<br>
> @@ -544,6 +552,17 @@ llvm::json::Value toJSON(const Workspace<br>
>     return llvm::json::Object{{"changes", std::move(FileChanges)}};<br>
>   }<br>
>   <br>
> +bool fromJSON(const llvm::json::Value &Params, TweakArgs &A) {<br>
> +  llvm::json::ObjectMapper O(Params);<br>
> +  return O && O.map("file", A.file) && O.map("selection", A.selection) &&<br>
> +         O.map("tweakID", A.tweakID);<br>
> +}<br>
> +<br>
> +llvm::json::Value toJSON(const TweakArgs &A) {<br>
> +  return llvm::json::Object{<br>
> +      {"tweakID", A.tweakID}, {"selection", A.selection}, {"file", A.file}};<br>
> +}<br>
> +<br>
>   llvm::json::Value toJSON(const ApplyWorkspaceEditParams &Params) {<br>
>     return llvm::json::Object{{"edit", Params.edit}};<br>
>   }<br>
> <br>
> Modified: clang-tools-extra/trunk/clangd/Protocol.h<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=352494&r1=352493&r2=352494&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=352494&r1=352493&r2=352494&view=diff</a><br>
> ==============================================================================<br>
> --- clang-tools-extra/trunk/clangd/Protocol.h (original)<br>
> +++ clang-tools-extra/trunk/clangd/Protocol.h Tue Jan 29 06:17:36 2019<br>
> @@ -631,6 +631,21 @@ struct WorkspaceEdit {<br>
>   bool fromJSON(const llvm::json::Value &, WorkspaceEdit &);<br>
>   llvm::json::Value toJSON(const WorkspaceEdit &WE);<br>
>   <br>
> +/// Arguments for the 'applyTweak' command. The server sends these commands as a<br>
> +/// response to the textDocument/codeAction request. The client can later send a<br>
> +/// command back to the server if the user requests to execute a particular code<br>
> +/// tweak.<br>
> +struct TweakArgs {<br>
> +  /// A file provided by the client on a textDocument/codeAction request.<br>
> +  URIForFile file;<br>
> +  /// A selection provided by the client on a textDocument/codeAction request.<br>
> +  Range selection;<br>
> +  /// ID of the tweak that should be executed. Corresponds to Tweak::id().<br>
> +  std::string tweakID;<br>
> +};<br>
> +bool fromJSON(const llvm::json::Value &, TweakArgs &);<br>
> +llvm::json::Value toJSON(const TweakArgs &A);<br>
> +<br>
>   /// Exact commands are not specified in the protocol so we define the<br>
>   /// ones supported by Clangd here. The protocol specifies the command arguments<br>
>   /// to be "any[]" but to make this safer and more manageable, each command we<br>
> @@ -642,12 +657,15 @@ llvm::json::Value toJSON(const Workspace<br>
>   struct ExecuteCommandParams {<br>
>     // Command to apply fix-its. Uses WorkspaceEdit as argument.<br>
>     const static llvm::StringLiteral CLANGD_APPLY_FIX_COMMAND;<br>
> +  // Command to apply the code action. Uses TweakArgs as argument.<br>
> +  const static llvm::StringLiteral CLANGD_APPLY_TWEAK;<br>
>   <br>
>     /// The command identifier, e.g. CLANGD_APPLY_FIX_COMMAND<br>
>     std::string command;<br>
>   <br>
>     // Arguments<br>
>     llvm::Optional<WorkspaceEdit> workspaceEdit;<br>
> +  llvm::Optional<TweakArgs> tweakArgs;<br>
>   };<br>
>   bool fromJSON(const llvm::json::Value &, ExecuteCommandParams &);<br>
>   <br>
> @@ -669,6 +687,7 @@ struct CodeAction {<br>
>     /// Used to filter code actions.<br>
>     llvm::Optional<std::string> kind;<br>
>     const static llvm::StringLiteral QUICKFIX_KIND;<br>
> +  const static llvm::StringLiteral REFACTOR_KIND;<br>
>   <br>
>     /// The diagnostics that this code action resolves.<br>
>     llvm::Optional<std::vector<Diagnostic>> diagnostics;<br>
> <br>
> Modified: clang-tools-extra/trunk/clangd/SourceCode.cpp<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/SourceCode.cpp?rev=352494&r1=352493&r2=352494&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/SourceCode.cpp?rev=352494&r1=352493&r2=352494&view=diff</a><br>
> ==============================================================================<br>
> --- clang-tools-extra/trunk/clangd/SourceCode.cpp (original)<br>
> +++ clang-tools-extra/trunk/clangd/SourceCode.cpp Tue Jan 29 06:17:36 2019<br>
> @@ -141,6 +141,16 @@ Position sourceLocToPosition(const Sourc<br>
>     return P;<br>
>   }<br>
>   <br>
> +llvm::Expected<SourceLocation> sourceLocationInMainFile(const SourceManager &SM,<br>
> +                                                        Position P) {<br>
> +  llvm::StringRef Code = SM.getBuffer(SM.getMainFileID())->getBuffer();<br>
> +  auto Offset =<br>
> +      positionToOffset(Code, P, /*AllowColumnBeyondLineLength=*/false);<br>
> +  if (!Offset)<br>
> +    return Offset.takeError();<br>
> +  return SM.getLocForStartOfFile(SM.getMainFileID()).getLocWithOffset(*Offset);<br>
> +}<br>
> +<br>
>   Range halfOpenToRange(const SourceManager &SM, CharSourceRange R) {<br>
>     // Clang is 1-based, LSP uses 0-based indexes.<br>
>     Position Begin = sourceLocToPosition(SM, R.getBegin());<br>
> <br>
> Modified: clang-tools-extra/trunk/clangd/SourceCode.h<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/SourceCode.h?rev=352494&r1=352493&r2=352494&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/SourceCode.h?rev=352494&r1=352493&r2=352494&view=diff</a><br>
> ==============================================================================<br>
> --- clang-tools-extra/trunk/clangd/SourceCode.h (original)<br>
> +++ clang-tools-extra/trunk/clangd/SourceCode.h Tue Jan 29 06:17:36 2019<br>
> @@ -56,6 +56,11 @@ Position offsetToPosition(llvm::StringRe<br>
>   /// FIXME: This should return an error if the location is invalid.<br>
>   Position sourceLocToPosition(const SourceManager &SM, SourceLocation Loc);<br>
>   <br>
> +/// Return the file location, corresponding to \p P. Note that one should take<br>
> +/// care to avoid comparing the result with expansion locations.<br>
> +llvm::Expected<SourceLocation> sourceLocationInMainFile(const SourceManager &SM,<br>
> +                                                        Position P);<br>
> +<br>
>   // Converts a half-open clang source range to an LSP range.<br>
>   // Note that clang also uses closed source ranges, which this can't handle!<br>
>   Range halfOpenToRange(const SourceManager &SM, CharSourceRange R);<br>
> <br>
> Added: clang-tools-extra/trunk/clangd/refactor/Tweak.cpp<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/refactor/Tweak.cpp?rev=352494&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/refactor/Tweak.cpp?rev=352494&view=auto</a><br>
> ==============================================================================<br>
> --- clang-tools-extra/trunk/clangd/refactor/Tweak.cpp (added)<br>
> +++ clang-tools-extra/trunk/clangd/refactor/Tweak.cpp Tue Jan 29 06:17:36 2019<br>
> @@ -0,0 +1,74 @@<br>
> +//===--- Tweak.cpp -----------------------------------------------*- C++-*-===//<br>
> +//<br>
> +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.<br>
> +// See <a href="https://llvm.org/LICENSE.txt" rel="noreferrer" target="_blank">https://llvm.org/LICENSE.txt</a> for license information.<br>
> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception<br>
> +//<br>
> +//===----------------------------------------------------------------------===//<br>
> +#include "Tweak.h"<br>
> +#include "Logger.h"<br>
> +#include "llvm/ADT/STLExtras.h"<br>
> +#include "llvm/ADT/StringMap.h"<br>
> +#include "llvm/Support/Error.h"<br>
> +#include "llvm/Support/Registry.h"<br>
> +#include <functional><br>
> +#include <memory><br>
> +<br>
> +LLVM_INSTANTIATE_REGISTRY(llvm::Registry<clang::clangd::Tweak>);<br>
> +<br>
> +namespace clang {<br>
> +namespace clangd {<br>
> +<br>
> +/// A handy typedef to save some typing.<br>
> +typedef llvm::Registry<Tweak> TweakRegistry;<br>
> +<br>
> +namespace {<br>
> +/// Asserts invariants on TweakRegistry. No-op with assertion disabled.<br>
> +void validateRegistry() {<br>
> +#ifndef NDEBUG<br>
> +  llvm::StringSet<> Seen;<br>
> +  for (const auto &E : TweakRegistry::entries()) {<br>
> +    // REGISTER_TWEAK ensures E.getName() is equal to the tweak class name.<br>
> +    // We check that id() matches it.<br>
> +    assert(E.instantiate()->id() == E.getName() &&<br>
> +           "id should be equal to class name");<br>
> +    assert(Seen.try_emplace(E.getName()).second && "duplicate check id");<br>
> +  }<br>
> +#endif<br>
> +}<br>
> +} // namespace<br>
> +<br>
> +std::vector<std::unique_ptr<Tweak>> prepareTweaks(const Tweak::Selection &S) {<br>
> +  validateRegistry();<br>
> +<br>
> +  std::vector<std::unique_ptr<Tweak>> Available;<br>
> +  for (const auto &E : TweakRegistry::entries()) {<br>
> +    std::unique_ptr<Tweak> T = E.instantiate();<br>
> +    if (!T->prepare(S))<br>
> +      continue;<br>
> +    Available.push_back(std::move(T));<br>
> +  }<br>
> +  // Ensure deterministic order of the results.<br>
> +  llvm::sort(Available,<br>
> +             [](const std::unique_ptr<Tweak> &L,<br>
> +                const std::unique_ptr<Tweak> &R) { return L->id() < R->id(); });<br>
> +  return Available;<br>
> +}<br>
> +<br>
> +llvm::Expected<std::unique_ptr<Tweak>> prepareTweak(TweakID ID,<br>
> +                                                    const Tweak::Selection &S) {<br>
> +  auto It = llvm::find_if(<br>
> +      TweakRegistry::entries(),<br>
> +      [ID](const TweakRegistry::entry &E) { return E.getName() == ID; });<br>
> +  if (It == TweakRegistry::end())<br>
> +    return llvm::createStringError(llvm::inconvertibleErrorCode(),<br>
> +                                   "id of the tweak is invalid");<br>
> +  std::unique_ptr<Tweak> T = It->instantiate();<br>
> +  if (!T->prepare(S))<br>
> +    return llvm::createStringError(llvm::inconvertibleErrorCode(),<br>
> +                                   "failed to prepare() a check");<br>
> +  return T;<br>
> +}<br>
> +<br>
> +} // namespace clangd<br>
> +} // namespace clang<br>
> <br>
> Added: clang-tools-extra/trunk/clangd/refactor/Tweak.h<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/refactor/Tweak.h?rev=352494&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/refactor/Tweak.h?rev=352494&view=auto</a><br>
> ==============================================================================<br>
> --- clang-tools-extra/trunk/clangd/refactor/Tweak.h (added)<br>
> +++ clang-tools-extra/trunk/clangd/refactor/Tweak.h Tue Jan 29 06:17:36 2019<br>
> @@ -0,0 +1,98 @@<br>
> +//===--- Tweak.h -------------------------------------------------*- C++-*-===//<br>
> +//<br>
> +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.<br>
> +// See <a href="https://llvm.org/LICENSE.txt" rel="noreferrer" target="_blank">https://llvm.org/LICENSE.txt</a> for license information.<br>
> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception<br>
> +//<br>
> +//===----------------------------------------------------------------------===//<br>
> +// Tweaks are small refactoring-like actions that run over the AST and produce<br>
> +// the set of edits as a result. They are local, i.e. they should take the<br>
> +// current editor context, e.g. the cursor position and selection into account.<br>
> +// The actions are executed in two stages:<br>
> +//   - Stage 1 should check whether the action is available in a current<br>
> +//     context. It should be cheap and fast to compute as it is executed for all<br>
> +//     available actions on every client request, which happen quite frequently.<br>
> +//   - Stage 2 is performed after stage 1 and can be more expensive to compute.<br>
> +//     It is performed when the user actually chooses the action.<br>
> +//===----------------------------------------------------------------------===//<br>
> +<br>
> +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_ACTIONS_TWEAK_H<br>
> +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_ACTIONS_TWEAK_H<br>
> +<br>
> +#include "ClangdUnit.h"<br>
> +#include "Protocol.h"<br>
> +#include "clang/Tooling/Core/Replacement.h"<br>
> +#include "llvm/ADT/Optional.h"<br>
> +#include "llvm/ADT/StringRef.h"<br>
> +namespace clang {<br>
> +namespace clangd {<br>
> +<br>
> +using TweakID = llvm::StringRef;<br>
> +<br>
> +/// An interface base for small context-sensitive refactoring actions.<br>
> +/// To implement a new tweak use the following pattern in a .cpp file:<br>
> +///   class MyTweak : public Tweak {<br>
> +///   public:<br>
> +///     TweakID id() const override final; // definition provided by<br>
> +///                                        // REGISTER_TWEAK.<br>
> +///     // implement other methods here.<br>
> +///   };<br>
> +///   REGISTER_TWEAK(MyTweak);<br>
> +class Tweak {<br>
> +public:<br>
> +  /// Input to prepare and apply tweaks.<br>
> +  struct Selection {<br>
> +    /// The text of the active document.<br>
> +    llvm::StringRef Code;<br>
> +    /// Parsed AST of the active file.<br>
> +    ParsedAST &AST;<br>
> +    /// A location of the cursor in the editor.<br>
> +    SourceLocation Cursor;<br>
> +    // FIXME: add selection when there are checks relying on it.<br>
> +    // FIXME: provide a way to get sources and ASTs for other files.<br>
> +    // FIXME: cache some commonly required information (e.g. AST nodes under<br>
> +    //        cursor) to avoid redundant AST visit in every action.<br>
> +  };<br>
> +  virtual ~Tweak() = default;<br>
> +  /// A unique id of the action, it is always equal to the name of the class<br>
> +  /// defining the Tweak. Definition is provided automatically by<br>
> +  /// REGISTER_TWEAK.<br>
> +  virtual TweakID id() const = 0;<br>
> +  /// Run the first stage of the action. The non-None result indicates that the<br>
> +  /// action is available and should be shown to the user. Returns None if the<br>
> +  /// action is not available.<br>
> +  /// This function should be fast, if the action requires non-trivial work it<br>
> +  /// should be moved into 'apply'.<br>
> +  /// Returns true iff the action is available and apply() can be called on it.<br>
> +  virtual bool prepare(const Selection &Sel) = 0;<br>
> +  /// Run the second stage of the action that would produce the actual changes.<br>
> +  /// EXPECTS: prepare() was called and returned true.<br>
> +  virtual Expected<tooling::Replacements> apply(const Selection &Sel) = 0;<br>
> +  /// A one-line title of the action that should be shown to the users in the<br>
> +  /// UI.<br>
> +  /// EXPECTS: prepare() was called and returned true.<br>
> +  virtual std::string title() const = 0;<br>
> +};<br>
> +<br>
> +// All tweaks must be registered in the .cpp file next to their definition.<br>
> +#define REGISTER_TWEAK(Subclass)                                               \<br>
> +  ::llvm::Registry<::clang::clangd::Tweak>::Add<Subclass>                      \<br>
> +      TweakRegistrationFor##Subclass(#Subclass, /*Description=*/"");           \<br>
> +  ::clang::clangd::TweakID Subclass::id() const {                              \<br>
> +    return llvm::StringLiteral(#Subclass);                                     \<br>
> +  }<br>
> +<br>
> +/// Calls prepare() on all tweaks, returning those that can run on the<br>
> +/// selection.<br>
> +std::vector<std::unique_ptr<Tweak>> prepareTweaks(const Tweak::Selection &S);<br>
> +<br>
> +// Calls prepare() on the tweak with a given ID.<br>
> +// If prepare() returns false, returns an error.<br>
> +// If prepare() returns true, returns the corresponding tweak.<br>
> +llvm::Expected<std::unique_ptr<Tweak>> prepareTweak(TweakID ID,<br>
> +                                                    const Tweak::Selection &S);<br>
> +<br>
> +} // namespace clangd<br>
> +} // namespace clang<br>
> +<br>
> +#endif<br>
> <br>
> Added: clang-tools-extra/trunk/clangd/refactor/tweaks/CMakeLists.txt<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/refactor/tweaks/CMakeLists.txt?rev=352494&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/refactor/tweaks/CMakeLists.txt?rev=352494&view=auto</a><br>
> ==============================================================================<br>
> --- clang-tools-extra/trunk/clangd/refactor/tweaks/CMakeLists.txt (added)<br>
> +++ clang-tools-extra/trunk/clangd/refactor/tweaks/CMakeLists.txt Tue Jan 29 06:17:36 2019<br>
> @@ -0,0 +1,13 @@<br>
> +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../..)<br>
> +<br>
> +# A target containing all code tweaks (i.e. mini-refactorings) provided by<br>
> +# clangd.<br>
> +# Built as an object library to make sure linker does not remove global<br>
> +# constructors that register individual tweaks in a global registry.<br>
> +# To enable these tweaks in exectubales or shared libraries, add<br>
> +# $<TARGET_OBJECTS:obj.clangDaemonTweaks> to a list of sources, see<br>
> +# clangd/tool/CMakeLists.txt for an example.<br>
> +add_clang_library(clangDaemonTweaks OBJECT<br>
> +  Dummy.cpp # FIXME: to avoid CMake errors due to empty inputs, remove when a<br>
> +            #        first tweak lands.<br>
> +  )<br>
> <br>
> Added: clang-tools-extra/trunk/clangd/refactor/tweaks/Dummy.cpp<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/refactor/tweaks/Dummy.cpp?rev=352494&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/refactor/tweaks/Dummy.cpp?rev=352494&view=auto</a><br>
> ==============================================================================<br>
> --- clang-tools-extra/trunk/clangd/refactor/tweaks/Dummy.cpp (added)<br>
> +++ clang-tools-extra/trunk/clangd/refactor/tweaks/Dummy.cpp Tue Jan 29 06:17:36 2019<br>
> @@ -0,0 +1,9 @@<br>
> +//===--- Dummy.cpp -----------------------------------------------*- C++-*-===//<br>
> +//<br>
> +//                     The LLVM Compiler Infrastructure<br>
> +//<br>
> +// This file is distributed under the University of Illinois Open Source<br>
> +// License. See LICENSE.TXT for details.<br>
> +//<br>
> +//===----------------------------------------------------------------------===//<br>
> +// Does nothing, only here to avoid cmake errors for empty libraries.<br>
> \ No newline at end of file<br>
> <br>
> Modified: clang-tools-extra/trunk/clangd/tool/CMakeLists.txt<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/tool/CMakeLists.txt?rev=352494&r1=352493&r2=352494&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/tool/CMakeLists.txt?rev=352494&r1=352493&r2=352494&view=diff</a><br>
> ==============================================================================<br>
> --- clang-tools-extra/trunk/clangd/tool/CMakeLists.txt (original)<br>
> +++ clang-tools-extra/trunk/clangd/tool/CMakeLists.txt Tue Jan 29 06:17:36 2019<br>
> @@ -3,6 +3,7 @@ include_directories(${CMAKE_CURRENT_BINA<br>
>   <br>
>   add_clang_tool(clangd<br>
>     ClangdMain.cpp<br>
> +  $<TARGET_OBJECTS:obj.clangDaemonTweaks><br>
>     )<br>
>   <br>
>   set(LLVM_LINK_COMPONENTS<br>
> <br>
> Modified: clang-tools-extra/trunk/test/clangd/fixits-command.test<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/fixits-command.test?rev=352494&r1=352493&r2=352494&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/fixits-command.test?rev=352494&r1=352493&r2=352494&view=diff</a><br>
> ==============================================================================<br>
> --- clang-tools-extra/trunk/test/clangd/fixits-command.test (original)<br>
> +++ clang-tools-extra/trunk/test/clangd/fixits-command.test Tue Jan 29 06:17:36 2019<br>
> @@ -23,7 +23,7 @@<br>
>   # CHECK-NEXT:    "uri": "file://{{.*}}/foo.c"<br>
>   # CHECK-NEXT:  }<br>
>   ---<br>
> -{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses"}]}}}<br>
> +{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":0,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses"}]}}}<br>
>   #      CHECK:  "id": 2,<br>
>   # CHECK-NEXT:  "jsonrpc": "2.0",<br>
>   # CHECK-NEXT:  "result": [<br>
> @@ -92,7 +92,7 @@<br>
>   # CHECK-NEXT:    }<br>
>   # CHECK-NEXT:  ]<br>
>   ---<br>
> -{"jsonrpc":"2.0","id":3,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":104,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses"}]}}}<br>
> +{"jsonrpc":"2.0","id":3,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"test:///foo.c"},"range":{"start":{"line":0,"character":13},"end":{"line":0,"character":35}},"context":{"diagnostics":[{"range":{"start": {"line": 0, "character": 32}, "end": {"line": 0, "character": 37}},"severity":2,"message":"Using the result of an assignment as a condition without parentheses"}]}}}<br>
>   # Make sure unused "code" and "source" fields ignored gracefully<br>
>   #      CHECK:  "id": 3,<br>
>   # CHECK-NEXT:  "jsonrpc": "2.0",<br>
> <br>
> Modified: clang-tools-extra/trunk/test/clangd/initialize-params.test<br>
> URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/initialize-params.test?rev=352494&r1=352493&r2=352494&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/initialize-params.test?rev=352494&r1=352493&r2=352494&view=diff</a><br>
> ==============================================================================<br>
> --- clang-tools-extra/trunk/test/clangd/initialize-params.test (original)<br>
> +++ clang-tools-extra/trunk/test/clangd/initialize-params.test Tue Jan 29 06:17:36 2019<br>
> @@ -25,7 +25,8 @@<br>
>   # CHECK-NEXT:      "documentSymbolProvider": true,<br>
>   # CHECK-NEXT:      "executeCommandProvider": {<br>
>   # CHECK-NEXT:        "commands": [<br>
> -# CHECK-NEXT:          "clangd.applyFix"<br>
> +# CHECK-NEXT:          "clangd.applyFix",<br>
> +# CHECK-NEXT:          "clangd.applyTweak"<br>
>   # CHECK-NEXT:        ]<br>
>   # CHECK-NEXT:      },<br>
>   # CHECK-NEXT:      "hoverProvider": true,<br>
> <br>
> <br>
> _______________________________________________<br>
> cfe-commits mailing list<br>
> <a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
> <a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
> <br>
</blockquote></div><br clear="all"><div><br></div>-- <br><div dir="ltr" class="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div>Regards,</div><div>Ilya Biryukov</div></div></div></div></div>