forked from OSchip/llvm-project
[mlir:LSP] Add a quickfix code action for inserting expected-* diagnostic checks
This allows for automatically inserting expected checks for parser and verifier diagnostics, which simplifies the workflow when building new dialect constructs or extending existing ones. Differential Revision: https://reviews.llvm.org/D130152
This commit is contained in:
parent
ad98ef8be4
commit
ed344c8877
|
@ -58,6 +58,16 @@ any generated diagnostics in-place.
|
|||
|
||||
![IMG](/mlir-lsp-server/diagnostics.png)
|
||||
|
||||
##### Automatically insert `expected-` diagnostic checks
|
||||
|
||||
MLIR provides
|
||||
[infrastructure](https://mlir.llvm.org/docs/Diagnostics/#sourcemgr-diagnostic-verifier-handler)
|
||||
for checking expected diagnostics, which is heavily utilized when defining IR
|
||||
parsing and verification. The language server provides code actions for
|
||||
automatically inserting the checks for diagnostics it knows about.
|
||||
|
||||
![IMG](/mlir-lsp-server/diagnostics_action.gif)
|
||||
|
||||
#### Code completion
|
||||
|
||||
The language server provides suggestions as you type, offering completions for
|
||||
|
|
|
@ -267,6 +267,10 @@ bool mlir::lsp::fromJSON(const llvm::json::Value &value,
|
|||
documentSymbol->getBoolean("hierarchicalDocumentSymbolSupport"))
|
||||
result.hierarchicalDocumentSymbol = *hierarchicalSupport;
|
||||
}
|
||||
if (auto *codeAction = textDocument->getObject("codeAction")) {
|
||||
if (codeAction->getObject("codeActionLiteralSupport"))
|
||||
result.codeActionStructure = true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -398,6 +402,12 @@ raw_ostream &mlir::lsp::operator<<(raw_ostream &os, const Range &value) {
|
|||
// Location
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool mlir::lsp::fromJSON(const llvm::json::Value &value, Location &result,
|
||||
llvm::json::Path path) {
|
||||
llvm::json::ObjectMapper o(value, path);
|
||||
return o && o.map("uri", result.uri) && o.map("range", result.range);
|
||||
}
|
||||
|
||||
llvm::json::Value mlir::lsp::toJSON(const Location &value) {
|
||||
return llvm::json::Object{
|
||||
{"uri", value.uri},
|
||||
|
@ -581,6 +591,14 @@ bool mlir::lsp::fromJSON(const llvm::json::Value &value,
|
|||
// DiagnosticRelatedInformation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool mlir::lsp::fromJSON(const llvm::json::Value &value,
|
||||
DiagnosticRelatedInformation &result,
|
||||
llvm::json::Path path) {
|
||||
llvm::json::ObjectMapper o(value, path);
|
||||
return o && o.map("location", result.location) &&
|
||||
o.map("message", result.message);
|
||||
}
|
||||
|
||||
llvm::json::Value mlir::lsp::toJSON(const DiagnosticRelatedInformation &info) {
|
||||
return llvm::json::Object{
|
||||
{"location", info.location},
|
||||
|
@ -607,6 +625,23 @@ llvm::json::Value mlir::lsp::toJSON(const Diagnostic &diag) {
|
|||
return std::move(result);
|
||||
}
|
||||
|
||||
bool mlir::lsp::fromJSON(const llvm::json::Value &value, Diagnostic &result,
|
||||
llvm::json::Path path) {
|
||||
llvm::json::ObjectMapper o(value, path);
|
||||
if (!o)
|
||||
return false;
|
||||
int severity = 0;
|
||||
if (!mapOptOrNull(value, "severity", severity, path))
|
||||
return false;
|
||||
result.severity = (DiagnosticSeverity)severity;
|
||||
|
||||
return o.map("range", result.range) && o.map("message", result.message) &&
|
||||
mapOptOrNull(value, "category", result.category, path) &&
|
||||
mapOptOrNull(value, "source", result.source, path) &&
|
||||
mapOptOrNull(value, "relatedInformation", result.relatedInformation,
|
||||
path);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// PublishDiagnosticsParams
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -892,3 +927,65 @@ llvm::raw_ostream &mlir::lsp::operator<<(llvm::raw_ostream &os,
|
|||
}
|
||||
llvm_unreachable("Unknown InlayHintKind");
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// CodeActionContext
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool mlir::lsp::fromJSON(const llvm::json::Value &value,
|
||||
CodeActionContext &result, llvm::json::Path path) {
|
||||
llvm::json::ObjectMapper o(value, path);
|
||||
if (!o || !o.map("diagnostics", result.diagnostics))
|
||||
return false;
|
||||
o.map("only", result.only);
|
||||
return true;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// CodeActionParams
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool mlir::lsp::fromJSON(const llvm::json::Value &value,
|
||||
CodeActionParams &result, llvm::json::Path path) {
|
||||
llvm::json::ObjectMapper o(value, path);
|
||||
return o && o.map("textDocument", result.textDocument) &&
|
||||
o.map("range", result.range) && o.map("context", result.context);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// WorkspaceEdit
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool mlir::lsp::fromJSON(const llvm::json::Value &value, WorkspaceEdit &result,
|
||||
llvm::json::Path path) {
|
||||
llvm::json::ObjectMapper o(value, path);
|
||||
return o && o.map("changes", result.changes);
|
||||
}
|
||||
|
||||
llvm::json::Value mlir::lsp::toJSON(const WorkspaceEdit &value) {
|
||||
llvm::json::Object fileChanges;
|
||||
for (auto &change : value.changes)
|
||||
fileChanges[change.first] = llvm::json::Array(change.second);
|
||||
return llvm::json::Object{{"changes", std::move(fileChanges)}};
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// CodeAction
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
const llvm::StringLiteral CodeAction::kQuickFix = "quickfix";
|
||||
const llvm::StringLiteral CodeAction::kRefactor = "refactor";
|
||||
const llvm::StringLiteral CodeAction::kInfo = "info";
|
||||
|
||||
llvm::json::Value mlir::lsp::toJSON(const CodeAction &value) {
|
||||
llvm::json::Object codeAction{{"title", value.title}};
|
||||
if (value.kind)
|
||||
codeAction["kind"] = *value.kind;
|
||||
if (value.diagnostics)
|
||||
codeAction["diagnostics"] = llvm::json::Array(*value.diagnostics);
|
||||
if (value.isPreferred)
|
||||
codeAction["isPreferred"] = true;
|
||||
if (value.edit)
|
||||
codeAction["edit"] = *value.edit;
|
||||
return std::move(codeAction);
|
||||
}
|
||||
|
|
|
@ -146,6 +146,10 @@ struct ClientCapabilities {
|
|||
/// Client supports hierarchical document symbols.
|
||||
/// textDocument.documentSymbol.hierarchicalDocumentSymbolSupport
|
||||
bool hierarchicalDocumentSymbol = false;
|
||||
|
||||
/// Client supports CodeAction return value for textDocument/codeAction.
|
||||
/// textDocument.codeAction.codeActionLiteralSupport.
|
||||
bool codeActionStructure = false;
|
||||
};
|
||||
|
||||
/// Add support for JSON serialization.
|
||||
|
@ -374,6 +378,8 @@ struct Location {
|
|||
};
|
||||
|
||||
/// Add support for JSON serialization.
|
||||
bool fromJSON(const llvm::json::Value &value, Location &result,
|
||||
llvm::json::Path path);
|
||||
llvm::json::Value toJSON(const Location &value);
|
||||
raw_ostream &operator<<(raw_ostream &os, const Location &value);
|
||||
|
||||
|
@ -612,6 +618,7 @@ bool fromJSON(const llvm::json::Value &value, DocumentSymbolParams &result,
|
|||
/// This should be used to point to code locations that cause or related to a
|
||||
/// diagnostics, e.g. when duplicating a symbol in a scope.
|
||||
struct DiagnosticRelatedInformation {
|
||||
DiagnosticRelatedInformation() = default;
|
||||
DiagnosticRelatedInformation(Location location, std::string message)
|
||||
: location(std::move(location)), message(std::move(message)) {}
|
||||
|
||||
|
@ -622,6 +629,8 @@ struct DiagnosticRelatedInformation {
|
|||
};
|
||||
|
||||
/// Add support for JSON serialization.
|
||||
bool fromJSON(const llvm::json::Value &value,
|
||||
DiagnosticRelatedInformation &result, llvm::json::Path path);
|
||||
llvm::json::Value toJSON(const DiagnosticRelatedInformation &info);
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -666,6 +675,8 @@ struct Diagnostic {
|
|||
|
||||
/// Add support for JSON serialization.
|
||||
llvm::json::Value toJSON(const Diagnostic &diag);
|
||||
bool fromJSON(const llvm::json::Value &value, Diagnostic &result,
|
||||
llvm::json::Path path);
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// PublishDiagnosticsParams
|
||||
|
@ -1086,6 +1097,103 @@ bool operator==(const InlayHint &lhs, const InlayHint &rhs);
|
|||
bool operator<(const InlayHint &lhs, const InlayHint &rhs);
|
||||
llvm::raw_ostream &operator<<(llvm::raw_ostream &os, InlayHintKind value);
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// CodeActionContext
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
struct CodeActionContext {
|
||||
/// An array of diagnostics known on the client side overlapping the range
|
||||
/// provided to the `textDocument/codeAction` request. They are provided so
|
||||
/// that the server knows which errors are currently presented to the user for
|
||||
/// the given range. There is no guarantee that these accurately reflect the
|
||||
/// error state of the resource. The primary parameter to compute code actions
|
||||
/// is the provided range.
|
||||
std::vector<Diagnostic> diagnostics;
|
||||
|
||||
/// Requested kind of actions to return.
|
||||
///
|
||||
/// Actions not of this kind are filtered out by the client before being
|
||||
/// shown. So servers can omit computing them.
|
||||
std::vector<std::string> only;
|
||||
};
|
||||
|
||||
/// Add support for JSON serialization.
|
||||
bool fromJSON(const llvm::json::Value &value, CodeActionContext &result,
|
||||
llvm::json::Path path);
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// CodeActionParams
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
struct CodeActionParams {
|
||||
/// The document in which the command was invoked.
|
||||
TextDocumentIdentifier textDocument;
|
||||
|
||||
/// The range for which the command was invoked.
|
||||
Range range;
|
||||
|
||||
/// Context carrying additional information.
|
||||
CodeActionContext context;
|
||||
};
|
||||
|
||||
/// Add support for JSON serialization.
|
||||
bool fromJSON(const llvm::json::Value &value, CodeActionParams &result,
|
||||
llvm::json::Path path);
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// WorkspaceEdit
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
struct WorkspaceEdit {
|
||||
/// Holds changes to existing resources.
|
||||
std::map<std::string, std::vector<TextEdit>> changes;
|
||||
|
||||
/// Note: "documentChanges" is not currently used because currently there is
|
||||
/// no support for versioned edits.
|
||||
};
|
||||
|
||||
/// Add support for JSON serialization.
|
||||
bool fromJSON(const llvm::json::Value &value, WorkspaceEdit &result,
|
||||
llvm::json::Path path);
|
||||
llvm::json::Value toJSON(const WorkspaceEdit &value);
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// CodeAction
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// A code action represents a change that can be performed in code, e.g. to fix
|
||||
/// a problem or to refactor code.
|
||||
///
|
||||
/// A CodeAction must set either `edit` and/or a `command`. If both are
|
||||
/// supplied, the `edit` is applied first, then the `command` is executed.
|
||||
struct CodeAction {
|
||||
/// A short, human-readable, title for this code action.
|
||||
std::string title;
|
||||
|
||||
/// The kind of the code action.
|
||||
/// Used to filter code actions.
|
||||
Optional<std::string> kind;
|
||||
const static llvm::StringLiteral kQuickFix;
|
||||
const static llvm::StringLiteral kRefactor;
|
||||
const static llvm::StringLiteral kInfo;
|
||||
|
||||
/// The diagnostics that this code action resolves.
|
||||
Optional<std::vector<Diagnostic>> diagnostics;
|
||||
|
||||
/// Marks this as a preferred action. Preferred actions are used by the
|
||||
/// `auto fix` command and can be targeted by keybindings.
|
||||
/// A quick fix should be marked preferred if it properly addresses the
|
||||
/// underlying error. A refactoring should be marked preferred if it is the
|
||||
/// most reasonable choice of actions to take.
|
||||
bool isPreferred = false;
|
||||
|
||||
/// The workspace edit this code action performs.
|
||||
Optional<WorkspaceEdit> edit;
|
||||
};
|
||||
|
||||
/// Add support for JSON serialization.
|
||||
llvm::json::Value toJSON(const CodeAction &);
|
||||
|
||||
} // namespace lsp
|
||||
} // namespace mlir
|
||||
|
||||
|
|
|
@ -68,6 +68,12 @@ struct LSPServer {
|
|||
void onCompletion(const CompletionParams ¶ms,
|
||||
Callback<CompletionList> reply);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Code Action
|
||||
|
||||
void onCodeAction(const CodeActionParams ¶ms,
|
||||
Callback<llvm::json::Value> reply);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Fields
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
@ -121,6 +127,16 @@ void LSPServer::onInitialize(const InitializeParams ¶ms,
|
|||
params.capabilities.hierarchicalDocumentSymbol},
|
||||
};
|
||||
|
||||
// Per LSP, codeActionProvider can be either boolean or CodeActionOptions.
|
||||
// CodeActionOptions is only valid if the client supports action literal
|
||||
// via textDocument.codeAction.codeActionLiteralSupport.
|
||||
serverCaps["codeActionProvider"] =
|
||||
params.capabilities.codeActionStructure
|
||||
? llvm::json::Object{{"codeActionKinds",
|
||||
{CodeAction::kQuickFix, CodeAction::kRefactor,
|
||||
CodeAction::kInfo}}}
|
||||
: llvm::json::Value(true);
|
||||
|
||||
llvm::json::Object result{
|
||||
{{"serverInfo",
|
||||
llvm::json::Object{{"name", "mlir-lsp-server"}, {"version", "0.0.0"}}},
|
||||
|
@ -215,6 +231,29 @@ void LSPServer::onCompletion(const CompletionParams ¶ms,
|
|||
reply(server.getCodeCompletion(params.textDocument.uri, params.position));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Code Action
|
||||
|
||||
void LSPServer::onCodeAction(const CodeActionParams ¶ms,
|
||||
Callback<llvm::json::Value> reply) {
|
||||
URIForFile uri = params.textDocument.uri;
|
||||
|
||||
// Check whether a particular CodeActionKind is included in the response.
|
||||
auto isKindAllowed = [only(params.context.only)](StringRef kind) {
|
||||
if (only.empty())
|
||||
return true;
|
||||
return llvm::any_of(only, [&](StringRef base) {
|
||||
return kind.consume_front(base) && (kind.empty() || kind.startswith("."));
|
||||
});
|
||||
};
|
||||
|
||||
// We provide a code action for fixes on the specified diagnostics.
|
||||
std::vector<CodeAction> actions;
|
||||
if (isKindAllowed(CodeAction::kQuickFix))
|
||||
server.getCodeActions(uri, params.range.start, params.context, actions);
|
||||
reply(std::move(actions));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Entry point
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -255,6 +294,10 @@ LogicalResult lsp::runMlirLSPServer(MLIRServer &server,
|
|||
messageHandler.method("textDocument/completion", &lspServer,
|
||||
&LSPServer::onCompletion);
|
||||
|
||||
// Code Action
|
||||
messageHandler.method("textDocument/codeAction", &lspServer,
|
||||
&LSPServer::onCodeAction);
|
||||
|
||||
// Diagnostics
|
||||
lspServer.publishDiagnostics =
|
||||
messageHandler.outgoingNotification<PublishDiagnosticsParams>(
|
||||
|
|
|
@ -285,6 +285,15 @@ struct MLIRDocument {
|
|||
const lsp::Position &completePos,
|
||||
const DialectRegistry ®istry);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Code Action
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
||||
void getCodeActionForDiagnostic(const lsp::URIForFile &uri,
|
||||
lsp::Position &pos, StringRef severity,
|
||||
StringRef message,
|
||||
std::vector<lsp::TextEdit> &edits);
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Fields
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
@ -796,6 +805,42 @@ MLIRDocument::getCodeCompletion(const lsp::URIForFile &uri,
|
|||
return completionList;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// MLIRDocument: Code Action
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
void MLIRDocument::getCodeActionForDiagnostic(
|
||||
const lsp::URIForFile &uri, lsp::Position &pos, StringRef severity,
|
||||
StringRef message, std::vector<lsp::TextEdit> &edits) {
|
||||
// Ignore diagnostics that print the current operation. These are always
|
||||
// enabled for the language server, but not generally during normal
|
||||
// parsing/verification.
|
||||
if (message.startswith("see current operation: "))
|
||||
return;
|
||||
|
||||
// Get the start of the line containing the diagnostic.
|
||||
const auto &buffer = sourceMgr.getBufferInfo(sourceMgr.getMainFileID());
|
||||
const char *lineStart = buffer.getPointerForLineNumber(pos.line + 1);
|
||||
if (!lineStart)
|
||||
return;
|
||||
StringRef line(lineStart, pos.character);
|
||||
|
||||
// Add a text edit for adding an expected-* diagnostic check for this
|
||||
// diagnostic.
|
||||
lsp::TextEdit edit;
|
||||
edit.range = lsp::Range(lsp::Position(pos.line, 0));
|
||||
|
||||
// Use the indent of the current line for the expected-* diagnostic.
|
||||
size_t indent = line.find_first_not_of(" ");
|
||||
if (indent == StringRef::npos)
|
||||
indent = line.size();
|
||||
|
||||
edit.newText.append(indent, ' ');
|
||||
llvm::raw_string_ostream(edit.newText)
|
||||
<< "// expected-" << severity << " @below {{" << message << "}}\n";
|
||||
edits.emplace_back(std::move(edit));
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// MLIRTextFileChunk
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -853,6 +898,9 @@ public:
|
|||
void findDocumentSymbols(std::vector<lsp::DocumentSymbol> &symbols);
|
||||
lsp::CompletionList getCodeCompletion(const lsp::URIForFile &uri,
|
||||
lsp::Position completePos);
|
||||
void getCodeActions(const lsp::URIForFile &uri, const lsp::Range &pos,
|
||||
const lsp::CodeActionContext &context,
|
||||
std::vector<lsp::CodeAction> &actions);
|
||||
|
||||
private:
|
||||
/// Find the MLIR document that contains the given position, and update the
|
||||
|
@ -1012,6 +1060,62 @@ lsp::CompletionList MLIRTextFile::getCodeCompletion(const lsp::URIForFile &uri,
|
|||
return completionList;
|
||||
}
|
||||
|
||||
void MLIRTextFile::getCodeActions(const lsp::URIForFile &uri,
|
||||
const lsp::Range &pos,
|
||||
const lsp::CodeActionContext &context,
|
||||
std::vector<lsp::CodeAction> &actions) {
|
||||
// Create actions for any diagnostics in this file.
|
||||
for (auto &diag : context.diagnostics) {
|
||||
if (diag.source != "mlir")
|
||||
continue;
|
||||
lsp::Position diagPos = diag.range.start;
|
||||
MLIRTextFileChunk &chunk = getChunkFor(diagPos);
|
||||
|
||||
// Add a new code action that inserts a "expected" diagnostic check.
|
||||
lsp::CodeAction action;
|
||||
action.title = "Add expected-* diagnostic checks";
|
||||
action.kind = lsp::CodeAction::kQuickFix.str();
|
||||
|
||||
StringRef severity;
|
||||
switch (diag.severity) {
|
||||
case lsp::DiagnosticSeverity::Error:
|
||||
severity = "error";
|
||||
break;
|
||||
case lsp::DiagnosticSeverity::Warning:
|
||||
severity = "warning";
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get edits for the diagnostic.
|
||||
std::vector<lsp::TextEdit> edits;
|
||||
chunk.document.getCodeActionForDiagnostic(uri, diagPos, severity,
|
||||
diag.message, edits);
|
||||
|
||||
// Walk the related diagnostics, this is how we encode notes.
|
||||
if (diag.relatedInformation) {
|
||||
for (auto ¬eDiag : *diag.relatedInformation) {
|
||||
if (noteDiag.location.uri != uri)
|
||||
continue;
|
||||
diagPos = noteDiag.location.range.start;
|
||||
diagPos.line -= chunk.lineOffset;
|
||||
chunk.document.getCodeActionForDiagnostic(uri, diagPos, "note",
|
||||
noteDiag.message, edits);
|
||||
}
|
||||
}
|
||||
// Fixup the locations for any edits.
|
||||
for (lsp::TextEdit &edit : edits)
|
||||
chunk.adjustLocForChunkOffset(edit.range);
|
||||
|
||||
action.edit.emplace();
|
||||
action.edit->changes[uri.uri().str()] = std::move(edits);
|
||||
action.diagnostics = {diag};
|
||||
|
||||
actions.emplace_back(std::move(action));
|
||||
}
|
||||
}
|
||||
|
||||
MLIRTextFileChunk &MLIRTextFile::getChunkFor(lsp::Position &pos) {
|
||||
if (chunks.size() == 1)
|
||||
return *chunks.front();
|
||||
|
@ -1106,3 +1210,11 @@ lsp::MLIRServer::getCodeCompletion(const URIForFile &uri,
|
|||
return fileIt->second->getCodeCompletion(uri, completePos);
|
||||
return CompletionList();
|
||||
}
|
||||
|
||||
void lsp::MLIRServer::getCodeActions(const URIForFile &uri, const Range &pos,
|
||||
const CodeActionContext &context,
|
||||
std::vector<CodeAction> &actions) {
|
||||
auto fileIt = impl->files.find(uri.file());
|
||||
if (fileIt != impl->files.end())
|
||||
fileIt->second->getCodeActions(uri, pos, context, actions);
|
||||
}
|
||||
|
|
|
@ -16,12 +16,15 @@ namespace mlir {
|
|||
class DialectRegistry;
|
||||
|
||||
namespace lsp {
|
||||
struct CodeAction;
|
||||
struct CodeActionContext;
|
||||
struct CompletionList;
|
||||
struct Diagnostic;
|
||||
struct DocumentSymbol;
|
||||
struct Hover;
|
||||
struct Location;
|
||||
struct Position;
|
||||
struct Range;
|
||||
class URIForFile;
|
||||
|
||||
/// This class implements all of the MLIR related functionality necessary for a
|
||||
|
@ -65,6 +68,11 @@ public:
|
|||
CompletionList getCodeCompletion(const URIForFile &uri,
|
||||
const Position &completePos);
|
||||
|
||||
/// Get the set of code actions within the file.
|
||||
void getCodeActions(const URIForFile &uri, const Range &pos,
|
||||
const CodeActionContext &context,
|
||||
std::vector<CodeAction> &actions);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
// RUN: mlir-lsp-server -lit-test < %s | FileCheck -strict-whitespace %s
|
||||
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"mlir","capabilities":{},"trace":"off"}}
|
||||
// -----
|
||||
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{
|
||||
"uri":"test:///foo.mlir",
|
||||
"languageId":"mlir",
|
||||
"version":1,
|
||||
"text":"#attr = 42 : f32\n// -----\nfunc.func @foo(%arg: i32) -> i64 {\nreturn %arg : i64\n}\n"
|
||||
}}}
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":1,"method":"textDocument/codeAction","params":{
|
||||
"textDocument":{
|
||||
"uri":"file:///foo.mlir"
|
||||
},
|
||||
"range":{
|
||||
"start":{"line":0,"character":8}, "end":{"line":0,"character":10}
|
||||
},
|
||||
"context":{
|
||||
"diagnostics":[{
|
||||
"range":{"start":{"line":0,"character":8}, "end":{"line":0,"character":10}},
|
||||
"message":"unexpected decimal integer literal for a floating point value",
|
||||
"severity":1,
|
||||
"relatedInformation":[{
|
||||
"message":"add a trailing dot to make the literal a float",
|
||||
"location":{
|
||||
"uri":"file:///foo.mlir",
|
||||
"range":{"start":{"line":0,"character":8}, "end":{"line":0,"character":10}}
|
||||
}
|
||||
}],
|
||||
"source":"mlir"
|
||||
}],
|
||||
"only":["quickfix"],
|
||||
"triggerKind":1
|
||||
}
|
||||
}}
|
||||
// CHECK-LABEL: "id": 1
|
||||
// CHECK-NEXT: "jsonrpc": "2.0",
|
||||
// CHECK-NEXT: "result": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "diagnostics": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "message": "unexpected decimal integer literal for a floating point value",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "character": 10,
|
||||
// CHECK-NEXT: "line": 0
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "start": {
|
||||
// CHECK-NEXT: "character": 8,
|
||||
// CHECK-NEXT: "line": 0
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "relatedInformation": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "location": {
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "character": 10,
|
||||
// CHECK-NEXT: "line": 0
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "start": {
|
||||
// CHECK-NEXT: "character": 8,
|
||||
// CHECK-NEXT: "line": 0
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "uri": "file:///foo.mlir"
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "message": "add a trailing dot to make the literal a float"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ],
|
||||
// CHECK-NEXT: "severity": 1,
|
||||
// CHECK-NEXT: "source": "mlir"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ],
|
||||
// CHECK-NEXT: "edit": {
|
||||
// CHECK-NEXT: "changes": {
|
||||
// CHECK-NEXT: "file:///foo.mlir": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-LITERAL: "newText": "// expected-error @below {{unexpected decimal integer literal for a floating point value}}\n"
|
||||
// CHECK: "range": {
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "character": 0,
|
||||
// CHECK-NEXT: "line": 0
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "start": {
|
||||
// CHECK-NEXT: "character": 0,
|
||||
// CHECK-NEXT: "line": 0
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT-LITERAL: "newText": "// expected-note @below {{add a trailing dot to make the literal a float}}\n",
|
||||
// CHECK: "range": {
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "character": 0,
|
||||
// CHECK-NEXT: "line": 0
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "start": {
|
||||
// CHECK-NEXT: "character": 0,
|
||||
// CHECK-NEXT: "line": 0
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "kind": "quickfix",
|
||||
// CHECK-NEXT: "title": "Add expected-* diagnostic checks"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":2,"method":"textDocument/codeAction","params":{
|
||||
"textDocument":{"uri":"file:///foo.mlir"},
|
||||
"range":{"start":{"line":3,"character":9},"end":{"line":3,"character":13}},
|
||||
"context":{
|
||||
"diagnostics":[{
|
||||
"range":{"start":{"line":3,"character":9},"end":{"line":3,"character":13}},
|
||||
"message":"use of value '%arg' expects different type than prior uses: 'i64' vs 'i32'",
|
||||
"severity":1,
|
||||
"relatedInformation":[{
|
||||
"message":"prior use here",
|
||||
"location":{
|
||||
"uri":"file:///foo.mlir",
|
||||
"range":{"start":{"line":2,"character":15},"end":{"line":2,"character":19}}
|
||||
}
|
||||
}],
|
||||
"source":"mlir"
|
||||
}],
|
||||
"only":["quickfix"],
|
||||
"triggerKind":1
|
||||
}
|
||||
}}
|
||||
// CHECK-LABEL: "id": 2
|
||||
// CHECK-NEXT: "jsonrpc": "2.0",
|
||||
// CHECK-NEXT: "result": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK: "edit": {
|
||||
// CHECK-NEXT: "changes": {
|
||||
// CHECK-NEXT: "file:///foo.mlir": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT-LITERAL: "newText": "// expected-error @below {{use of value '%arg' expects different type than prior uses: 'i64' vs 'i32'}}\n",
|
||||
// CHECK: "range": {
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "character": 0,
|
||||
// CHECK-NEXT: "line": 3
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "start": {
|
||||
// CHECK-NEXT: "character": 0,
|
||||
// CHECK-NEXT: "line": 3
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT-LITERAL: "newText": "// expected-note @below {{prior use here}}\n",
|
||||
// CHECK: "range": {
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "character": 0,
|
||||
// CHECK-NEXT: "line": 2
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "start": {
|
||||
// CHECK-NEXT: "character": 0,
|
||||
// CHECK-NEXT: "line": 2
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "kind": "quickfix",
|
||||
// CHECK-NEXT: "title": "Add expected-* diagnostic checks"
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// -----
|
||||
{"jsonrpc":"2.0","id":10,"method":"shutdown"}
|
||||
// -----
|
||||
{"jsonrpc":"2.0","method":"exit"}
|
|
@ -5,6 +5,7 @@
|
|||
// CHECK-NEXT: "jsonrpc": "2.0",
|
||||
// CHECK-NEXT: "result": {
|
||||
// CHECK-NEXT: "capabilities": {
|
||||
// CHECK-NEXT: "codeActionProvider": true,
|
||||
// CHECK-NEXT: "completionProvider": {
|
||||
// CHECK-NEXT: "allCommitCharacters": [
|
||||
// CHECK: ],
|
||||
|
|
Loading…
Reference in New Issue