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/Logging.h"
11 #include "lsp/Protocol.h"
12 #include "mlir/IR/Operation.h"
13 #include "mlir/Parser.h"
14 #include "mlir/Parser/AsmParserState.h"
15 #include "llvm/Support/SourceMgr.h"
16 
17 using namespace mlir;
18 
19 /// Returns a language server position for the given source location.
20 static lsp::Position getPosFromLoc(llvm::SourceMgr &mgr, llvm::SMLoc loc) {
21   std::pair<unsigned, unsigned> lineAndCol = mgr.getLineAndColumn(loc);
22   lsp::Position pos;
23   pos.line = lineAndCol.first - 1;
24   pos.character = lineAndCol.second;
25   return pos;
26 }
27 
28 /// Returns a source location from the given language server position.
29 static llvm::SMLoc getPosFromLoc(llvm::SourceMgr &mgr, lsp::Position pos) {
30   return mgr.FindLocForLineAndColumn(mgr.getMainFileID(), pos.line + 1,
31                                      pos.character);
32 }
33 
34 /// Returns a language server range for the given source range.
35 static lsp::Range getRangeFromLoc(llvm::SourceMgr &mgr, llvm::SMRange range) {
36   // lsp::Range is an inclusive range, SMRange is half-open.
37   llvm::SMLoc inclusiveEnd =
38       llvm::SMLoc::getFromPointer(range.End.getPointer() - 1);
39   return {getPosFromLoc(mgr, range.Start), getPosFromLoc(mgr, inclusiveEnd)};
40 }
41 
42 /// Returns a language server location from the given source range.
43 static lsp::Location getLocationFromLoc(llvm::SourceMgr &mgr,
44                                         llvm::SMRange range,
45                                         const lsp::URIForFile &uri) {
46   return lsp::Location{uri, getRangeFromLoc(mgr, range)};
47 }
48 
49 /// Returns a language server location from the given MLIR file location.
50 static Optional<lsp::Location> getLocationFromLoc(FileLineColLoc loc) {
51   llvm::Expected<lsp::URIForFile> sourceURI =
52       lsp::URIForFile::fromFile(loc.getFilename());
53   if (!sourceURI) {
54     lsp::Logger::error("Failed to create URI for file `{0}`: {1}",
55                        loc.getFilename(),
56                        llvm::toString(sourceURI.takeError()));
57     return llvm::None;
58   }
59 
60   lsp::Position position;
61   position.line = loc.getLine() - 1;
62   position.character = loc.getColumn();
63   return lsp::Location{*sourceURI, lsp::Range(position)};
64 }
65 
66 /// Returns a language server location from the given MLIR location, or None if
67 /// one couldn't be created. `uri` is an optional additional filter that, when
68 /// present, is used to filter sub locations that do not share the same uri.
69 static Optional<lsp::Location>
70 getLocationFromLoc(Location loc, const lsp::URIForFile *uri = nullptr) {
71   Optional<lsp::Location> location;
72   loc->walk([&](Location nestedLoc) {
73     FileLineColLoc fileLoc = nestedLoc.dyn_cast<FileLineColLoc>();
74     if (!fileLoc)
75       return WalkResult::advance();
76 
77     Optional<lsp::Location> sourceLoc = getLocationFromLoc(fileLoc);
78     if (sourceLoc && (!uri || sourceLoc->uri == *uri)) {
79       location = *sourceLoc;
80       return WalkResult::interrupt();
81     }
82     return WalkResult::advance();
83   });
84   return location;
85 }
86 
87 /// Collect all of the locations from the given MLIR location that are not
88 /// contained within the given URI.
89 static void collectLocationsFromLoc(Location loc,
90                                     std::vector<lsp::Location> &locations,
91                                     const lsp::URIForFile &uri) {
92   SetVector<Location> visitedLocs;
93   loc->walk([&](Location nestedLoc) {
94     FileLineColLoc fileLoc = nestedLoc.dyn_cast<FileLineColLoc>();
95     if (!fileLoc || !visitedLocs.insert(nestedLoc))
96       return WalkResult::advance();
97 
98     Optional<lsp::Location> sourceLoc = getLocationFromLoc(fileLoc);
99     if (sourceLoc && sourceLoc->uri != uri)
100       locations.push_back(*sourceLoc);
101     return WalkResult::advance();
102   });
103 }
104 
105 /// Returns true if the given range contains the given source location. Note
106 /// that this has slightly different behavior than SMRange because it is
107 /// inclusive of the end location.
108 static bool contains(llvm::SMRange range, llvm::SMLoc loc) {
109   return range.Start.getPointer() <= loc.getPointer() &&
110          loc.getPointer() <= range.End.getPointer();
111 }
112 
113 /// Returns true if the given location is contained by the definition or one of
114 /// the uses of the given SMDefinition. If provided, `overlappedRange` is set to
115 /// the range within `def` that the provided `loc` overlapped with.
116 static bool isDefOrUse(const AsmParserState::SMDefinition &def, llvm::SMLoc loc,
117                        llvm::SMRange *overlappedRange = nullptr) {
118   // Check the main definition.
119   if (contains(def.loc, loc)) {
120     if (overlappedRange)
121       *overlappedRange = def.loc;
122     return true;
123   }
124 
125   // Check the uses.
126   auto useIt = llvm::find_if(def.uses, [&](const llvm::SMRange &range) {
127     return contains(range, loc);
128   });
129   if (useIt != def.uses.end()) {
130     if (overlappedRange)
131       *overlappedRange = *useIt;
132     return true;
133   }
134   return false;
135 }
136 
137 /// Given a location pointing to a result, return the result number it refers
138 /// to or None if it refers to all of the results.
139 static Optional<unsigned> getResultNumberFromLoc(llvm::SMLoc loc) {
140   // Skip all of the identifier characters.
141   auto isIdentifierChar = [](char c) {
142     return isalnum(c) || c == '%' || c == '$' || c == '.' || c == '_' ||
143            c == '-';
144   };
145   const char *curPtr = loc.getPointer();
146   while (isIdentifierChar(*curPtr))
147     ++curPtr;
148 
149   // Check to see if this location indexes into the result group, via `#`. If it
150   // doesn't, we can't extract a sub result number.
151   if (*curPtr != '#')
152     return llvm::None;
153 
154   // Compute the sub result number from the remaining portion of the string.
155   const char *numberStart = ++curPtr;
156   while (llvm::isDigit(*curPtr))
157     ++curPtr;
158   StringRef numberStr(numberStart, curPtr - numberStart);
159   unsigned resultNumber = 0;
160   return numberStr.consumeInteger(10, resultNumber) ? Optional<unsigned>()
161                                                     : resultNumber;
162 }
163 
164 /// Given a source location range, return the text covered by the given range.
165 /// If the range is invalid, returns None.
166 static Optional<StringRef> getTextFromRange(llvm::SMRange range) {
167   if (!range.isValid())
168     return None;
169   const char *startPtr = range.Start.getPointer();
170   return StringRef(startPtr, range.End.getPointer() - startPtr);
171 }
172 
173 /// Given a block, return its position in its parent region.
174 static unsigned getBlockNumber(Block *block) {
175   return std::distance(block->getParent()->begin(), block->getIterator());
176 }
177 
178 /// Given a block and source location, print the source name of the block to the
179 /// given output stream.
180 static void printDefBlockName(raw_ostream &os, Block *block,
181                               llvm::SMRange loc = {}) {
182   // Try to extract a name from the source location.
183   Optional<StringRef> text = getTextFromRange(loc);
184   if (text && text->startswith("^")) {
185     os << *text;
186     return;
187   }
188 
189   // Otherwise, we don't have a name so print the block number.
190   os << "<Block #" << getBlockNumber(block) << ">";
191 }
192 static void printDefBlockName(raw_ostream &os,
193                               const AsmParserState::BlockDefinition &def) {
194   printDefBlockName(os, def.block, def.definition.loc);
195 }
196 
197 /// Convert the given MLIR diagnostic to the LSP form.
198 static lsp::Diagnostic getLspDiagnoticFromDiag(Diagnostic &diag,
199                                                const lsp::URIForFile &uri) {
200   lsp::Diagnostic lspDiag;
201   lspDiag.source = "mlir";
202 
203   // Note: Right now all of the diagnostics are treated as parser issues, but
204   // some are parser and some are verifier.
205   lspDiag.category = "Parse Error";
206 
207   // Try to grab a file location for this diagnostic.
208   // TODO: For simplicity, we just grab the first one. It may be likely that we
209   // will need a more interesting heuristic here.'
210   Optional<lsp::Location> lspLocation =
211       getLocationFromLoc(diag.getLocation(), &uri);
212   if (lspLocation)
213     lspDiag.range = lspLocation->range;
214 
215   // Convert the severity for the diagnostic.
216   switch (diag.getSeverity()) {
217   case DiagnosticSeverity::Note:
218     llvm_unreachable("expected notes to be handled separately");
219   case DiagnosticSeverity::Warning:
220     lspDiag.severity = lsp::DiagnosticSeverity::Warning;
221     break;
222   case DiagnosticSeverity::Error:
223     lspDiag.severity = lsp::DiagnosticSeverity::Error;
224     break;
225   case DiagnosticSeverity::Remark:
226     lspDiag.severity = lsp::DiagnosticSeverity::Information;
227     break;
228   }
229   lspDiag.message = diag.str();
230 
231   // Attach any notes to the main diagnostic as related information.
232   std::vector<lsp::DiagnosticRelatedInformation> relatedDiags;
233   for (Diagnostic &note : diag.getNotes()) {
234     lsp::Location noteLoc;
235     if (Optional<lsp::Location> loc = getLocationFromLoc(note.getLocation()))
236       noteLoc = *loc;
237     else
238       noteLoc.uri = uri;
239     relatedDiags.emplace_back(noteLoc, note.str());
240   }
241   if (!relatedDiags.empty())
242     lspDiag.relatedInformation = std::move(relatedDiags);
243 
244   return lspDiag;
245 }
246 
247 //===----------------------------------------------------------------------===//
248 // MLIRDocument
249 //===----------------------------------------------------------------------===//
250 
251 namespace {
252 /// This class represents all of the information pertaining to a specific MLIR
253 /// document.
254 struct MLIRDocument {
255   MLIRDocument(const lsp::URIForFile &uri, StringRef contents,
256                DialectRegistry &registry,
257                std::vector<lsp::Diagnostic> &diagnostics);
258 
259   //===--------------------------------------------------------------------===//
260   // Definitions and References
261   //===--------------------------------------------------------------------===//
262 
263   void getLocationsOf(const lsp::URIForFile &uri, const lsp::Position &defPos,
264                       std::vector<lsp::Location> &locations);
265   void findReferencesOf(const lsp::URIForFile &uri, const lsp::Position &pos,
266                         std::vector<lsp::Location> &references);
267 
268   //===--------------------------------------------------------------------===//
269   // Hover
270   //===--------------------------------------------------------------------===//
271 
272   Optional<lsp::Hover> findHover(const lsp::URIForFile &uri,
273                                  const lsp::Position &hoverPos);
274   Optional<lsp::Hover>
275   buildHoverForOperation(const AsmParserState::OperationDefinition &op);
276   lsp::Hover buildHoverForOperationResult(llvm::SMRange hoverRange,
277                                           Operation *op, unsigned resultStart,
278                                           unsigned resultEnd,
279                                           llvm::SMLoc posLoc);
280   lsp::Hover buildHoverForBlock(llvm::SMRange hoverRange,
281                                 const AsmParserState::BlockDefinition &block);
282   lsp::Hover
283   buildHoverForBlockArgument(llvm::SMRange hoverRange, BlockArgument arg,
284                              const AsmParserState::BlockDefinition &block);
285 
286   /// The context used to hold the state contained by the parsed document.
287   MLIRContext context;
288 
289   /// The high level parser state used to find definitions and references within
290   /// the source file.
291   AsmParserState asmState;
292 
293   /// The container for the IR parsed from the input file.
294   Block parsedIR;
295 
296   /// The source manager containing the contents of the input file.
297   llvm::SourceMgr sourceMgr;
298 };
299 } // namespace
300 
301 MLIRDocument::MLIRDocument(const lsp::URIForFile &uri, StringRef contents,
302                            DialectRegistry &registry,
303                            std::vector<lsp::Diagnostic> &diagnostics)
304     : context(registry) {
305   context.allowUnregisteredDialects();
306   ScopedDiagnosticHandler handler(&context, [&](Diagnostic &diag) {
307     diagnostics.push_back(getLspDiagnoticFromDiag(diag, uri));
308   });
309 
310   // Try to parsed the given IR string.
311   auto memBuffer = llvm::MemoryBuffer::getMemBufferCopy(contents, uri.file());
312   if (!memBuffer) {
313     lsp::Logger::error("Failed to create memory buffer for file", uri.file());
314     return;
315   }
316 
317   sourceMgr.AddNewSourceBuffer(std::move(memBuffer), llvm::SMLoc());
318   if (failed(parseSourceFile(sourceMgr, &parsedIR, &context, nullptr,
319                              &asmState))) {
320     // If parsing failed, clear out any of the current state.
321     parsedIR.clear();
322     asmState = AsmParserState();
323     return;
324   }
325 }
326 
327 //===----------------------------------------------------------------------===//
328 // MLIRDocument: Definitions and References
329 //===----------------------------------------------------------------------===//
330 
331 void MLIRDocument::getLocationsOf(const lsp::URIForFile &uri,
332                                   const lsp::Position &defPos,
333                                   std::vector<lsp::Location> &locations) {
334   llvm::SMLoc posLoc = getPosFromLoc(sourceMgr, defPos);
335 
336   // Functor used to check if an SM definition contains the position.
337   auto containsPosition = [&](const AsmParserState::SMDefinition &def) {
338     if (!isDefOrUse(def, posLoc))
339       return false;
340     locations.push_back(getLocationFromLoc(sourceMgr, def.loc, uri));
341     return true;
342   };
343 
344   // Check all definitions related to operations.
345   for (const AsmParserState::OperationDefinition &op : asmState.getOpDefs()) {
346     if (contains(op.loc, posLoc))
347       return collectLocationsFromLoc(op.op->getLoc(), locations, uri);
348     for (const auto &result : op.resultGroups)
349       if (containsPosition(result.second))
350         return collectLocationsFromLoc(op.op->getLoc(), locations, uri);
351   }
352 
353   // Check all definitions related to blocks.
354   for (const AsmParserState::BlockDefinition &block : asmState.getBlockDefs()) {
355     if (containsPosition(block.definition))
356       return;
357     for (const AsmParserState::SMDefinition &arg : block.arguments)
358       if (containsPosition(arg))
359         return;
360   }
361 }
362 
363 void MLIRDocument::findReferencesOf(const lsp::URIForFile &uri,
364                                     const lsp::Position &pos,
365                                     std::vector<lsp::Location> &references) {
366   // Functor used to append all of the definitions/uses of the given SM
367   // definition to the reference list.
368   auto appendSMDef = [&](const AsmParserState::SMDefinition &def) {
369     references.push_back(getLocationFromLoc(sourceMgr, def.loc, uri));
370     for (const llvm::SMRange &use : def.uses)
371       references.push_back(getLocationFromLoc(sourceMgr, use, uri));
372   };
373 
374   llvm::SMLoc posLoc = getPosFromLoc(sourceMgr, pos);
375 
376   // Check all definitions related to operations.
377   for (const AsmParserState::OperationDefinition &op : asmState.getOpDefs()) {
378     if (contains(op.loc, posLoc)) {
379       for (const auto &result : op.resultGroups)
380         appendSMDef(result.second);
381       return;
382     }
383     for (const auto &result : op.resultGroups)
384       if (isDefOrUse(result.second, posLoc))
385         return appendSMDef(result.second);
386   }
387 
388   // Check all definitions related to blocks.
389   for (const AsmParserState::BlockDefinition &block : asmState.getBlockDefs()) {
390     if (isDefOrUse(block.definition, posLoc))
391       return appendSMDef(block.definition);
392 
393     for (const AsmParserState::SMDefinition &arg : block.arguments)
394       if (isDefOrUse(arg, posLoc))
395         return appendSMDef(arg);
396   }
397 }
398 
399 //===----------------------------------------------------------------------===//
400 // MLIRDocument: Hover
401 //===----------------------------------------------------------------------===//
402 
403 Optional<lsp::Hover> MLIRDocument::findHover(const lsp::URIForFile &uri,
404                                              const lsp::Position &hoverPos) {
405   llvm::SMLoc posLoc = getPosFromLoc(sourceMgr, hoverPos);
406   llvm::SMRange hoverRange;
407 
408   // Check for Hovers on operations and results.
409   for (const AsmParserState::OperationDefinition &op : asmState.getOpDefs()) {
410     // Check if the position points at this operation.
411     if (contains(op.loc, posLoc))
412       return buildHoverForOperation(op);
413 
414     // Check if the position points at a result group.
415     for (unsigned i = 0, e = op.resultGroups.size(); i < e; ++i) {
416       const auto &result = op.resultGroups[i];
417       if (!isDefOrUse(result.second, posLoc, &hoverRange))
418         continue;
419 
420       // Get the range of results covered by the over position.
421       unsigned resultStart = result.first;
422       unsigned resultEnd =
423           (i == e - 1) ? op.op->getNumResults() : op.resultGroups[i + 1].first;
424       return buildHoverForOperationResult(hoverRange, op.op, resultStart,
425                                           resultEnd, posLoc);
426     }
427   }
428 
429   // Check to see if the hover is over a block argument.
430   for (const AsmParserState::BlockDefinition &block : asmState.getBlockDefs()) {
431     if (isDefOrUse(block.definition, posLoc, &hoverRange))
432       return buildHoverForBlock(hoverRange, block);
433 
434     for (const auto &arg : llvm::enumerate(block.arguments)) {
435       if (!isDefOrUse(arg.value(), posLoc, &hoverRange))
436         continue;
437 
438       return buildHoverForBlockArgument(
439           hoverRange, block.block->getArgument(arg.index()), block);
440     }
441   }
442   return llvm::None;
443 }
444 
445 Optional<lsp::Hover> MLIRDocument::buildHoverForOperation(
446     const AsmParserState::OperationDefinition &op) {
447   // Don't show hovers for operations with regions to avoid huge hover  blocks.
448   // TODO: Should we add support for printing an op without its regions?
449   if (llvm::any_of(op.op->getRegions(),
450                    [](Region &region) { return !region.empty(); }))
451     return llvm::None;
452 
453   lsp::Hover hover(getRangeFromLoc(sourceMgr, op.loc));
454   llvm::raw_string_ostream os(hover.contents.value);
455 
456   // For hovers on an operation, show the generic form.
457   os << "```mlir\n";
458   op.op->print(
459       os, OpPrintingFlags().printGenericOpForm().elideLargeElementsAttrs());
460   os << "\n```\n";
461 
462   return hover;
463 }
464 
465 lsp::Hover MLIRDocument::buildHoverForOperationResult(llvm::SMRange hoverRange,
466                                                       Operation *op,
467                                                       unsigned resultStart,
468                                                       unsigned resultEnd,
469                                                       llvm::SMLoc posLoc) {
470   lsp::Hover hover(getRangeFromLoc(sourceMgr, hoverRange));
471   llvm::raw_string_ostream os(hover.contents.value);
472 
473   // Add the parent operation name to the hover.
474   os << "Operation: \"" << op->getName() << "\"\n\n";
475 
476   // Check to see if the location points to a specific result within the
477   // group.
478   if (Optional<unsigned> resultNumber = getResultNumberFromLoc(posLoc)) {
479     if ((resultStart + *resultNumber) < resultEnd) {
480       resultStart += *resultNumber;
481       resultEnd = resultStart + 1;
482     }
483   }
484 
485   // Add the range of results and their types to the hover info.
486   if ((resultStart + 1) == resultEnd) {
487     os << "Result #" << resultStart << "\n\n"
488        << "Type: `" << op->getResult(resultStart).getType() << "`\n\n";
489   } else {
490     os << "Result #[" << resultStart << ", " << (resultEnd - 1) << "]\n\n"
491        << "Types: ";
492     llvm::interleaveComma(
493         op->getResults().slice(resultStart, resultEnd), os,
494         [&](Value result) { os << "`" << result.getType() << "`"; });
495   }
496 
497   return hover;
498 }
499 
500 lsp::Hover
501 MLIRDocument::buildHoverForBlock(llvm::SMRange hoverRange,
502                                  const AsmParserState::BlockDefinition &block) {
503   lsp::Hover hover(getRangeFromLoc(sourceMgr, hoverRange));
504   llvm::raw_string_ostream os(hover.contents.value);
505 
506   // Print the given block to the hover output stream.
507   auto printBlockToHover = [&](Block *newBlock) {
508     if (const auto *def = asmState.getBlockDef(newBlock))
509       printDefBlockName(os, *def);
510     else
511       printDefBlockName(os, newBlock);
512   };
513 
514   // Display the parent operation, block number, predecessors, and successors.
515   os << "Operation: \"" << block.block->getParentOp()->getName() << "\"\n\n"
516      << "Block #" << getBlockNumber(block.block) << "\n\n";
517   if (!block.block->hasNoPredecessors()) {
518     os << "Predecessors: ";
519     llvm::interleaveComma(block.block->getPredecessors(), os,
520                           printBlockToHover);
521     os << "\n\n";
522   }
523   if (!block.block->hasNoSuccessors()) {
524     os << "Successors: ";
525     llvm::interleaveComma(block.block->getSuccessors(), os, printBlockToHover);
526     os << "\n\n";
527   }
528 
529   return hover;
530 }
531 
532 lsp::Hover MLIRDocument::buildHoverForBlockArgument(
533     llvm::SMRange hoverRange, BlockArgument arg,
534     const AsmParserState::BlockDefinition &block) {
535   lsp::Hover hover(getRangeFromLoc(sourceMgr, hoverRange));
536   llvm::raw_string_ostream os(hover.contents.value);
537 
538   // Display the parent operation, block, the argument number, and the type.
539   os << "Operation: \"" << block.block->getParentOp()->getName() << "\"\n\n"
540      << "Block: ";
541   printDefBlockName(os, block);
542   os << "\n\nArgument #" << arg.getArgNumber() << "\n\n"
543      << "Type: `" << arg.getType() << "`\n\n";
544 
545   return hover;
546 }
547 
548 //===----------------------------------------------------------------------===//
549 // MLIRServer::Impl
550 //===----------------------------------------------------------------------===//
551 
552 struct lsp::MLIRServer::Impl {
553   Impl(DialectRegistry &registry) : registry(registry) {}
554 
555   /// The registry containing dialects that can be recognized in parsed .mlir
556   /// files.
557   DialectRegistry &registry;
558 
559   /// The documents held by the server, mapped by their URI file name.
560   llvm::StringMap<std::unique_ptr<MLIRDocument>> documents;
561 };
562 
563 //===----------------------------------------------------------------------===//
564 // MLIRServer
565 //===----------------------------------------------------------------------===//
566 
567 lsp::MLIRServer::MLIRServer(DialectRegistry &registry)
568     : impl(std::make_unique<Impl>(registry)) {}
569 lsp::MLIRServer::~MLIRServer() {}
570 
571 void lsp::MLIRServer::addOrUpdateDocument(
572     const URIForFile &uri, StringRef contents,
573     std::vector<Diagnostic> &diagnostics) {
574   impl->documents[uri.file()] = std::make_unique<MLIRDocument>(
575       uri, contents, impl->registry, diagnostics);
576 }
577 
578 void lsp::MLIRServer::removeDocument(const URIForFile &uri) {
579   impl->documents.erase(uri.file());
580 }
581 
582 void lsp::MLIRServer::getLocationsOf(const URIForFile &uri,
583                                      const Position &defPos,
584                                      std::vector<Location> &locations) {
585   auto fileIt = impl->documents.find(uri.file());
586   if (fileIt != impl->documents.end())
587     fileIt->second->getLocationsOf(uri, defPos, locations);
588 }
589 
590 void lsp::MLIRServer::findReferencesOf(const URIForFile &uri,
591                                        const Position &pos,
592                                        std::vector<Location> &references) {
593   auto fileIt = impl->documents.find(uri.file());
594   if (fileIt != impl->documents.end())
595     fileIt->second->findReferencesOf(uri, pos, references);
596 }
597 
598 Optional<lsp::Hover> lsp::MLIRServer::findHover(const URIForFile &uri,
599                                                 const Position &hoverPos) {
600   auto fileIt = impl->documents.find(uri.file());
601   if (fileIt != impl->documents.end())
602     return fileIt->second->findHover(uri, hoverPos);
603   return llvm::None;
604 }
605