1 //===- MLIRServer.cpp - MLIR Generic Language Server ----------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "MLIRServer.h"
10 #include "../lsp-server-support/Logging.h"
11 #include "../lsp-server-support/Protocol.h"
12 #include "mlir/IR/FunctionInterfaces.h"
13 #include "mlir/IR/Operation.h"
14 #include "mlir/Parser/AsmParserState.h"
15 #include "mlir/Parser/Parser.h"
16 #include "llvm/Support/SourceMgr.h"
17 
18 using namespace mlir;
19 
20 /// Returns a language server location from the given MLIR file location.
21 static Optional<lsp::Location> getLocationFromLoc(FileLineColLoc loc) {
22   llvm::Expected<lsp::URIForFile> sourceURI =
23       lsp::URIForFile::fromFile(loc.getFilename());
24   if (!sourceURI) {
25     lsp::Logger::error("Failed to create URI for file `{0}`: {1}",
26                        loc.getFilename(),
27                        llvm::toString(sourceURI.takeError()));
28     return llvm::None;
29   }
30 
31   lsp::Position position;
32   position.line = loc.getLine() - 1;
33   position.character = loc.getColumn() ? loc.getColumn() - 1 : 0;
34   return lsp::Location{*sourceURI, lsp::Range(position)};
35 }
36 
37 /// Returns a language server location from the given MLIR location, or None if
38 /// one couldn't be created. `uri` is an optional additional filter that, when
39 /// present, is used to filter sub locations that do not share the same uri.
40 static Optional<lsp::Location>
41 getLocationFromLoc(llvm::SourceMgr &sourceMgr, Location loc,
42                    const lsp::URIForFile *uri = nullptr) {
43   Optional<lsp::Location> location;
44   loc->walk([&](Location nestedLoc) {
45     FileLineColLoc fileLoc = nestedLoc.dyn_cast<FileLineColLoc>();
46     if (!fileLoc)
47       return WalkResult::advance();
48 
49     Optional<lsp::Location> sourceLoc = getLocationFromLoc(fileLoc);
50     if (sourceLoc && (!uri || sourceLoc->uri == *uri)) {
51       location = *sourceLoc;
52       SMLoc loc = sourceMgr.FindLocForLineAndColumn(
53           sourceMgr.getMainFileID(), fileLoc.getLine(), fileLoc.getColumn());
54 
55       // Use range of potential identifier starting at location, else length 1
56       // range.
57       location->range.end.character += 1;
58       if (Optional<SMRange> range = AsmParserState::convertIdLocToRange(loc)) {
59         auto lineCol = sourceMgr.getLineAndColumn(range->End);
60         location->range.end.character =
61             std::max(fileLoc.getColumn() + 1, lineCol.second - 1);
62       }
63       return WalkResult::interrupt();
64     }
65     return WalkResult::advance();
66   });
67   return location;
68 }
69 
70 /// Collect all of the locations from the given MLIR location that are not
71 /// contained within the given URI.
72 static void collectLocationsFromLoc(Location loc,
73                                     std::vector<lsp::Location> &locations,
74                                     const lsp::URIForFile &uri) {
75   SetVector<Location> visitedLocs;
76   loc->walk([&](Location nestedLoc) {
77     FileLineColLoc fileLoc = nestedLoc.dyn_cast<FileLineColLoc>();
78     if (!fileLoc || !visitedLocs.insert(nestedLoc))
79       return WalkResult::advance();
80 
81     Optional<lsp::Location> sourceLoc = getLocationFromLoc(fileLoc);
82     if (sourceLoc && sourceLoc->uri != uri)
83       locations.push_back(*sourceLoc);
84     return WalkResult::advance();
85   });
86 }
87 
88 /// Returns true if the given range contains the given source location. Note
89 /// that this has slightly different behavior than SMRange because it is
90 /// inclusive of the end location.
91 static bool contains(SMRange range, SMLoc loc) {
92   return range.Start.getPointer() <= loc.getPointer() &&
93          loc.getPointer() <= range.End.getPointer();
94 }
95 
96 /// Returns true if the given location is contained by the definition or one of
97 /// the uses of the given SMDefinition. If provided, `overlappedRange` is set to
98 /// the range within `def` that the provided `loc` overlapped with.
99 static bool isDefOrUse(const AsmParserState::SMDefinition &def, SMLoc loc,
100                        SMRange *overlappedRange = nullptr) {
101   // Check the main definition.
102   if (contains(def.loc, loc)) {
103     if (overlappedRange)
104       *overlappedRange = def.loc;
105     return true;
106   }
107 
108   // Check the uses.
109   const auto *useIt = llvm::find_if(
110       def.uses, [&](const SMRange &range) { return contains(range, loc); });
111   if (useIt != def.uses.end()) {
112     if (overlappedRange)
113       *overlappedRange = *useIt;
114     return true;
115   }
116   return false;
117 }
118 
119 /// Given a location pointing to a result, return the result number it refers
120 /// to or None if it refers to all of the results.
121 static Optional<unsigned> getResultNumberFromLoc(SMLoc loc) {
122   // Skip all of the identifier characters.
123   auto isIdentifierChar = [](char c) {
124     return isalnum(c) || c == '%' || c == '$' || c == '.' || c == '_' ||
125            c == '-';
126   };
127   const char *curPtr = loc.getPointer();
128   while (isIdentifierChar(*curPtr))
129     ++curPtr;
130 
131   // Check to see if this location indexes into the result group, via `#`. If it
132   // doesn't, we can't extract a sub result number.
133   if (*curPtr != '#')
134     return llvm::None;
135 
136   // Compute the sub result number from the remaining portion of the string.
137   const char *numberStart = ++curPtr;
138   while (llvm::isDigit(*curPtr))
139     ++curPtr;
140   StringRef numberStr(numberStart, curPtr - numberStart);
141   unsigned resultNumber = 0;
142   return numberStr.consumeInteger(10, resultNumber) ? Optional<unsigned>()
143                                                     : resultNumber;
144 }
145 
146 /// Given a source location range, return the text covered by the given range.
147 /// If the range is invalid, returns None.
148 static Optional<StringRef> getTextFromRange(SMRange range) {
149   if (!range.isValid())
150     return None;
151   const char *startPtr = range.Start.getPointer();
152   return StringRef(startPtr, range.End.getPointer() - startPtr);
153 }
154 
155 /// Given a block, return its position in its parent region.
156 static unsigned getBlockNumber(Block *block) {
157   return std::distance(block->getParent()->begin(), block->getIterator());
158 }
159 
160 /// Given a block and source location, print the source name of the block to the
161 /// given output stream.
162 static void printDefBlockName(raw_ostream &os, Block *block, SMRange loc = {}) {
163   // Try to extract a name from the source location.
164   Optional<StringRef> text = getTextFromRange(loc);
165   if (text && text->startswith("^")) {
166     os << *text;
167     return;
168   }
169 
170   // Otherwise, we don't have a name so print the block number.
171   os << "<Block #" << getBlockNumber(block) << ">";
172 }
173 static void printDefBlockName(raw_ostream &os,
174                               const AsmParserState::BlockDefinition &def) {
175   printDefBlockName(os, def.block, def.definition.loc);
176 }
177 
178 /// Convert the given MLIR diagnostic to the LSP form.
179 static lsp::Diagnostic getLspDiagnoticFromDiag(llvm::SourceMgr &sourceMgr,
180                                                Diagnostic &diag,
181                                                const lsp::URIForFile &uri) {
182   lsp::Diagnostic lspDiag;
183   lspDiag.source = "mlir";
184 
185   // Note: Right now all of the diagnostics are treated as parser issues, but
186   // some are parser and some are verifier.
187   lspDiag.category = "Parse Error";
188 
189   // Try to grab a file location for this diagnostic.
190   // TODO: For simplicity, we just grab the first one. It may be likely that we
191   // will need a more interesting heuristic here.'
192   Optional<lsp::Location> lspLocation =
193       getLocationFromLoc(sourceMgr, diag.getLocation(), &uri);
194   if (lspLocation)
195     lspDiag.range = lspLocation->range;
196 
197   // Convert the severity for the diagnostic.
198   switch (diag.getSeverity()) {
199   case DiagnosticSeverity::Note:
200     llvm_unreachable("expected notes to be handled separately");
201   case DiagnosticSeverity::Warning:
202     lspDiag.severity = lsp::DiagnosticSeverity::Warning;
203     break;
204   case DiagnosticSeverity::Error:
205     lspDiag.severity = lsp::DiagnosticSeverity::Error;
206     break;
207   case DiagnosticSeverity::Remark:
208     lspDiag.severity = lsp::DiagnosticSeverity::Information;
209     break;
210   }
211   lspDiag.message = diag.str();
212 
213   // Attach any notes to the main diagnostic as related information.
214   std::vector<lsp::DiagnosticRelatedInformation> relatedDiags;
215   for (Diagnostic &note : diag.getNotes()) {
216     lsp::Location noteLoc;
217     if (Optional<lsp::Location> loc =
218             getLocationFromLoc(sourceMgr, note.getLocation()))
219       noteLoc = *loc;
220     else
221       noteLoc.uri = uri;
222     relatedDiags.emplace_back(noteLoc, note.str());
223   }
224   if (!relatedDiags.empty())
225     lspDiag.relatedInformation = std::move(relatedDiags);
226 
227   return lspDiag;
228 }
229 
230 //===----------------------------------------------------------------------===//
231 // MLIRDocument
232 //===----------------------------------------------------------------------===//
233 
234 namespace {
235 /// This class represents all of the information pertaining to a specific MLIR
236 /// document.
237 struct MLIRDocument {
238   MLIRDocument(MLIRContext &context, const lsp::URIForFile &uri,
239                StringRef contents, std::vector<lsp::Diagnostic> &diagnostics);
240   MLIRDocument(const MLIRDocument &) = delete;
241   MLIRDocument &operator=(const MLIRDocument &) = delete;
242 
243   //===--------------------------------------------------------------------===//
244   // Definitions and References
245   //===--------------------------------------------------------------------===//
246 
247   void getLocationsOf(const lsp::URIForFile &uri, const lsp::Position &defPos,
248                       std::vector<lsp::Location> &locations);
249   void findReferencesOf(const lsp::URIForFile &uri, const lsp::Position &pos,
250                         std::vector<lsp::Location> &references);
251 
252   //===--------------------------------------------------------------------===//
253   // Hover
254   //===--------------------------------------------------------------------===//
255 
256   Optional<lsp::Hover> findHover(const lsp::URIForFile &uri,
257                                  const lsp::Position &hoverPos);
258   Optional<lsp::Hover>
259   buildHoverForOperation(SMRange hoverRange,
260                          const AsmParserState::OperationDefinition &op);
261   lsp::Hover buildHoverForOperationResult(SMRange hoverRange, Operation *op,
262                                           unsigned resultStart,
263                                           unsigned resultEnd, SMLoc posLoc);
264   lsp::Hover buildHoverForBlock(SMRange hoverRange,
265                                 const AsmParserState::BlockDefinition &block);
266   lsp::Hover
267   buildHoverForBlockArgument(SMRange hoverRange, BlockArgument arg,
268                              const AsmParserState::BlockDefinition &block);
269 
270   //===--------------------------------------------------------------------===//
271   // Document Symbols
272   //===--------------------------------------------------------------------===//
273 
274   void findDocumentSymbols(std::vector<lsp::DocumentSymbol> &symbols);
275   void findDocumentSymbols(Operation *op,
276                            std::vector<lsp::DocumentSymbol> &symbols);
277 
278   //===--------------------------------------------------------------------===//
279   // Fields
280   //===--------------------------------------------------------------------===//
281 
282   /// The high level parser state used to find definitions and references within
283   /// the source file.
284   AsmParserState asmState;
285 
286   /// The container for the IR parsed from the input file.
287   Block parsedIR;
288 
289   /// The source manager containing the contents of the input file.
290   llvm::SourceMgr sourceMgr;
291 };
292 } // namespace
293 
294 MLIRDocument::MLIRDocument(MLIRContext &context, const lsp::URIForFile &uri,
295                            StringRef contents,
296                            std::vector<lsp::Diagnostic> &diagnostics) {
297   ScopedDiagnosticHandler handler(&context, [&](Diagnostic &diag) {
298     diagnostics.push_back(getLspDiagnoticFromDiag(sourceMgr, diag, uri));
299   });
300 
301   // Try to parsed the given IR string.
302   auto memBuffer = llvm::MemoryBuffer::getMemBufferCopy(contents, uri.file());
303   if (!memBuffer) {
304     lsp::Logger::error("Failed to create memory buffer for file", uri.file());
305     return;
306   }
307 
308   sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
309   if (failed(parseSourceFile(sourceMgr, &parsedIR, &context, nullptr,
310                              &asmState))) {
311     // If parsing failed, clear out any of the current state.
312     parsedIR.clear();
313     asmState = AsmParserState();
314     return;
315   }
316 }
317 
318 //===----------------------------------------------------------------------===//
319 // MLIRDocument: Definitions and References
320 //===----------------------------------------------------------------------===//
321 
322 void MLIRDocument::getLocationsOf(const lsp::URIForFile &uri,
323                                   const lsp::Position &defPos,
324                                   std::vector<lsp::Location> &locations) {
325   SMLoc posLoc = defPos.getAsSMLoc(sourceMgr);
326 
327   // Functor used to check if an SM definition contains the position.
328   auto containsPosition = [&](const AsmParserState::SMDefinition &def) {
329     if (!isDefOrUse(def, posLoc))
330       return false;
331     locations.emplace_back(uri, sourceMgr, def.loc);
332     return true;
333   };
334 
335   // Check all definitions related to operations.
336   for (const AsmParserState::OperationDefinition &op : asmState.getOpDefs()) {
337     if (contains(op.loc, posLoc))
338       return collectLocationsFromLoc(op.op->getLoc(), locations, uri);
339     for (const auto &result : op.resultGroups)
340       if (containsPosition(result.definition))
341         return collectLocationsFromLoc(op.op->getLoc(), locations, uri);
342     for (const auto &symUse : op.symbolUses) {
343       if (contains(symUse, posLoc)) {
344         locations.emplace_back(uri, sourceMgr, op.loc);
345         return collectLocationsFromLoc(op.op->getLoc(), locations, uri);
346       }
347     }
348   }
349 
350   // Check all definitions related to blocks.
351   for (const AsmParserState::BlockDefinition &block : asmState.getBlockDefs()) {
352     if (containsPosition(block.definition))
353       return;
354     for (const AsmParserState::SMDefinition &arg : block.arguments)
355       if (containsPosition(arg))
356         return;
357   }
358 }
359 
360 void MLIRDocument::findReferencesOf(const lsp::URIForFile &uri,
361                                     const lsp::Position &pos,
362                                     std::vector<lsp::Location> &references) {
363   // Functor used to append all of the definitions/uses of the given SM
364   // definition to the reference list.
365   auto appendSMDef = [&](const AsmParserState::SMDefinition &def) {
366     references.emplace_back(uri, sourceMgr, def.loc);
367     for (const SMRange &use : def.uses)
368       references.emplace_back(uri, sourceMgr, use);
369   };
370 
371   SMLoc posLoc = pos.getAsSMLoc(sourceMgr);
372 
373   // Check all definitions related to operations.
374   for (const AsmParserState::OperationDefinition &op : asmState.getOpDefs()) {
375     if (contains(op.loc, posLoc)) {
376       for (const auto &result : op.resultGroups)
377         appendSMDef(result.definition);
378       for (const auto &symUse : op.symbolUses)
379         if (contains(symUse, posLoc))
380           references.emplace_back(uri, sourceMgr, symUse);
381       return;
382     }
383     for (const auto &result : op.resultGroups)
384       if (isDefOrUse(result.definition, posLoc))
385         return appendSMDef(result.definition);
386     for (const auto &symUse : op.symbolUses) {
387       if (!contains(symUse, posLoc))
388         continue;
389       for (const auto &symUse : op.symbolUses)
390         references.emplace_back(uri, sourceMgr, symUse);
391       return;
392     }
393   }
394 
395   // Check all definitions related to blocks.
396   for (const AsmParserState::BlockDefinition &block : asmState.getBlockDefs()) {
397     if (isDefOrUse(block.definition, posLoc))
398       return appendSMDef(block.definition);
399 
400     for (const AsmParserState::SMDefinition &arg : block.arguments)
401       if (isDefOrUse(arg, posLoc))
402         return appendSMDef(arg);
403   }
404 }
405 
406 //===----------------------------------------------------------------------===//
407 // MLIRDocument: Hover
408 //===----------------------------------------------------------------------===//
409 
410 Optional<lsp::Hover> MLIRDocument::findHover(const lsp::URIForFile &uri,
411                                              const lsp::Position &hoverPos) {
412   SMLoc posLoc = hoverPos.getAsSMLoc(sourceMgr);
413   SMRange hoverRange;
414 
415   // Check for Hovers on operations and results.
416   for (const AsmParserState::OperationDefinition &op : asmState.getOpDefs()) {
417     // Check if the position points at this operation.
418     if (contains(op.loc, posLoc))
419       return buildHoverForOperation(op.loc, op);
420 
421     // Check if the position points at the symbol name.
422     for (auto &use : op.symbolUses)
423       if (contains(use, posLoc))
424         return buildHoverForOperation(use, op);
425 
426     // Check if the position points at a result group.
427     for (unsigned i = 0, e = op.resultGroups.size(); i < e; ++i) {
428       const auto &result = op.resultGroups[i];
429       if (!isDefOrUse(result.definition, posLoc, &hoverRange))
430         continue;
431 
432       // Get the range of results covered by the over position.
433       unsigned resultStart = result.startIndex;
434       unsigned resultEnd = (i == e - 1) ? op.op->getNumResults()
435                                         : op.resultGroups[i + 1].startIndex;
436       return buildHoverForOperationResult(hoverRange, op.op, resultStart,
437                                           resultEnd, posLoc);
438     }
439   }
440 
441   // Check to see if the hover is over a block argument.
442   for (const AsmParserState::BlockDefinition &block : asmState.getBlockDefs()) {
443     if (isDefOrUse(block.definition, posLoc, &hoverRange))
444       return buildHoverForBlock(hoverRange, block);
445 
446     for (const auto &arg : llvm::enumerate(block.arguments)) {
447       if (!isDefOrUse(arg.value(), posLoc, &hoverRange))
448         continue;
449 
450       return buildHoverForBlockArgument(
451           hoverRange, block.block->getArgument(arg.index()), block);
452     }
453   }
454   return llvm::None;
455 }
456 
457 Optional<lsp::Hover> MLIRDocument::buildHoverForOperation(
458     SMRange hoverRange, const AsmParserState::OperationDefinition &op) {
459   lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
460   llvm::raw_string_ostream os(hover.contents.value);
461 
462   // Add the operation name to the hover.
463   os << "\"" << op.op->getName() << "\"";
464   if (SymbolOpInterface symbol = dyn_cast<SymbolOpInterface>(op.op))
465     os << " : " << symbol.getVisibility() << " @" << symbol.getName() << "";
466   os << "\n\n";
467 
468   os << "Generic Form:\n\n```mlir\n";
469 
470   // Temporary drop the regions of this operation so that they don't get
471   // printed in the output. This helps keeps the size of the output hover
472   // small.
473   SmallVector<std::unique_ptr<Region>> regions;
474   for (Region &region : op.op->getRegions()) {
475     regions.emplace_back(std::make_unique<Region>());
476     regions.back()->takeBody(region);
477   }
478 
479   op.op->print(
480       os, OpPrintingFlags().printGenericOpForm().elideLargeElementsAttrs());
481   os << "\n```\n";
482 
483   // Move the regions back to the current operation.
484   for (Region &region : op.op->getRegions())
485     region.takeBody(*regions.back());
486 
487   return hover;
488 }
489 
490 lsp::Hover MLIRDocument::buildHoverForOperationResult(SMRange hoverRange,
491                                                       Operation *op,
492                                                       unsigned resultStart,
493                                                       unsigned resultEnd,
494                                                       SMLoc posLoc) {
495   lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
496   llvm::raw_string_ostream os(hover.contents.value);
497 
498   // Add the parent operation name to the hover.
499   os << "Operation: \"" << op->getName() << "\"\n\n";
500 
501   // Check to see if the location points to a specific result within the
502   // group.
503   if (Optional<unsigned> resultNumber = getResultNumberFromLoc(posLoc)) {
504     if ((resultStart + *resultNumber) < resultEnd) {
505       resultStart += *resultNumber;
506       resultEnd = resultStart + 1;
507     }
508   }
509 
510   // Add the range of results and their types to the hover info.
511   if ((resultStart + 1) == resultEnd) {
512     os << "Result #" << resultStart << "\n\n"
513        << "Type: `" << op->getResult(resultStart).getType() << "`\n\n";
514   } else {
515     os << "Result #[" << resultStart << ", " << (resultEnd - 1) << "]\n\n"
516        << "Types: ";
517     llvm::interleaveComma(
518         op->getResults().slice(resultStart, resultEnd), os,
519         [&](Value result) { os << "`" << result.getType() << "`"; });
520   }
521 
522   return hover;
523 }
524 
525 lsp::Hover
526 MLIRDocument::buildHoverForBlock(SMRange hoverRange,
527                                  const AsmParserState::BlockDefinition &block) {
528   lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
529   llvm::raw_string_ostream os(hover.contents.value);
530 
531   // Print the given block to the hover output stream.
532   auto printBlockToHover = [&](Block *newBlock) {
533     if (const auto *def = asmState.getBlockDef(newBlock))
534       printDefBlockName(os, *def);
535     else
536       printDefBlockName(os, newBlock);
537   };
538 
539   // Display the parent operation, block number, predecessors, and successors.
540   os << "Operation: \"" << block.block->getParentOp()->getName() << "\"\n\n"
541      << "Block #" << getBlockNumber(block.block) << "\n\n";
542   if (!block.block->hasNoPredecessors()) {
543     os << "Predecessors: ";
544     llvm::interleaveComma(block.block->getPredecessors(), os,
545                           printBlockToHover);
546     os << "\n\n";
547   }
548   if (!block.block->hasNoSuccessors()) {
549     os << "Successors: ";
550     llvm::interleaveComma(block.block->getSuccessors(), os, printBlockToHover);
551     os << "\n\n";
552   }
553 
554   return hover;
555 }
556 
557 lsp::Hover MLIRDocument::buildHoverForBlockArgument(
558     SMRange hoverRange, BlockArgument arg,
559     const AsmParserState::BlockDefinition &block) {
560   lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
561   llvm::raw_string_ostream os(hover.contents.value);
562 
563   // Display the parent operation, block, the argument number, and the type.
564   os << "Operation: \"" << block.block->getParentOp()->getName() << "\"\n\n"
565      << "Block: ";
566   printDefBlockName(os, block);
567   os << "\n\nArgument #" << arg.getArgNumber() << "\n\n"
568      << "Type: `" << arg.getType() << "`\n\n";
569 
570   return hover;
571 }
572 
573 //===----------------------------------------------------------------------===//
574 // MLIRDocument: Document Symbols
575 //===----------------------------------------------------------------------===//
576 
577 void MLIRDocument::findDocumentSymbols(
578     std::vector<lsp::DocumentSymbol> &symbols) {
579   for (Operation &op : parsedIR)
580     findDocumentSymbols(&op, symbols);
581 }
582 
583 void MLIRDocument::findDocumentSymbols(
584     Operation *op, std::vector<lsp::DocumentSymbol> &symbols) {
585   std::vector<lsp::DocumentSymbol> *childSymbols = &symbols;
586 
587   // Check for the source information of this operation.
588   if (const AsmParserState::OperationDefinition *def = asmState.getOpDef(op)) {
589     // If this operation defines a symbol, record it.
590     if (SymbolOpInterface symbol = dyn_cast<SymbolOpInterface>(op)) {
591       symbols.emplace_back(symbol.getName(),
592                            isa<FunctionOpInterface>(op)
593                                ? lsp::SymbolKind::Function
594                                : lsp::SymbolKind::Class,
595                            lsp::Range(sourceMgr, def->scopeLoc),
596                            lsp::Range(sourceMgr, def->loc));
597       childSymbols = &symbols.back().children;
598 
599     } else if (op->hasTrait<OpTrait::SymbolTable>()) {
600       // Otherwise, if this is a symbol table push an anonymous document symbol.
601       symbols.emplace_back("<" + op->getName().getStringRef() + ">",
602                            lsp::SymbolKind::Namespace,
603                            lsp::Range(sourceMgr, def->scopeLoc),
604                            lsp::Range(sourceMgr, def->loc));
605       childSymbols = &symbols.back().children;
606     }
607   }
608 
609   // Recurse into the regions of this operation.
610   if (!op->getNumRegions())
611     return;
612   for (Region &region : op->getRegions())
613     for (Operation &childOp : region.getOps())
614       findDocumentSymbols(&childOp, *childSymbols);
615 }
616 
617 //===----------------------------------------------------------------------===//
618 // MLIRTextFileChunk
619 //===----------------------------------------------------------------------===//
620 
621 namespace {
622 /// This class represents a single chunk of an MLIR text file.
623 struct MLIRTextFileChunk {
624   MLIRTextFileChunk(MLIRContext &context, uint64_t lineOffset,
625                     const lsp::URIForFile &uri, StringRef contents,
626                     std::vector<lsp::Diagnostic> &diagnostics)
627       : lineOffset(lineOffset), document(context, uri, contents, diagnostics) {}
628 
629   /// Adjust the line number of the given range to anchor at the beginning of
630   /// the file, instead of the beginning of this chunk.
631   void adjustLocForChunkOffset(lsp::Range &range) {
632     adjustLocForChunkOffset(range.start);
633     adjustLocForChunkOffset(range.end);
634   }
635   /// Adjust the line number of the given position to anchor at the beginning of
636   /// the file, instead of the beginning of this chunk.
637   void adjustLocForChunkOffset(lsp::Position &pos) { pos.line += lineOffset; }
638 
639   /// The line offset of this chunk from the beginning of the file.
640   uint64_t lineOffset;
641   /// The document referred to by this chunk.
642   MLIRDocument document;
643 };
644 } // namespace
645 
646 //===----------------------------------------------------------------------===//
647 // MLIRTextFile
648 //===----------------------------------------------------------------------===//
649 
650 namespace {
651 /// This class represents a text file containing one or more MLIR documents.
652 class MLIRTextFile {
653 public:
654   MLIRTextFile(const lsp::URIForFile &uri, StringRef fileContents,
655                int64_t version, DialectRegistry &registry,
656                std::vector<lsp::Diagnostic> &diagnostics);
657 
658   /// Return the current version of this text file.
659   int64_t getVersion() const { return version; }
660 
661   //===--------------------------------------------------------------------===//
662   // LSP Queries
663   //===--------------------------------------------------------------------===//
664 
665   void getLocationsOf(const lsp::URIForFile &uri, lsp::Position defPos,
666                       std::vector<lsp::Location> &locations);
667   void findReferencesOf(const lsp::URIForFile &uri, lsp::Position pos,
668                         std::vector<lsp::Location> &references);
669   Optional<lsp::Hover> findHover(const lsp::URIForFile &uri,
670                                  lsp::Position hoverPos);
671   void findDocumentSymbols(std::vector<lsp::DocumentSymbol> &symbols);
672 
673 private:
674   /// Find the MLIR document that contains the given position, and update the
675   /// position to be anchored at the start of the found chunk instead of the
676   /// beginning of the file.
677   MLIRTextFileChunk &getChunkFor(lsp::Position &pos);
678 
679   /// The context used to hold the state contained by the parsed document.
680   MLIRContext context;
681 
682   /// The full string contents of the file.
683   std::string contents;
684 
685   /// The version of this file.
686   int64_t version;
687 
688   /// The number of lines in the file.
689   int64_t totalNumLines = 0;
690 
691   /// The chunks of this file. The order of these chunks is the order in which
692   /// they appear in the text file.
693   std::vector<std::unique_ptr<MLIRTextFileChunk>> chunks;
694 };
695 } // namespace
696 
697 MLIRTextFile::MLIRTextFile(const lsp::URIForFile &uri, StringRef fileContents,
698                            int64_t version, DialectRegistry &registry,
699                            std::vector<lsp::Diagnostic> &diagnostics)
700     : context(registry, MLIRContext::Threading::DISABLED),
701       contents(fileContents.str()), version(version) {
702   context.allowUnregisteredDialects();
703 
704   // Split the file into separate MLIR documents.
705   // TODO: Find a way to share the split file marker with other tools. We don't
706   // want to use `splitAndProcessBuffer` here, but we do want to make sure this
707   // marker doesn't go out of sync.
708   SmallVector<StringRef, 8> subContents;
709   StringRef(contents).split(subContents, "// -----");
710   chunks.emplace_back(std::make_unique<MLIRTextFileChunk>(
711       context, /*lineOffset=*/0, uri, subContents.front(), diagnostics));
712 
713   uint64_t lineOffset = subContents.front().count('\n');
714   for (StringRef docContents : llvm::drop_begin(subContents)) {
715     unsigned currentNumDiags = diagnostics.size();
716     auto chunk = std::make_unique<MLIRTextFileChunk>(context, lineOffset, uri,
717                                                      docContents, diagnostics);
718     lineOffset += docContents.count('\n');
719 
720     // Adjust locations used in diagnostics to account for the offset from the
721     // beginning of the file.
722     for (lsp::Diagnostic &diag :
723          llvm::drop_begin(diagnostics, currentNumDiags)) {
724       chunk->adjustLocForChunkOffset(diag.range);
725 
726       if (!diag.relatedInformation)
727         continue;
728       for (auto &it : *diag.relatedInformation)
729         if (it.location.uri == uri)
730           chunk->adjustLocForChunkOffset(it.location.range);
731     }
732     chunks.emplace_back(std::move(chunk));
733   }
734   totalNumLines = lineOffset;
735 }
736 
737 void MLIRTextFile::getLocationsOf(const lsp::URIForFile &uri,
738                                   lsp::Position defPos,
739                                   std::vector<lsp::Location> &locations) {
740   MLIRTextFileChunk &chunk = getChunkFor(defPos);
741   chunk.document.getLocationsOf(uri, defPos, locations);
742 
743   // Adjust any locations within this file for the offset of this chunk.
744   if (chunk.lineOffset == 0)
745     return;
746   for (lsp::Location &loc : locations)
747     if (loc.uri == uri)
748       chunk.adjustLocForChunkOffset(loc.range);
749 }
750 
751 void MLIRTextFile::findReferencesOf(const lsp::URIForFile &uri,
752                                     lsp::Position pos,
753                                     std::vector<lsp::Location> &references) {
754   MLIRTextFileChunk &chunk = getChunkFor(pos);
755   chunk.document.findReferencesOf(uri, pos, references);
756 
757   // Adjust any locations within this file for the offset of this chunk.
758   if (chunk.lineOffset == 0)
759     return;
760   for (lsp::Location &loc : references)
761     if (loc.uri == uri)
762       chunk.adjustLocForChunkOffset(loc.range);
763 }
764 
765 Optional<lsp::Hover> MLIRTextFile::findHover(const lsp::URIForFile &uri,
766                                              lsp::Position hoverPos) {
767   MLIRTextFileChunk &chunk = getChunkFor(hoverPos);
768   Optional<lsp::Hover> hoverInfo = chunk.document.findHover(uri, hoverPos);
769 
770   // Adjust any locations within this file for the offset of this chunk.
771   if (chunk.lineOffset != 0 && hoverInfo && hoverInfo->range)
772     chunk.adjustLocForChunkOffset(*hoverInfo->range);
773   return hoverInfo;
774 }
775 
776 void MLIRTextFile::findDocumentSymbols(
777     std::vector<lsp::DocumentSymbol> &symbols) {
778   if (chunks.size() == 1)
779     return chunks.front()->document.findDocumentSymbols(symbols);
780 
781   // If there are multiple chunks in this file, we create top-level symbols for
782   // each chunk.
783   for (unsigned i = 0, e = chunks.size(); i < e; ++i) {
784     MLIRTextFileChunk &chunk = *chunks[i];
785     lsp::Position startPos(chunk.lineOffset);
786     lsp::Position endPos((i == e - 1) ? totalNumLines - 1
787                                       : chunks[i + 1]->lineOffset);
788     lsp::DocumentSymbol symbol("<file-split-" + Twine(i) + ">",
789                                lsp::SymbolKind::Namespace,
790                                /*range=*/lsp::Range(startPos, endPos),
791                                /*selectionRange=*/lsp::Range(startPos));
792     chunk.document.findDocumentSymbols(symbol.children);
793 
794     // Fixup the locations of document symbols within this chunk.
795     if (i != 0) {
796       SmallVector<lsp::DocumentSymbol *> symbolsToFix;
797       for (lsp::DocumentSymbol &childSymbol : symbol.children)
798         symbolsToFix.push_back(&childSymbol);
799 
800       while (!symbolsToFix.empty()) {
801         lsp::DocumentSymbol *symbol = symbolsToFix.pop_back_val();
802         chunk.adjustLocForChunkOffset(symbol->range);
803         chunk.adjustLocForChunkOffset(symbol->selectionRange);
804 
805         for (lsp::DocumentSymbol &childSymbol : symbol->children)
806           symbolsToFix.push_back(&childSymbol);
807       }
808     }
809 
810     // Push the symbol for this chunk.
811     symbols.emplace_back(std::move(symbol));
812   }
813 }
814 
815 MLIRTextFileChunk &MLIRTextFile::getChunkFor(lsp::Position &pos) {
816   if (chunks.size() == 1)
817     return *chunks.front();
818 
819   // Search for the first chunk with a greater line offset, the previous chunk
820   // is the one that contains `pos`.
821   auto it = llvm::upper_bound(
822       chunks, pos, [](const lsp::Position &pos, const auto &chunk) {
823         return static_cast<uint64_t>(pos.line) < chunk->lineOffset;
824       });
825   MLIRTextFileChunk &chunk = it == chunks.end() ? *chunks.back() : **(--it);
826   pos.line -= chunk.lineOffset;
827   return chunk;
828 }
829 
830 //===----------------------------------------------------------------------===//
831 // MLIRServer::Impl
832 //===----------------------------------------------------------------------===//
833 
834 struct lsp::MLIRServer::Impl {
835   Impl(DialectRegistry &registry) : registry(registry) {}
836 
837   /// The registry containing dialects that can be recognized in parsed .mlir
838   /// files.
839   DialectRegistry &registry;
840 
841   /// The files held by the server, mapped by their URI file name.
842   llvm::StringMap<std::unique_ptr<MLIRTextFile>> files;
843 };
844 
845 //===----------------------------------------------------------------------===//
846 // MLIRServer
847 //===----------------------------------------------------------------------===//
848 
849 lsp::MLIRServer::MLIRServer(DialectRegistry &registry)
850     : impl(std::make_unique<Impl>(registry)) {}
851 lsp::MLIRServer::~MLIRServer() = default;
852 
853 void lsp::MLIRServer::addOrUpdateDocument(
854     const URIForFile &uri, StringRef contents, int64_t version,
855     std::vector<Diagnostic> &diagnostics) {
856   impl->files[uri.file()] = std::make_unique<MLIRTextFile>(
857       uri, contents, version, impl->registry, diagnostics);
858 }
859 
860 Optional<int64_t> lsp::MLIRServer::removeDocument(const URIForFile &uri) {
861   auto it = impl->files.find(uri.file());
862   if (it == impl->files.end())
863     return llvm::None;
864 
865   int64_t version = it->second->getVersion();
866   impl->files.erase(it);
867   return version;
868 }
869 
870 void lsp::MLIRServer::getLocationsOf(const URIForFile &uri,
871                                      const Position &defPos,
872                                      std::vector<Location> &locations) {
873   auto fileIt = impl->files.find(uri.file());
874   if (fileIt != impl->files.end())
875     fileIt->second->getLocationsOf(uri, defPos, locations);
876 }
877 
878 void lsp::MLIRServer::findReferencesOf(const URIForFile &uri,
879                                        const Position &pos,
880                                        std::vector<Location> &references) {
881   auto fileIt = impl->files.find(uri.file());
882   if (fileIt != impl->files.end())
883     fileIt->second->findReferencesOf(uri, pos, references);
884 }
885 
886 Optional<lsp::Hover> lsp::MLIRServer::findHover(const URIForFile &uri,
887                                                 const Position &hoverPos) {
888   auto fileIt = impl->files.find(uri.file());
889   if (fileIt != impl->files.end())
890     return fileIt->second->findHover(uri, hoverPos);
891   return llvm::None;
892 }
893 
894 void lsp::MLIRServer::findDocumentSymbols(
895     const URIForFile &uri, std::vector<DocumentSymbol> &symbols) {
896   auto fileIt = impl->files.find(uri.file());
897   if (fileIt != impl->files.end())
898     fileIt->second->findDocumentSymbols(symbols);
899 }
900