[Mlir-commits] [mlir] 0b070ab - [mlir][lsp] Use rootUri and rootPath for source files (#185479)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Tue Apr 7 07:13:55 PDT 2026
Author: Jacques Pienaar
Date: 2026-04-07T14:13:47Z
New Revision: 0b070ab623647c79dfe244f42998a630299cc67a
URL: https://github.com/llvm/llvm-project/commit/0b070ab623647c79dfe244f42998a630299cc67a
DIFF: https://github.com/llvm/llvm-project/commit/0b070ab623647c79dfe244f42998a630299cc67a.diff
LOG: [mlir][lsp] Use rootUri and rootPath for source files (#185479)
When looking up source files, consider the rootUri and rootPath (if set) and filename is non local path.
Added:
mlir/test/mlir-lsp-server/rooturi_rel_path.test
Modified:
llvm/include/llvm/Support/LSP/Protocol.h
llvm/lib/Support/LSP/Protocol.cpp
mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp
mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp
mlir/lib/Tools/mlir-lsp-server/MLIRServer.h
mlir/test/mlir-lsp-server/diagnostics.test
mlir/utils/vscode/package.json
Removed:
################################################################################
diff --git a/llvm/include/llvm/Support/LSP/Protocol.h b/llvm/include/llvm/Support/LSP/Protocol.h
index a75ac291d1bcc..f4cb879f77bd3 100644
--- a/llvm/include/llvm/Support/LSP/Protocol.h
+++ b/llvm/include/llvm/Support/LSP/Protocol.h
@@ -215,6 +215,13 @@ struct InitializeParams {
/// The initial trace setting. If omitted trace is disabled ('off').
std::optional<TraceLevel> trace;
+
+ /// The root URI of the workspace. Is null if no folder is open.
+ std::optional<std::string> rootUri;
+
+ /// The root path of the workspace. Is null if no folder is open.
+ /// This is deprecated, use rootUri instead, but kept for more compatibility.
+ std::optional<std::string> rootPath;
};
/// Add support for JSON serialization.
diff --git a/llvm/lib/Support/LSP/Protocol.cpp b/llvm/lib/Support/LSP/Protocol.cpp
index 5c2379431e900..c1abdb0f4d732 100644
--- a/llvm/lib/Support/LSP/Protocol.cpp
+++ b/llvm/lib/Support/LSP/Protocol.cpp
@@ -335,6 +335,8 @@ bool llvm::lsp::fromJSON(const llvm::json::Value &Value,
// We deliberately don't fail if we can't parse individual fields.
O.map("capabilities", Result.capabilities);
O.map("trace", Result.trace);
+ O.map("rootUri", Result.rootUri);
+ O.map("rootPath", Result.rootPath);
mapOptOrNull(Value, "clientInfo", Result.clientInfo, Path);
return true;
diff --git a/mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp b/mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp
index 1bbbcdecb57af..73ebe6a6b1f09 100644
--- a/mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp
+++ b/mlir/lib/Tools/mlir-lsp-server/LSPServer.cpp
@@ -130,6 +130,17 @@ struct LSPServer {
void LSPServer::onInitialize(const InitializeParams ¶ms,
Callback<llvm::json::Value> reply) {
+ // Configure the workspace root if it was provided.
+ if (params.rootUri) {
+ llvm::Expected<URIForFile> rootURI = URIForFile::fromURI(*params.rootUri);
+ if (rootURI)
+ server.setWorkspaceRoot(rootURI->file());
+ else
+ consumeError(rootURI.takeError());
+ } else if (params.rootPath) {
+ server.setWorkspaceRoot(*params.rootPath);
+ }
+
// Send a response with the capabilities of this server.
llvm::json::Object serverCaps{
{"textDocumentSync",
diff --git a/mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp b/mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp
index 05ab4fb4f07cd..fd0d1fc1df039 100644
--- a/mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp
+++ b/mlir/lib/Tools/mlir-lsp-server/MLIRServer.cpp
@@ -19,7 +19,9 @@
#include "mlir/Tools/lsp-server-support/SourceMgrUtils.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Base64.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/LSP/Logging.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/SourceMgr.h"
#include <optional>
@@ -34,14 +36,28 @@ static SMRange convertTokenLocToRange(SMLoc loc) {
/// Returns a language server location from the given MLIR file location.
/// `uriScheme` is the scheme to use when building new uris.
-static std::optional<lsp::Location> getLocationFromLoc(StringRef uriScheme,
- FileLineColLoc loc) {
+static std::optional<lsp::Location>
+getLocationFromLoc(StringRef uriScheme, FileLineColLoc loc,
+ StringRef workspaceRoot) {
+ StringRef filename = loc.getFilename();
+ SmallString<128> absPath;
+ // Always make the path absolute. Skip paths that start with a separator:
+ // prevents incorrect resolution of virtual paths used in tests on Windows.
+ if (!llvm::sys::path::is_absolute(filename) && !filename.starts_with("/") &&
+ !filename.starts_with("\\")) {
+ if (!workspaceRoot.empty())
+ llvm::sys::path::append(absPath, workspaceRoot, filename);
+ else
+ absPath = filename;
+ llvm::sys::fs::make_absolute(absPath);
+ filename = absPath;
+ }
+
llvm::Expected<lsp::URIForFile> sourceURI =
- lsp::URIForFile::fromFile(loc.getFilename(), uriScheme);
+ lsp::URIForFile::fromFile(filename, uriScheme);
if (!sourceURI) {
llvm::lsp::Logger::error("Failed to create URI for file `{0}`: {1}",
- loc.getFilename(),
- llvm::toString(sourceURI.takeError()));
+ filename, llvm::toString(sourceURI.takeError()));
return std::nullopt;
}
@@ -57,15 +73,16 @@ static std::optional<lsp::Location> getLocationFromLoc(StringRef uriScheme,
/// present, is used to filter sub locations that do not share the same uri.
static std::optional<lsp::Location>
getLocationFromLoc(llvm::SourceMgr &sourceMgr, Location loc,
- StringRef uriScheme, const lsp::URIForFile *uri = nullptr) {
+ StringRef uriScheme, StringRef workspaceRoot,
+ const lsp::URIForFile *uri = nullptr) {
std::optional<lsp::Location> location;
loc->walk([&](Location nestedLoc) {
- FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(nestedLoc);
+ auto fileLoc = dyn_cast<FileLineColLoc>(nestedLoc);
if (!fileLoc)
return WalkResult::advance();
std::optional<lsp::Location> sourceLoc =
- getLocationFromLoc(uriScheme, fileLoc);
+ getLocationFromLoc(uriScheme, fileLoc, workspaceRoot);
if (sourceLoc && (!uri || sourceLoc->uri == *uri)) {
location = *sourceLoc;
SMLoc loc = sourceMgr.FindLocForLineAndColumn(
@@ -90,7 +107,8 @@ getLocationFromLoc(llvm::SourceMgr &sourceMgr, Location loc,
/// contained within the given URI.
static void collectLocationsFromLoc(Location loc,
std::vector<lsp::Location> &locations,
- const lsp::URIForFile &uri) {
+ const lsp::URIForFile &uri,
+ StringRef workspaceRoot) {
SetVector<Location> visitedLocs;
loc->walk([&](Location nestedLoc) {
FileLineColLoc fileLoc = dyn_cast<FileLineColLoc>(nestedLoc);
@@ -98,7 +116,7 @@ static void collectLocationsFromLoc(Location loc,
return WalkResult::advance();
std::optional<lsp::Location> sourceLoc =
- getLocationFromLoc(uri.scheme(), fileLoc);
+ getLocationFromLoc(uri.scheme(), fileLoc, workspaceRoot);
if (sourceLoc && sourceLoc->uri != uri)
locations.push_back(*sourceLoc);
return WalkResult::advance();
@@ -193,7 +211,8 @@ static void printDefBlockName(raw_ostream &os,
/// Convert the given MLIR diagnostic to the LSP form.
static lsp::Diagnostic getLspDiagnoticFromDiag(llvm::SourceMgr &sourceMgr,
Diagnostic &diag,
- const lsp::URIForFile &uri) {
+ const lsp::URIForFile &uri,
+ StringRef workspaceRoot) {
lsp::Diagnostic lspDiag;
lspDiag.source = "mlir";
@@ -205,8 +224,8 @@ static lsp::Diagnostic getLspDiagnoticFromDiag(llvm::SourceMgr &sourceMgr,
// TODO: For simplicity, we just grab the first one. It may be likely that we
// will need a more interesting heuristic here.'
StringRef uriScheme = uri.scheme();
- std::optional<lsp::Location> lspLocation =
- getLocationFromLoc(sourceMgr, diag.getLocation(), uriScheme, &uri);
+ std::optional<lsp::Location> lspLocation = getLocationFromLoc(
+ sourceMgr, diag.getLocation(), uriScheme, workspaceRoot, &uri);
if (lspLocation)
lspDiag.range = lspLocation->range;
@@ -230,8 +249,8 @@ static lsp::Diagnostic getLspDiagnoticFromDiag(llvm::SourceMgr &sourceMgr,
std::vector<llvm::lsp::DiagnosticRelatedInformation> relatedDiags;
for (Diagnostic ¬e : diag.getNotes()) {
lsp::Location noteLoc;
- if (std::optional<lsp::Location> loc =
- getLocationFromLoc(sourceMgr, note.getLocation(), uriScheme))
+ if (std::optional<lsp::Location> loc = getLocationFromLoc(
+ sourceMgr, note.getLocation(), uriScheme, workspaceRoot))
noteLoc = *loc;
else
noteLoc.uri = uri;
@@ -252,7 +271,8 @@ namespace {
/// document.
struct MLIRDocument {
MLIRDocument(MLIRContext &context, const lsp::URIForFile &uri,
- StringRef contents, std::vector<lsp::Diagnostic> &diagnostics);
+ StringRef contents, StringRef workspaceRoot,
+ std::vector<lsp::Diagnostic> &diagnostics);
MLIRDocument(const MLIRDocument &) = delete;
MLIRDocument &operator=(const MLIRDocument &) = delete;
@@ -337,14 +357,19 @@ struct MLIRDocument {
/// The source manager containing the contents of the input file.
llvm::SourceMgr sourceMgr;
+
+ /// The workspace root of the server.
+ std::string workspaceRoot;
};
} // namespace
MLIRDocument::MLIRDocument(MLIRContext &context, const lsp::URIForFile &uri,
- StringRef contents,
- std::vector<lsp::Diagnostic> &diagnostics) {
+ StringRef contents, StringRef workspaceRoot,
+ std::vector<lsp::Diagnostic> &diagnostics)
+ : workspaceRoot(workspaceRoot.str()) {
ScopedDiagnosticHandler handler(&context, [&](Diagnostic &diag) {
- diagnostics.push_back(getLspDiagnoticFromDiag(sourceMgr, diag, uri));
+ diagnostics.push_back(
+ getLspDiagnoticFromDiag(sourceMgr, diag, uri, workspaceRoot));
});
// Try to parsed the given IR string.
@@ -387,14 +412,17 @@ void MLIRDocument::getLocationsOf(const lsp::URIForFile &uri,
// Check all definitions related to operations.
for (const AsmParserState::OperationDefinition &op : asmState.getOpDefs()) {
if (contains(op.loc, posLoc))
- return collectLocationsFromLoc(op.op->getLoc(), locations, uri);
+ return collectLocationsFromLoc(op.op->getLoc(), locations, uri,
+ workspaceRoot);
for (const auto &result : op.resultGroups)
if (containsPosition(result.definition))
- return collectLocationsFromLoc(op.op->getLoc(), locations, uri);
+ return collectLocationsFromLoc(op.op->getLoc(), locations, uri,
+ workspaceRoot);
for (const auto &symUse : op.symbolUses) {
if (contains(symUse, posLoc)) {
locations.emplace_back(uri, sourceMgr, op.loc);
- return collectLocationsFromLoc(op.op->getLoc(), locations, uri);
+ return collectLocationsFromLoc(op.op->getLoc(), locations, uri,
+ workspaceRoot);
}
}
}
@@ -968,8 +996,10 @@ namespace {
struct MLIRTextFileChunk {
MLIRTextFileChunk(MLIRContext &context, uint64_t lineOffset,
const lsp::URIForFile &uri, StringRef contents,
+ StringRef workspaceRoot,
std::vector<lsp::Diagnostic> &diagnostics)
- : lineOffset(lineOffset), document(context, uri, contents, diagnostics) {}
+ : lineOffset(lineOffset),
+ document(context, uri, contents, workspaceRoot, diagnostics) {}
/// Adjust the line number of the given range to anchor at the beginning of
/// the file, instead of the beginning of this chunk.
@@ -998,6 +1028,7 @@ class MLIRTextFile {
public:
MLIRTextFile(const lsp::URIForFile &uri, StringRef fileContents,
int64_t version, lsp::DialectRegistryFn registryFn,
+ StringRef workspaceRoot,
std::vector<lsp::Diagnostic> &diagnostics);
/// Return the current version of this text file.
@@ -1047,6 +1078,7 @@ class MLIRTextFile {
MLIRTextFile::MLIRTextFile(const lsp::URIForFile &uri, StringRef fileContents,
int64_t version, lsp::DialectRegistryFn registryFn,
+ StringRef workspaceRoot,
std::vector<lsp::Diagnostic> &diagnostics)
: context(registryFn(uri), MLIRContext::Threading::DISABLED),
contents(fileContents.str()), version(version) {
@@ -1056,13 +1088,14 @@ MLIRTextFile::MLIRTextFile(const lsp::URIForFile &uri, StringRef fileContents,
SmallVector<StringRef, 8> subContents;
StringRef(contents).split(subContents, kDefaultSplitMarker);
chunks.emplace_back(std::make_unique<MLIRTextFileChunk>(
- context, /*lineOffset=*/0, uri, subContents.front(), diagnostics));
+ context, /*lineOffset=*/0, uri, subContents.front(), workspaceRoot,
+ diagnostics));
uint64_t lineOffset = subContents.front().count('\n');
for (StringRef docContents : llvm::drop_begin(subContents)) {
unsigned currentNumDiags = diagnostics.size();
- auto chunk = std::make_unique<MLIRTextFileChunk>(context, lineOffset, uri,
- docContents, diagnostics);
+ auto chunk = std::make_unique<MLIRTextFileChunk>(
+ context, lineOffset, uri, docContents, workspaceRoot, diagnostics);
lineOffset += docContents.count('\n');
// Adjust locations used in diagnostics to account for the offset from the
@@ -1271,6 +1304,9 @@ struct lsp::MLIRServer::Impl {
/// The files held by the server, mapped by their URI file name.
llvm::StringMap<std::unique_ptr<MLIRTextFile>> files;
+
+ /// The workspace root of the server.
+ std::string workspaceRoot;
};
//===----------------------------------------------------------------------===//
@@ -1284,8 +1320,9 @@ lsp::MLIRServer::~MLIRServer() = default;
void lsp::MLIRServer::addOrUpdateDocument(
const URIForFile &uri, StringRef contents, int64_t version,
std::vector<llvm::lsp::Diagnostic> &diagnostics) {
- impl->files[uri.file()] = std::make_unique<MLIRTextFile>(
- uri, contents, version, impl->registryFn, diagnostics);
+ impl->files[uri.file()] =
+ std::make_unique<MLIRTextFile>(uri, contents, version, impl->registryFn,
+ impl->workspaceRoot, diagnostics);
}
std::optional<int64_t> lsp::MLIRServer::removeDocument(const URIForFile &uri) {
@@ -1407,3 +1444,7 @@ lsp::MLIRServer::convertToBytecode(const URIForFile &uri) {
}
return fileIt->second->convertToBytecode();
}
+
+void lsp::MLIRServer::setWorkspaceRoot(StringRef root) {
+ impl->workspaceRoot = root.str();
+}
diff --git a/mlir/lib/Tools/mlir-lsp-server/MLIRServer.h b/mlir/lib/Tools/mlir-lsp-server/MLIRServer.h
index 31a01fec8bbc9..aafe36ba82e0e 100644
--- a/mlir/lib/Tools/mlir-lsp-server/MLIRServer.h
+++ b/mlir/lib/Tools/mlir-lsp-server/MLIRServer.h
@@ -87,6 +87,9 @@ class MLIRServer {
llvm::Expected<MLIRConvertBytecodeResult>
convertToBytecode(const URIForFile &uri);
+ /// Set the workspace root for the server.
+ void setWorkspaceRoot(StringRef root);
+
private:
struct Impl;
diff --git a/mlir/test/mlir-lsp-server/diagnostics.test b/mlir/test/mlir-lsp-server/diagnostics.test
index 99edd11b574f5..25c57ba531f3b 100644
--- a/mlir/test/mlir-lsp-server/diagnostics.test
+++ b/mlir/test/mlir-lsp-server/diagnostics.test
@@ -27,7 +27,7 @@
// CHECK-NEXT: "source": "mlir"
// CHECK-NEXT: }
// CHECK-NEXT: ],
-// CHECK-NEXT: "uri": "test:///foo.mlir",
+// CHECK-NEXT: "uri": "test:///{{([a-zA-Z]:/)?}}foo.mlir",
// CHECK-NEXT: "version": 1
// CHECK-NEXT: }
// -----
@@ -57,7 +57,7 @@
// CHECK-NEXT: "source": "mlir"
// CHECK-NEXT: }
// CHECK-NEXT: ],
-// CHECK-NEXT: "uri": "test:///foo.mlir",
+// CHECK-NEXT: "uri": "test:///{{([a-zA-Z]:/)?}}foo.mlir",
// CHECK-NEXT: "version": 1
// CHECK-NEXT: }
// -----
diff --git a/mlir/test/mlir-lsp-server/rooturi_rel_path.test b/mlir/test/mlir-lsp-server/rooturi_rel_path.test
new file mode 100644
index 0000000000000..14dc7c192ed9b
--- /dev/null
+++ b/mlir/test/mlir-lsp-server/rooturi_rel_path.test
@@ -0,0 +1,35 @@
+// RUN: mlir-lsp-server -lit-test < %s | FileCheck -strict-whitespace %s
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootUri":"test:///root/","capabilities":{},"trace":"off"}}
+// -----
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{
+ "uri":"test:///foo.mlir",
+ "languageId":"mlir",
+ "version":1,
+ "text":"func.func @foo() {\n %0 = arith.constant true loc(\"indexer.cc\":10:10)\n return\n}"
+}}}
+// -----
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{
+ "textDocument":{"uri":"test:///foo.mlir"},
+ "position":{"line":1,"character":12}
+}}
+// CHECK: "id": 1
+// CHECK-NEXT: "jsonrpc": "2.0",
+// CHECK-NEXT: "result": [
+// CHECK-NEXT: {
+// CHECK-NEXT: "range": {
+// CHECK-NEXT: "end": {
+// CHECK-NEXT: "character": 9,
+// CHECK-NEXT: "line": 9
+// CHECK-NEXT: },
+// CHECK-NEXT: "start": {
+// CHECK-NEXT: "character": 9,
+// CHECK-NEXT: "line": 9
+// CHECK-NEXT: }
+// CHECK-NEXT: },
+// CHECK-NEXT: "uri": "test:///{{([a-zA-Z]:/)?}}root/indexer.cc"
+// CHECK-NEXT: }
+// CHECK-NEXT: ]
+// -----
+{"jsonrpc":"2.0","id":3,"method":"shutdown"}
+// -----
+{"jsonrpc":"2.0","method":"exit"}
diff --git a/mlir/utils/vscode/package.json b/mlir/utils/vscode/package.json
index c52da0af1b18b..6b9b35a30f27d 100644
--- a/mlir/utils/vscode/package.json
+++ b/mlir/utils/vscode/package.json
@@ -2,7 +2,7 @@
"name": "vscode-mlir",
"displayName": "MLIR",
"description": "MLIR Language Extension",
- "version": "0.0.14",
+ "version": "0.0.15",
"publisher": "llvm-vs-code-extensions",
"homepage": "https://mlir.llvm.org/",
"icon": "icon.png",
More information about the Mlir-commits
mailing list