20#include "llvm/ADT/StringExtras.h"
21#include "llvm/Support/Base64.h"
22#include "llvm/Support/FileSystem.h"
23#include "llvm/Support/LSP/Logging.h"
24#include "llvm/Support/Path.h"
25#include "llvm/Support/SourceMgr.h"
44 if (value >
static_cast<unsigned>(std::numeric_limits<int>::max()))
46 return static_cast<int>(value);
51static std::optional<lsp::Location>
53 StringRef workspaceRoot) {
58 if (!llvm::sys::path::is_absolute(filename) && !filename.starts_with(
"/") &&
59 !filename.starts_with(
"\\")) {
60 if (!workspaceRoot.empty())
61 llvm::sys::path::append(absPath, workspaceRoot, filename);
64 llvm::sys::fs::make_absolute(absPath);
69 lsp::URIForFile::fromFile(filename, uriScheme);
71 llvm::lsp::Logger::error(
"Failed to create URI for file `{0}`: {1}",
72 filename, llvm::toString(sourceURI.takeError()));
78 if (!line || !character)
81 lsp::Position position;
82 position.line = *line;
83 position.character = *character;
84 return lsp::Location{*sourceURI, lsp::Range(position)};
91static std::optional<lsp::Location>
93 StringRef uriScheme, StringRef workspaceRoot,
94 const lsp::URIForFile *uri =
nullptr) {
95 std::optional<lsp::Location> location;
97 auto fileLoc = dyn_cast<FileLineColLoc>(nestedLoc);
101 std::optional<lsp::Location> sourceLoc =
103 if (sourceLoc && (!uri || sourceLoc->uri == *uri)) {
104 location = *sourceLoc;
105 SMLoc loc = sourceMgr.FindLocForLineAndColumn(
106 sourceMgr.getMainFileID(), fileLoc.getLine(), fileLoc.getColumn());
110 if (location->range.end.character < std::numeric_limits<int>::max())
111 ++location->range.end.character;
114 auto lineCol = sourceMgr.getLineAndColumn(range.End);
115 uint64_t endCharacter = std::max<uint64_t>(
116 static_cast<uint64_t
>(fileLoc.getColumn()) + 1, lineCol.second - 1);
117 location->range.end.character =
static_cast<int>(
118 std::min<uint64_t>(endCharacter, std::numeric_limits<int>::max()));
130 std::vector<lsp::Location> &locations,
131 const lsp::URIForFile &uri,
132 StringRef workspaceRoot) {
136 if (!fileLoc || !visitedLocs.insert(nestedLoc))
139 std::optional<lsp::Location> sourceLoc =
141 if (sourceLoc && sourceLoc->uri != uri)
142 locations.push_back(*sourceLoc);
151 return range.Start.getPointer() <= loc.getPointer() &&
152 loc.getPointer() <= range.End.getPointer();
159 SMRange *overlappedRange =
nullptr) {
163 *overlappedRange = def.
loc;
168 const auto *useIt = llvm::find_if(
169 def.
uses, [&](
const SMRange &range) { return contains(range, loc); });
170 if (useIt != def.
uses.end()) {
172 *overlappedRange = *useIt;
182 auto isIdentifierChar = [](
char c) {
183 return isalnum(c) || c ==
'%' || c ==
'$' || c ==
'.' || c ==
'_' ||
186 const char *curPtr = loc.getPointer();
187 while (isIdentifierChar(*curPtr))
196 const char *numberStart = ++curPtr;
197 while (llvm::isDigit(*curPtr))
199 StringRef numberStr(numberStart, curPtr - numberStart);
200 unsigned resultNumber = 0;
201 return numberStr.consumeInteger(10, resultNumber) ? std::optional<unsigned>()
208 if (!range.isValid())
210 const char *startPtr = range.Start.getPointer();
211 return StringRef(startPtr, range.End.getPointer() - startPtr);
219 if (text && text->starts_with(
"^")) {
235 const lsp::URIForFile &uri,
236 StringRef workspaceRoot) {
237 lsp::Diagnostic lspDiag;
238 lspDiag.source =
"mlir";
242 lspDiag.category =
"Parse Error";
247 StringRef uriScheme = uri.scheme();
249 sourceMgr,
diag.getLocation(), uriScheme, workspaceRoot, &uri);
251 lspDiag.range = lspLocation->range;
254 switch (
diag.getSeverity()) {
256 llvm_unreachable(
"expected notes to be handled separately");
258 lspDiag.severity = llvm::lsp::DiagnosticSeverity::Warning;
261 lspDiag.severity = llvm::lsp::DiagnosticSeverity::Error;
264 lspDiag.severity = llvm::lsp::DiagnosticSeverity::Information;
267 lspDiag.message =
diag.str();
270 std::vector<llvm::lsp::DiagnosticRelatedInformation> relatedDiags;
272 lsp::Location noteLoc;
274 sourceMgr, note.getLocation(), uriScheme, workspaceRoot))
278 relatedDiags.emplace_back(noteLoc, note.str());
280 if (!relatedDiags.empty())
281 lspDiag.relatedInformation = std::move(relatedDiags);
294 MLIRDocument(MLIRContext &context,
const lsp::URIForFile &uri,
295 StringRef contents, StringRef workspaceRoot,
296 std::vector<lsp::Diagnostic> &diagnostics);
297 MLIRDocument(
const MLIRDocument &) =
delete;
298 MLIRDocument &operator=(
const MLIRDocument &) =
delete;
304 void getLocationsOf(
const lsp::URIForFile &uri,
const lsp::Position &defPos,
305 std::vector<lsp::Location> &locations);
306 void findReferencesOf(
const lsp::URIForFile &uri,
const lsp::Position &pos,
307 std::vector<lsp::Location> &references);
313 std::optional<lsp::Hover> findHover(
const lsp::URIForFile &uri,
314 const lsp::Position &hoverPos);
315 std::optional<lsp::Hover>
316 buildHoverForOperation(SMRange hoverRange,
317 const AsmParserState::OperationDefinition &op);
318 lsp::Hover buildHoverForOperationResult(SMRange hoverRange, Operation *op,
319 unsigned resultStart,
320 unsigned resultEnd, SMLoc posLoc);
321 lsp::Hover buildHoverForBlock(SMRange hoverRange,
322 const AsmParserState::BlockDefinition &block);
324 buildHoverForBlockArgument(SMRange hoverRange, BlockArgument arg,
325 const AsmParserState::BlockDefinition &block);
327 lsp::Hover buildHoverForAttributeAlias(
328 SMRange hoverRange,
const AsmParserState::AttributeAliasDefinition &attr);
330 buildHoverForTypeAlias(SMRange hoverRange,
331 const AsmParserState::TypeAliasDefinition &type);
337 void findDocumentSymbols(std::vector<lsp::DocumentSymbol> &symbols);
338 void findDocumentSymbols(Operation *op,
339 std::vector<lsp::DocumentSymbol> &symbols);
345 lsp::CompletionList getCodeCompletion(
const lsp::URIForFile &uri,
346 const lsp::Position &completePos,
347 const DialectRegistry ®istry);
353 void getCodeActionForDiagnostic(
const lsp::URIForFile &uri,
354 lsp::Position &pos, StringRef severity,
356 std::vector<llvm::lsp::TextEdit> &edits);
362 llvm::Expected<lsp::MLIRConvertBytecodeResult> convertToBytecode();
370 AsmParserState asmState;
377 FallbackAsmResourceMap fallbackResourceMap;
380 llvm::SourceMgr sourceMgr;
383 std::string workspaceRoot;
387MLIRDocument::MLIRDocument(
MLIRContext &context,
const lsp::URIForFile &uri,
388 StringRef contents, StringRef workspaceRoot,
389 std::vector<lsp::Diagnostic> &diagnostics)
390 : workspaceRoot(workspaceRoot.str()) {
392 diagnostics.push_back(
397 auto memBuffer = llvm::MemoryBuffer::getMemBufferCopy(contents, uri.file());
399 llvm::lsp::Logger::error(
"Failed to create memory buffer for file",
405 &fallbackResourceMap);
406 sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
420void MLIRDocument::getLocationsOf(
const lsp::URIForFile &uri,
421 const lsp::Position &defPos,
422 std::vector<lsp::Location> &locations) {
423 SMLoc posLoc = defPos.getAsSMLoc(sourceMgr);
426 auto containsPosition = [&](
const AsmParserState::SMDefinition &def) {
429 locations.emplace_back(uri, sourceMgr, def.loc);
434 for (
const AsmParserState::OperationDefinition &op : asmState.
getOpDefs()) {
438 for (
const auto &
result : op.resultGroups)
439 if (containsPosition(
result.definition))
442 for (
const auto &symUse : op.symbolUses) {
444 locations.emplace_back(uri, sourceMgr, op.loc);
452 for (
const AsmParserState::BlockDefinition &block : asmState.
getBlockDefs()) {
453 if (containsPosition(block.definition))
455 for (
const AsmParserState::SMDefinition &arg : block.arguments)
456 if (containsPosition(arg))
461 for (
const AsmParserState::AttributeAliasDefinition &attr :
463 if (containsPosition(attr.definition))
466 for (
const AsmParserState::TypeAliasDefinition &type :
468 if (containsPosition(type.definition))
473void MLIRDocument::findReferencesOf(
const lsp::URIForFile &uri,
474 const lsp::Position &pos,
475 std::vector<lsp::Location> &references) {
478 auto appendSMDef = [&](
const AsmParserState::SMDefinition &def) {
479 references.emplace_back(uri, sourceMgr, def.loc);
480 for (
const SMRange &use : def.uses)
481 references.emplace_back(uri, sourceMgr, use);
484 SMLoc posLoc = pos.getAsSMLoc(sourceMgr);
487 for (
const AsmParserState::OperationDefinition &op : asmState.
getOpDefs()) {
489 for (
const auto &
result : op.resultGroups)
490 appendSMDef(
result.definition);
491 for (
const auto &symUse : op.symbolUses)
493 references.emplace_back(uri, sourceMgr, symUse);
496 for (
const auto &
result : op.resultGroups)
498 return appendSMDef(
result.definition);
499 for (
const auto &symUse : op.symbolUses) {
502 for (
const auto &symUse : op.symbolUses)
503 references.emplace_back(uri, sourceMgr, symUse);
509 for (
const AsmParserState::BlockDefinition &block : asmState.
getBlockDefs()) {
511 return appendSMDef(block.definition);
513 for (
const AsmParserState::SMDefinition &arg : block.arguments)
515 return appendSMDef(arg);
519 for (
const AsmParserState::AttributeAliasDefinition &attr :
522 return appendSMDef(attr.definition);
524 for (
const AsmParserState::TypeAliasDefinition &type :
527 return appendSMDef(type.definition);
535std::optional<lsp::Hover>
536MLIRDocument::findHover(
const lsp::URIForFile &uri,
537 const lsp::Position &hoverPos) {
538 SMLoc posLoc = hoverPos.getAsSMLoc(sourceMgr);
542 for (
const AsmParserState::OperationDefinition &op : asmState.
getOpDefs()) {
545 return buildHoverForOperation(op.loc, op);
548 for (
auto &use : op.symbolUses)
550 return buildHoverForOperation(use, op);
553 for (
unsigned i = 0, e = op.resultGroups.size(); i < e; ++i) {
554 const auto &
result = op.resultGroups[i];
559 unsigned resultStart =
result.startIndex;
560 unsigned resultEnd = (i == e - 1) ? op.op->getNumResults()
561 : op.resultGroups[i + 1].startIndex;
562 return buildHoverForOperationResult(hoverRange, op.op, resultStart,
568 for (
const AsmParserState::BlockDefinition &block : asmState.
getBlockDefs()) {
569 if (
isDefOrUse(block.definition, posLoc, &hoverRange))
570 return buildHoverForBlock(hoverRange, block);
572 for (
const auto &arg : llvm::enumerate(block.arguments)) {
573 if (!
isDefOrUse(arg.value(), posLoc, &hoverRange))
576 return buildHoverForBlockArgument(
577 hoverRange, block.block->
getArgument(arg.index()), block);
582 for (
const AsmParserState::AttributeAliasDefinition &attr :
584 if (
isDefOrUse(attr.definition, posLoc, &hoverRange))
585 return buildHoverForAttributeAlias(hoverRange, attr);
587 for (
const AsmParserState::TypeAliasDefinition &type :
589 if (
isDefOrUse(type.definition, posLoc, &hoverRange))
590 return buildHoverForTypeAlias(hoverRange, type);
596std::optional<lsp::Hover> MLIRDocument::buildHoverForOperation(
597 SMRange hoverRange,
const AsmParserState::OperationDefinition &op) {
598 lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
599 llvm::raw_string_ostream os(hover.contents.value);
603 if (SymbolOpInterface symbol = dyn_cast<SymbolOpInterface>(op.
op))
604 os <<
" : " << symbol.getVisibility() <<
" @" << symbol.getName() <<
"";
607 os <<
"Generic Form:\n\n```mlir\n";
609 op.
op->
print(os, OpPrintingFlags()
610 .printGenericOpForm()
611 .elideLargeElementsAttrs()
618lsp::Hover MLIRDocument::buildHoverForOperationResult(SMRange hoverRange,
620 unsigned resultStart,
623 lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
624 llvm::raw_string_ostream os(hover.contents.value);
627 os <<
"Operation: \"" << op->
getName() <<
"\"\n\n";
632 if ((resultStart + *resultNumber) < resultEnd) {
633 resultStart += *resultNumber;
634 resultEnd = resultStart + 1;
639 if ((resultStart + 1) == resultEnd) {
640 os <<
"Result #" << resultStart <<
"\n\n"
643 os <<
"Result #[" << resultStart <<
", " << (resultEnd - 1) <<
"]\n\n"
645 llvm::interleaveComma(
646 op->
getResults().slice(resultStart, resultEnd), os,
647 [&](Value
result) { os <<
"`" << result.getType() <<
"`"; });
654MLIRDocument::buildHoverForBlock(SMRange hoverRange,
655 const AsmParserState::BlockDefinition &block) {
656 lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
657 llvm::raw_string_ostream os(hover.contents.value);
660 auto printBlockToHover = [&](
Block *newBlock) {
661 if (
const auto *def = asmState.
getBlockDef(newBlock))
671 os <<
"Predecessors: ";
677 os <<
"Successors: ";
685lsp::Hover MLIRDocument::buildHoverForBlockArgument(
686 SMRange hoverRange, BlockArgument arg,
687 const AsmParserState::BlockDefinition &block) {
688 lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
689 llvm::raw_string_ostream os(hover.contents.value);
696 <<
"Type: `" << arg.
getType() <<
"`\n\n";
701lsp::Hover MLIRDocument::buildHoverForAttributeAlias(
702 SMRange hoverRange,
const AsmParserState::AttributeAliasDefinition &attr) {
703 lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
704 llvm::raw_string_ostream os(hover.contents.value);
706 os <<
"Attribute Alias: \"" << attr.
name <<
"\n\n";
707 os <<
"Value: ```mlir\n" << attr.
value <<
"\n```\n\n";
712lsp::Hover MLIRDocument::buildHoverForTypeAlias(
713 SMRange hoverRange,
const AsmParserState::TypeAliasDefinition &type) {
714 lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
715 llvm::raw_string_ostream os(hover.contents.value);
717 os <<
"Type Alias: \"" << type.
name <<
"\n\n";
718 os <<
"Value: ```mlir\n" << type.
value <<
"\n```\n\n";
727void MLIRDocument::findDocumentSymbols(
728 std::vector<lsp::DocumentSymbol> &symbols) {
729 for (Operation &op : parsedIR)
730 findDocumentSymbols(&op, symbols);
733void MLIRDocument::findDocumentSymbols(
734 Operation *op, std::vector<lsp::DocumentSymbol> &symbols) {
735 std::vector<lsp::DocumentSymbol> *childSymbols = &symbols;
738 if (
const AsmParserState::OperationDefinition *def = asmState.
getOpDef(op)) {
740 if (SymbolOpInterface symbol = dyn_cast<SymbolOpInterface>(op)) {
741 symbols.emplace_back(symbol.getName(),
742 isa<FunctionOpInterface>(op)
743 ? llvm::lsp::SymbolKind::Function
744 : llvm::lsp::SymbolKind::Class,
745 lsp::Range(sourceMgr, def->scopeLoc),
746 lsp::Range(sourceMgr, def->loc));
747 childSymbols = &symbols.back().children;
749 }
else if (op->
hasTrait<OpTrait::SymbolTable>()) {
752 llvm::lsp::SymbolKind::Namespace,
753 llvm::lsp::Range(sourceMgr, def->scopeLoc),
754 llvm::lsp::Range(sourceMgr, def->loc));
755 childSymbols = &symbols.back().children;
763 for (Operation &childOp : region.getOps())
764 findDocumentSymbols(&childOp, *childSymbols);
772class LSPCodeCompleteContext :
public AsmParserCodeCompleteContext {
774 LSPCodeCompleteContext(SMLoc completeLoc, lsp::CompletionList &completionList,
776 : AsmParserCodeCompleteContext(completeLoc),
777 completionList(completionList), ctx(ctx) {}
780 void completeDialectName(StringRef prefix)
final {
782 llvm::lsp::CompletionItem item(prefix + dialect,
783 llvm::lsp::CompletionItemKind::Module,
785 item.detail =
"dialect";
786 completionList.items.emplace_back(item);
792 void completeOperationName(StringRef dialectName)
final {
801 llvm::lsp::CompletionItem item(
802 op.getStringRef().drop_front(dialectName.size() + 1),
803 llvm::lsp::CompletionItemKind::Field,
805 item.detail =
"operation";
806 completionList.items.emplace_back(item);
812 void appendSSAValueCompletion(StringRef name, std::string typeData)
final {
814 bool stripPrefix = getCodeCompleteLoc().getPointer()[-1] ==
'%';
816 llvm::lsp::CompletionItem item(name,
817 llvm::lsp::CompletionItemKind::Variable);
819 item.insertText = name.drop_front(1).str();
820 item.detail = std::move(typeData);
821 completionList.items.emplace_back(item);
826 void appendBlockCompletion(StringRef name)
final {
828 bool stripPrefix = getCodeCompleteLoc().getPointer()[-1] ==
'^';
830 llvm::lsp::CompletionItem item(name, llvm::lsp::CompletionItemKind::Field);
832 item.insertText = name.drop_front(1).str();
833 completionList.items.emplace_back(item);
837 void completeExpectedTokens(ArrayRef<StringRef> tokens,
bool optional)
final {
838 for (StringRef token : tokens) {
839 llvm::lsp::CompletionItem item(token,
840 llvm::lsp::CompletionItemKind::Keyword,
842 item.detail = optional ?
"optional" :
"";
843 completionList.items.emplace_back(item);
848 void completeAttribute(
const llvm::StringMap<Attribute> &aliases)
override {
849 appendSimpleCompletions({
"affine_set",
"affine_map",
"dense",
850 "dense_resource",
"false",
"loc",
"sparse",
"true",
852 llvm::lsp::CompletionItemKind::Field,
855 completeDialectName(
"#");
856 completeAliases(aliases,
"#");
858 void completeDialectAttributeOrAlias(
859 const llvm::StringMap<Attribute> &aliases)
override {
860 completeDialectName();
861 completeAliases(aliases);
865 void completeType(
const llvm::StringMap<Type> &aliases)
override {
867 appendSimpleCompletions({
"memref",
"tensor",
"complex",
"tuple",
"vector",
868 "bf16",
"f16",
"f32",
"f64",
"f80",
"f128",
870 llvm::lsp::CompletionItemKind::Field,
874 for (StringRef type : {
"i",
"si",
"ui"}) {
875 llvm::lsp::CompletionItem item(type +
"<N>",
876 llvm::lsp::CompletionItemKind::Field,
878 item.insertText = type.str();
879 completionList.items.emplace_back(item);
883 completeDialectName(
"!");
884 completeAliases(aliases,
"!");
887 completeDialectTypeOrAlias(
const llvm::StringMap<Type> &aliases)
override {
888 completeDialectName();
889 completeAliases(aliases);
893 template <
typename T>
894 void completeAliases(
const llvm::StringMap<T> &aliases,
895 StringRef prefix =
"") {
896 for (
const auto &alias : aliases) {
897 llvm::lsp::CompletionItem item(prefix + alias.getKey(),
898 llvm::lsp::CompletionItemKind::Field,
900 llvm::raw_string_ostream(item.detail) <<
"alias: " << alias.getValue();
901 completionList.items.emplace_back(item);
906 void appendSimpleCompletions(ArrayRef<StringRef> completions,
907 llvm::lsp::CompletionItemKind kind,
908 StringRef sortText =
"") {
909 for (StringRef completion : completions)
910 completionList.items.emplace_back(completion, kind, sortText);
914 lsp::CompletionList &completionList;
920MLIRDocument::getCodeCompletion(
const lsp::URIForFile &uri,
921 const lsp::Position &completePos,
922 const DialectRegistry ®istry) {
923 SMLoc posLoc = completePos.getAsSMLoc(sourceMgr);
924 if (!posLoc.isValid())
925 return lsp::CompletionList();
929 MLIRContext tmpContext(registry, MLIRContext::Threading::DISABLED);
930 tmpContext.allowUnregisteredDialects();
931 lsp::CompletionList completionList;
932 LSPCodeCompleteContext lspCompleteContext(posLoc, completionList,
936 AsmParserState tmpState;
938 &lspCompleteContext);
939 return completionList;
946void MLIRDocument::getCodeActionForDiagnostic(
947 const lsp::URIForFile &uri, lsp::Position &pos, StringRef severity,
948 StringRef message, std::vector<llvm::lsp::TextEdit> &edits) {
952 if (message.starts_with(
"see current operation: "))
956 const auto &buffer = sourceMgr.getBufferInfo(sourceMgr.getMainFileID());
957 const char *lineStart = buffer.getPointerForLineNumber(pos.line + 1);
960 StringRef line(lineStart, pos.character);
964 llvm::lsp::TextEdit edit;
965 edit.range = lsp::Range(lsp::Position(pos.line, 0));
968 size_t indent = line.find_first_not_of(
' ');
969 if (indent == StringRef::npos)
970 indent = line.size();
972 edit.newText.append(indent,
' ');
973 llvm::raw_string_ostream(edit.newText)
974 <<
"// expected-" << severity <<
" @below {{" << message <<
"}}\n";
975 edits.emplace_back(std::move(edit));
982llvm::Expected<lsp::MLIRConvertBytecodeResult>
983MLIRDocument::convertToBytecode() {
986 if (!llvm::hasSingleElement(parsedIR)) {
987 if (parsedIR.
empty()) {
988 return llvm::make_error<llvm::lsp::LSPError>(
989 "expected a single and valid top-level operation, please ensure "
990 "there are no errors",
991 llvm::lsp::ErrorCode::RequestFailed);
993 return llvm::make_error<llvm::lsp::LSPError>(
994 "expected a single top-level operation",
995 llvm::lsp::ErrorCode::RequestFailed);
998 lsp::MLIRConvertBytecodeResult
result;
1000 BytecodeWriterConfig writerConfig(fallbackResourceMap);
1002 std::string rawBytecodeBuffer;
1003 llvm::raw_string_ostream os(rawBytecodeBuffer);
1006 result.output = llvm::encodeBase64(rawBytecodeBuffer);
1017struct MLIRTextFileChunk {
1018 MLIRTextFileChunk(MLIRContext &context, uint64_t lineOffset,
1019 const lsp::URIForFile &uri, StringRef contents,
1020 StringRef workspaceRoot,
1021 std::vector<lsp::Diagnostic> &diagnostics)
1022 : lineOffset(lineOffset),
1023 document(context, uri, contents, workspaceRoot, diagnostics) {}
1027 void adjustLocForChunkOffset(lsp::Range &range) {
1028 adjustLocForChunkOffset(range.start);
1029 adjustLocForChunkOffset(range.end);
1033 void adjustLocForChunkOffset(lsp::Position &pos) { pos.line += lineOffset; }
1036 uint64_t lineOffset;
1038 MLIRDocument document;
1050 MLIRTextFile(
const lsp::URIForFile &uri, StringRef fileContents,
1052 StringRef workspaceRoot,
1053 std::vector<lsp::Diagnostic> &diagnostics);
1056 int64_t getVersion()
const {
return version; }
1062 void getLocationsOf(
const lsp::URIForFile &uri, lsp::Position defPos,
1063 std::vector<lsp::Location> &locations);
1064 void findReferencesOf(
const lsp::URIForFile &uri, lsp::Position pos,
1065 std::vector<lsp::Location> &references);
1066 std::optional<lsp::Hover> findHover(
const lsp::URIForFile &uri,
1067 lsp::Position hoverPos);
1068 void findDocumentSymbols(std::vector<lsp::DocumentSymbol> &symbols);
1069 lsp::CompletionList getCodeCompletion(
const lsp::URIForFile &uri,
1070 lsp::Position completePos);
1071 void getCodeActions(
const lsp::URIForFile &uri,
const lsp::Range &pos,
1072 const lsp::CodeActionContext &context,
1073 std::vector<lsp::CodeAction> &actions);
1074 llvm::Expected<lsp::MLIRConvertBytecodeResult> convertToBytecode();
1080 MLIRTextFileChunk &getChunkFor(lsp::Position &pos);
1083 MLIRContext context;
1086 std::string contents;
1092 int64_t totalNumLines = 0;
1096 std::vector<std::unique_ptr<MLIRTextFileChunk>> chunks;
1100MLIRTextFile::MLIRTextFile(
const lsp::URIForFile &uri, StringRef fileContents,
1102 StringRef workspaceRoot,
1103 std::vector<lsp::Diagnostic> &diagnostics)
1104 : context(registryFn(uri), MLIRContext::Threading::
DISABLED),
1105 contents(fileContents.str()), version(version) {
1111 chunks.emplace_back(std::make_unique<MLIRTextFileChunk>(
1112 context, 0, uri, subContents.front(), workspaceRoot,
1115 uint64_t lineOffset = subContents.front().count(
'\n');
1116 for (StringRef docContents : llvm::drop_begin(subContents)) {
1117 unsigned currentNumDiags = diagnostics.size();
1118 auto chunk = std::make_unique<MLIRTextFileChunk>(
1119 context, lineOffset, uri, docContents, workspaceRoot, diagnostics);
1120 lineOffset += docContents.count(
'\n');
1124 for (lsp::Diagnostic &
diag :
1125 llvm::drop_begin(diagnostics, currentNumDiags)) {
1126 chunk->adjustLocForChunkOffset(
diag.range);
1128 if (!
diag.relatedInformation)
1130 for (
auto &it : *
diag.relatedInformation)
1131 if (it.location.uri == uri)
1132 chunk->adjustLocForChunkOffset(it.location.range);
1134 chunks.emplace_back(std::move(chunk));
1136 totalNumLines = lineOffset;
1139void MLIRTextFile::getLocationsOf(
const lsp::URIForFile &uri,
1140 lsp::Position defPos,
1141 std::vector<lsp::Location> &locations) {
1142 MLIRTextFileChunk &chunk = getChunkFor(defPos);
1143 chunk.document.getLocationsOf(uri, defPos, locations);
1146 if (chunk.lineOffset == 0)
1148 for (lsp::Location &loc : locations)
1150 chunk.adjustLocForChunkOffset(loc.range);
1153void MLIRTextFile::findReferencesOf(
const lsp::URIForFile &uri,
1155 std::vector<lsp::Location> &references) {
1156 MLIRTextFileChunk &chunk = getChunkFor(pos);
1157 chunk.document.findReferencesOf(uri, pos, references);
1160 if (chunk.lineOffset == 0)
1162 for (lsp::Location &loc : references)
1164 chunk.adjustLocForChunkOffset(loc.range);
1167std::optional<lsp::Hover> MLIRTextFile::findHover(
const lsp::URIForFile &uri,
1168 lsp::Position hoverPos) {
1169 MLIRTextFileChunk &chunk = getChunkFor(hoverPos);
1170 std::optional<lsp::Hover> hoverInfo = chunk.document.findHover(uri, hoverPos);
1173 if (chunk.lineOffset != 0 && hoverInfo && hoverInfo->range)
1174 chunk.adjustLocForChunkOffset(*hoverInfo->range);
1178void MLIRTextFile::findDocumentSymbols(
1179 std::vector<lsp::DocumentSymbol> &symbols) {
1180 if (chunks.size() == 1)
1181 return chunks.front()->document.findDocumentSymbols(symbols);
1185 for (
unsigned i = 0, e = chunks.size(); i < e; ++i) {
1186 MLIRTextFileChunk &chunk = *chunks[i];
1187 lsp::Position startPos(chunk.lineOffset);
1188 lsp::Position endPos((i == e - 1) ? totalNumLines - 1
1189 : chunks[i + 1]->lineOffset);
1190 lsp::DocumentSymbol symbol(
"<file-split-" + Twine(i) +
">",
1191 llvm::lsp::SymbolKind::Namespace,
1192 lsp::Range(startPos, endPos),
1193 lsp::Range(startPos));
1194 chunk.document.findDocumentSymbols(symbol.children);
1198 SmallVector<lsp::DocumentSymbol *> symbolsToFix;
1199 for (lsp::DocumentSymbol &childSymbol : symbol.children)
1200 symbolsToFix.push_back(&childSymbol);
1202 while (!symbolsToFix.empty()) {
1203 lsp::DocumentSymbol *symbol = symbolsToFix.pop_back_val();
1204 chunk.adjustLocForChunkOffset(symbol->range);
1205 chunk.adjustLocForChunkOffset(symbol->selectionRange);
1207 for (lsp::DocumentSymbol &childSymbol : symbol->children)
1208 symbolsToFix.push_back(&childSymbol);
1213 symbols.emplace_back(std::move(symbol));
1217lsp::CompletionList MLIRTextFile::getCodeCompletion(
const lsp::URIForFile &uri,
1218 lsp::Position completePos) {
1219 MLIRTextFileChunk &chunk = getChunkFor(completePos);
1220 lsp::CompletionList completionList = chunk.document.getCodeCompletion(
1224 for (llvm::lsp::CompletionItem &item : completionList.items) {
1226 chunk.adjustLocForChunkOffset(item.textEdit->range);
1227 for (llvm::lsp::TextEdit &edit : item.additionalTextEdits)
1228 chunk.adjustLocForChunkOffset(edit.range);
1230 return completionList;
1233void MLIRTextFile::getCodeActions(
const lsp::URIForFile &uri,
1234 const lsp::Range &pos,
1235 const lsp::CodeActionContext &context,
1236 std::vector<lsp::CodeAction> &actions) {
1238 for (
auto &
diag : context.diagnostics) {
1239 if (
diag.source !=
"mlir")
1241 lsp::Position diagPos =
diag.range.start;
1242 MLIRTextFileChunk &chunk = getChunkFor(diagPos);
1245 lsp::CodeAction action;
1246 action.title =
"Add expected-* diagnostic checks";
1247 action.kind = lsp::CodeAction::kQuickFix.str();
1250 switch (
diag.severity) {
1251 case llvm::lsp::DiagnosticSeverity::Error:
1254 case llvm::lsp::DiagnosticSeverity::Warning:
1255 severity =
"warning";
1262 std::vector<llvm::lsp::TextEdit> edits;
1263 chunk.document.getCodeActionForDiagnostic(uri, diagPos, severity,
1264 diag.message, edits);
1267 if (
diag.relatedInformation) {
1268 for (
auto ¬eDiag : *
diag.relatedInformation) {
1269 if (noteDiag.location.uri != uri)
1271 diagPos = noteDiag.location.range.start;
1272 diagPos.line -= chunk.lineOffset;
1273 chunk.document.getCodeActionForDiagnostic(uri, diagPos,
"note",
1274 noteDiag.message, edits);
1278 for (llvm::lsp::TextEdit &edit : edits)
1279 chunk.adjustLocForChunkOffset(edit.range);
1281 action.edit.emplace();
1282 action.edit->changes[uri.uri().str()] = std::move(edits);
1283 action.diagnostics = {
diag};
1285 actions.emplace_back(std::move(action));
1289llvm::Expected<lsp::MLIRConvertBytecodeResult>
1290MLIRTextFile::convertToBytecode() {
1292 if (chunks.size() != 1) {
1293 return llvm::make_error<llvm::lsp::LSPError>(
1294 "unexpected split file, please remove all `// -----`",
1295 llvm::lsp::ErrorCode::RequestFailed);
1297 return chunks.front()->document.convertToBytecode();
1300MLIRTextFileChunk &MLIRTextFile::getChunkFor(lsp::Position &pos) {
1301 if (chunks.size() == 1)
1302 return *chunks.front();
1306 auto it = llvm::upper_bound(
1307 chunks, pos, [](
const lsp::Position &pos,
const auto &chunk) {
1308 return static_cast<uint64_t
>(pos.line) < chunk->lineOffset;
1310 MLIRTextFileChunk &chunk = it == chunks.end() ? *chunks.back() : **(--it);
1311 pos.line -= chunk.lineOffset;
1327 llvm::StringMap<std::unique_ptr<MLIRTextFile>>
files;
1338 :
impl(std::make_unique<
Impl>(registryFn)) {}
1342 const URIForFile &uri, StringRef contents,
int64_t version,
1343 std::vector<llvm::lsp::Diagnostic> &diagnostics) {
1344 impl->files[uri.file()] =
1345 std::make_unique<MLIRTextFile>(uri, contents, version,
impl->registryFn,
1346 impl->workspaceRoot, diagnostics);
1350 auto it =
impl->files.find(uri.file());
1351 if (it ==
impl->files.end())
1352 return std::nullopt;
1354 int64_t version = it->second->getVersion();
1355 impl->files.erase(it);
1360 const URIForFile &uri,
const Position &defPos,
1361 std::vector<llvm::lsp::Location> &locations) {
1362 auto fileIt =
impl->files.find(uri.file());
1363 if (fileIt !=
impl->files.end())
1364 fileIt->second->getLocationsOf(uri, defPos, locations);
1368 const URIForFile &uri,
const Position &pos,
1369 std::vector<llvm::lsp::Location> &references) {
1370 auto fileIt =
impl->files.find(uri.file());
1371 if (fileIt !=
impl->files.end())
1372 fileIt->second->findReferencesOf(uri, pos, references);
1376 const Position &hoverPos) {
1377 auto fileIt =
impl->files.find(uri.file());
1378 if (fileIt !=
impl->files.end())
1379 return fileIt->second->findHover(uri, hoverPos);
1380 return std::nullopt;
1384 const URIForFile &uri, std::vector<DocumentSymbol> &symbols) {
1385 auto fileIt =
impl->files.find(uri.file());
1386 if (fileIt !=
impl->files.end())
1387 fileIt->second->findDocumentSymbols(symbols);
1392 const Position &completePos) {
1393 auto fileIt =
impl->files.find(uri.file());
1394 if (fileIt !=
impl->files.end())
1395 return fileIt->second->getCodeCompletion(uri, completePos);
1396 return CompletionList();
1400 const CodeActionContext &context,
1401 std::vector<CodeAction> &actions) {
1402 auto fileIt =
impl->files.find(uri.file());
1403 if (fileIt !=
impl->files.end())
1404 fileIt->second->getCodeActions(uri, pos, context, actions);
1413 std::string errorMsg;
1423 &fallbackResourceMap);
1427 if (failed(
parseSourceFile(uri.file(), &parsedBlock, parserConfig))) {
1428 return llvm::make_error<llvm::lsp::LSPError>(
1429 "failed to parse bytecode source file: " + errorMsg,
1430 llvm::lsp::ErrorCode::RequestFailed);
1435 if (!llvm::hasSingleElement(parsedBlock)) {
1436 return llvm::make_error<llvm::lsp::LSPError>(
1437 "expected bytecode to contain a single top-level operation",
1438 llvm::lsp::ErrorCode::RequestFailed);
1450 nullptr, &fallbackResourceMap);
1452 llvm::raw_string_ostream os(
result.output);
1453 topOp->print(os, state);
1455 return std::move(
result);
1460 auto fileIt =
impl->files.find(uri.file());
1461 if (fileIt ==
impl->files.end()) {
1462 return llvm::make_error<llvm::lsp::LSPError>(
1463 "language server does not contain an entry for this source file",
1464 llvm::lsp::ErrorCode::RequestFailed);
1466 return fileIt->second->convertToBytecode();
1470 impl->workspaceRoot = root.str();
static std::optional< unsigned > getResultNumberFromLoc(SMLoc loc)
Given a location pointing to a result, return the result number it refers to or std::nullopt if it re...
static std::optional< StringRef > getTextFromRange(SMRange range)
Given a source location range, return the text covered by the given range.
static std::optional< lsp::Location > getLocationFromLoc(StringRef uriScheme, FileLineColLoc loc, StringRef workspaceRoot)
Returns a language server location from the given MLIR file location.
static bool isDefOrUse(const AsmParserState::SMDefinition &def, SMLoc loc, SMRange *overlappedRange=nullptr)
Returns true if the given location is contained by the definition or one of the uses of the given SMD...
static bool contains(SMRange range, SMLoc loc)
Returns true if the given range contains the given source location.
static void collectLocationsFromLoc(Location loc, std::vector< lsp::Location > &locations, const lsp::URIForFile &uri, StringRef workspaceRoot)
Collect all of the locations from the given MLIR location that are not contained within the given URI...
static lsp::Diagnostic getLspDiagnoticFromDiag(llvm::SourceMgr &sourceMgr, Diagnostic &diag, const lsp::URIForFile &uri, StringRef workspaceRoot)
Convert the given MLIR diagnostic to the LSP form.
static void printDefBlockName(raw_ostream &os, Block *block, SMRange loc={})
Given a block and source location, print the source name of the block to the given output stream.
static SMRange convertTokenLocToRange(SMLoc loc)
Returns the range of a lexical token given a SMLoc corresponding to the start of an token location.
static std::optional< int > convertFileLocPosition(unsigned value)
Convert an MLIR one-based file position to a zero-based LSP position.
static std::string diag(const llvm::Value &value)
void completeDialectName()
This class represents state from a parsed MLIR textual format string.
iterator_range< AttributeDefIterator > getAttributeAliasDefs() const
Return a range of the AttributeAliasDefinitions held by the current parser state.
iterator_range< BlockDefIterator > getBlockDefs() const
Return a range of the BlockDefinitions held by the current parser state.
const OperationDefinition * getOpDef(Operation *op) const
Return the definition for the given operation, or nullptr if the given operation does not have a defi...
const BlockDefinition * getBlockDef(Block *block) const
Return the definition for the given block, or nullptr if the given block does not have a definition.
iterator_range< OperationDefIterator > getOpDefs() const
Return a range of the OperationDefinitions held by the current parser state.
iterator_range< TypeDefIterator > getTypeAliasDefs() const
Return a range of the TypeAliasDefinitions held by the current parser state.
This class provides management for the lifetime of the state used when printing the IR.
unsigned getArgNumber() const
Returns the number of this argument.
Block represents an ordered list of Operations.
BlockArgument getArgument(unsigned i)
bool hasNoSuccessors()
Returns true if this blocks has no successors.
iterator_range< pred_iterator > getPredecessors()
SuccessorRange getSuccessors()
bool hasNoPredecessors()
Return true if this block has no predecessors.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
unsigned computeBlockNumber()
Compute the position of this block within its parent region using an O(N) linear scan.
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
A fallback map containing external resources not explicitly handled by another parser/printer.
An instance of this location represents a tuple of file, line number, and column number.
StringAttr getFilename() const
unsigned getColumn() const
WalkResult walk(function_ref< WalkResult(Location)> walkFn)
Walk all of the locations nested directly under, and including, the current.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
T * getOrLoadDialect()
Get (or create) a dialect for the given derived dialect type.
ArrayRef< RegisteredOperationName > getRegisteredOperations()
Return a sorted array containing the information about all registered operations.
const DialectRegistry & getDialectRegistry()
Return the dialect registry associated with this context.
std::vector< StringRef > getAvailableDialects()
Return information about all available dialects in the registry in this context.
void allowUnregisteredDialects(bool allow=true)
Enables creating operations in unregistered dialects.
Set of flags used to control the behavior of the various IR print methods (e.g.
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
unsigned getNumRegions()
Returns the number of regions held by this operation.
OperationName getName()
The name of an operation is the key identifier for it.
void print(raw_ostream &os, const OpPrintingFlags &flags={})
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
result_range getResults()
This class acts as an owning reference to an op, and will automatically destroy the held op on destru...
This class represents a configuration for the MLIR assembly parser.
This diagnostic handler is a simple RAII class that registers and erases a diagnostic handler on a gi...
Type getType() const
Return the type of this value.
static WalkResult advance()
static WalkResult interrupt()
void addOrUpdateDocument(const URIForFile &uri, StringRef contents, int64_t version, std::vector< Diagnostic > &diagnostics)
Add or update the document, with the provided version, at the given URI.
std::optional< int64_t > removeDocument(const URIForFile &uri)
Remove the document with the given uri.
void findReferencesOf(const URIForFile &uri, const Position &pos, std::vector< Location > &references)
Find all references of the object pointed at by the given position.
void getLocationsOf(const URIForFile &uri, const Position &defPos, std::vector< Location > &locations)
Return the locations of the object pointed at by the given position.
std::optional< Hover > findHover(const URIForFile &uri, const Position &hoverPos)
Find a hover description for the given hover position, or std::nullopt if one couldn't be found.
llvm::Expected< MLIRConvertBytecodeResult > convertFromBytecode(const URIForFile &uri)
Convert the given bytecode file to the textual format.
llvm::Expected< MLIRConvertBytecodeResult > convertToBytecode(const URIForFile &uri)
Convert the given textual file to the bytecode format.
void setWorkspaceRoot(StringRef root)
Set the workspace root for the server.
CompletionList getCodeCompletion(const URIForFile &uri, const Position &completePos)
Get the code completion list for the position within the given file.
void findDocumentSymbols(const URIForFile &uri, std::vector< DocumentSymbol > &symbols)
Find all of the document symbols within the given file.
MLIRServer(DialectRegistryFn registry_fn)
Construct a new server with the given dialect registry function.
void getCodeActions(const URIForFile &uri, const Range &pos, const CodeActionContext &context, std::vector< CodeAction > &actions)
Get the set of code actions within the file.
llvm::function_ref< DialectRegistry &(const llvm::lsp::URIForFile &uri)> DialectRegistryFn
SMRange convertTokenLocToRange(SMLoc loc, StringRef identifierChars="")
Returns the range of a lexical token given a SMLoc corresponding to the start of an token location.
Include the generated interface declarations.
const char *const kDefaultSplitMarker
LogicalResult parseAsmSourceFile(const llvm::SourceMgr &sourceMgr, Block *block, const ParserConfig &config, AsmParserState *asmState=nullptr, AsmParserCodeCompleteContext *codeCompleteContext=nullptr)
This parses the file specified by the indicated SourceMgr and appends parsed operations to the given ...
llvm::SetVector< T, Vector, Set, N > SetVector
LogicalResult parseSourceFile(const llvm::SourceMgr &sourceMgr, Block *block, const ParserConfig &config, LocationAttr *sourceFileLoc=nullptr)
This parses the file specified by the indicated SourceMgr and appends parsed operations to the given ...
LogicalResult writeBytecodeToFile(Operation *op, raw_ostream &os, const BytecodeWriterConfig &config={})
Write the bytecode for the given operation to the provided output stream.
This class represents the result of converting between MLIR's bytecode and textual format.
std::string workspaceRoot
The workspace root of the server.
lsp::DialectRegistryFn registryFn
The registry factory for containing dialects that can be recognized in parsed .mlir files.
llvm::StringMap< std::unique_ptr< MLIRTextFile > > files
The files held by the server, mapped by their URI file name.
Impl(lsp::DialectRegistryFn registryFn)
StringRef name
The name of the attribute alias.
Attribute value
The value of the alias.
This class represents the information for a block definition within the input file.
Block * block
The block representing this definition.
SMDefinition definition
The source location for the block, i.e.
Operation * op
The operation representing this definition.
This class represents a definition within the source manager, containing it's defining location and l...
SmallVector< SMRange > uses
The source location of all uses of the definition.
SMRange loc
The source location of the definition.
Type value
The value of the alias.
StringRef name
The name of the attribute alias.
Represents a range (offset, size, and stride) where each element of the triple may be dynamic or stat...