MLIR  22.0.0git
TableGenServer.cpp
Go to the documentation of this file.
1 //===- TableGenServer.cpp - TableGen 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 "TableGenServer.h"
10 
14 #include "llvm/ADT/IntervalMap.h"
15 #include "llvm/ADT/PointerUnion.h"
16 #include "llvm/ADT/StringMap.h"
17 #include "llvm/Support/LSP/Logging.h"
18 #include "llvm/Support/LSP/Protocol.h"
19 #include "llvm/Support/Path.h"
20 #include "llvm/Support/VirtualFileSystem.h"
21 #include "llvm/TableGen/Parser.h"
22 #include "llvm/TableGen/Record.h"
23 #include <optional>
24 
25 using namespace mlir;
26 using llvm::Record;
27 using llvm::RecordKeeper;
28 using llvm::RecordVal;
29 using llvm::SourceMgr;
30 
31 /// Returns the range of a lexical token given a SMLoc corresponding to the
32 /// start of an token location. The range is computed heuristically, and
33 /// supports identifier-like tokens, strings, etc.
34 static SMRange convertTokenLocToRange(SMLoc loc) {
35  return lsp::convertTokenLocToRange(loc, "$");
36 }
37 
38 /// Returns a language server uri for the given source location. `mainFileURI`
39 /// corresponds to the uri for the main file of the source manager.
40 static llvm::lsp::URIForFile
41 getURIFromLoc(const SourceMgr &mgr, SMLoc loc,
42  const llvm::lsp::URIForFile &mainFileURI) {
43  int bufferId = mgr.FindBufferContainingLoc(loc);
44  if (bufferId == 0 || bufferId == static_cast<int>(mgr.getMainFileID()))
45  return mainFileURI;
47  llvm::lsp::URIForFile::fromFile(
48  mgr.getBufferInfo(bufferId).Buffer->getBufferIdentifier());
49  if (fileForLoc)
50  return *fileForLoc;
51  llvm::lsp::Logger::error("Failed to create URI for include file: {0}",
52  llvm::toString(fileForLoc.takeError()));
53  return mainFileURI;
54 }
55 
56 /// Returns a language server location from the given source range.
57 static llvm::lsp::Location
58 getLocationFromLoc(SourceMgr &mgr, SMRange loc,
59  const llvm::lsp::URIForFile &uri) {
60  return llvm::lsp::Location(getURIFromLoc(mgr, loc.Start, uri),
61  llvm::lsp::Range(mgr, loc));
62 }
63 static llvm::lsp::Location
64 getLocationFromLoc(SourceMgr &mgr, SMLoc loc,
65  const llvm::lsp::URIForFile &uri) {
66  return getLocationFromLoc(mgr, convertTokenLocToRange(loc), uri);
67 }
68 
69 /// Convert the given TableGen diagnostic to the LSP form.
70 static std::optional<llvm::lsp::Diagnostic>
71 getLspDiagnoticFromDiag(const llvm::SMDiagnostic &diag,
72  const llvm::lsp::URIForFile &uri) {
73  auto *sourceMgr = const_cast<SourceMgr *>(diag.getSourceMgr());
74  if (!sourceMgr || !diag.getLoc().isValid())
75  return std::nullopt;
76 
77  llvm::lsp::Diagnostic lspDiag;
78  lspDiag.source = "tablegen";
79  lspDiag.category = "Parse Error";
80 
81  // Try to grab a file location for this diagnostic.
82  llvm::lsp::Location loc = getLocationFromLoc(*sourceMgr, diag.getLoc(), uri);
83  lspDiag.range = loc.range;
84 
85  // Skip diagnostics that weren't emitted within the main file.
86  if (loc.uri != uri)
87  return std::nullopt;
88 
89  // Convert the severity for the diagnostic.
90  switch (diag.getKind()) {
91  case SourceMgr::DK_Warning:
92  lspDiag.severity = llvm::lsp::DiagnosticSeverity::Warning;
93  break;
94  case SourceMgr::DK_Error:
95  lspDiag.severity = llvm::lsp::DiagnosticSeverity::Error;
96  break;
97  case SourceMgr::DK_Note:
98  // Notes are emitted separately from the main diagnostic, so we just treat
99  // them as remarks given that we can't determine the diagnostic to relate
100  // them to.
101  case SourceMgr::DK_Remark:
102  lspDiag.severity = llvm::lsp::DiagnosticSeverity::Information;
103  break;
104  }
105  lspDiag.message = diag.getMessage().str();
106 
107  return lspDiag;
108 }
109 
110 /// Get the base definition of the given record value, or nullptr if one
111 /// couldn't be found.
112 static std::pair<const Record *, const RecordVal *>
113 getBaseValue(const Record *record, const RecordVal *value) {
114  if (value->isTemplateArg())
115  return {nullptr, nullptr};
116 
117  // Find a base value for the field in the super classes of the given record.
118  // On success, `record` is updated to the new parent record.
119  StringRef valueName = value->getName();
120  auto findValueInSupers = [&](const Record *&record) -> const RecordVal * {
121  for (const Record *parentRecord : record->getSuperClasses()) {
122  if (auto *newBase = parentRecord->getValue(valueName)) {
123  record = parentRecord;
124  return newBase;
125  }
126  }
127  return nullptr;
128  };
129 
130  // Try to find the lowest definition of the record value.
131  std::pair<const Record *, const RecordVal *> baseValue = {};
132  while (const RecordVal *newBase = findValueInSupers(record))
133  baseValue = {record, newBase};
134 
135  // Check that the base isn't the same as the current value (e.g. if the value
136  // wasn't overridden).
137  if (!baseValue.second || baseValue.second->getLoc() == value->getLoc())
138  return {nullptr, nullptr};
139  return baseValue;
140 }
141 
142 //===----------------------------------------------------------------------===//
143 // TableGenIndex
144 //===----------------------------------------------------------------------===//
145 
146 namespace {
147 /// This class represents a single symbol definition within a TableGen index. It
148 /// contains the definition of the symbol, the location of the symbol, and any
149 /// recorded references.
150 struct TableGenIndexSymbol {
151  TableGenIndexSymbol(const Record *record)
152  : definition(record),
153  defLoc(convertTokenLocToRange(record->getLoc().front())) {}
154  TableGenIndexSymbol(const RecordVal *value)
155  : definition(value), defLoc(convertTokenLocToRange(value->getLoc())) {}
156  virtual ~TableGenIndexSymbol() = default;
157 
158  // The main definition of the symbol.
160 
161  /// The source location of the definition.
162  SMRange defLoc;
163 
164  /// The source location of the references of the definition.
165  SmallVector<SMRange> references;
166 };
167 /// This class represents a single record symbol.
168 struct TableGenRecordSymbol : public TableGenIndexSymbol {
169  TableGenRecordSymbol(const Record *record) : TableGenIndexSymbol(record) {}
170  ~TableGenRecordSymbol() override = default;
171 
172  static bool classof(const TableGenIndexSymbol *symbol) {
173  return isa<const Record *>(symbol->definition);
174  }
175 
176  /// Return the value of this symbol.
177  const Record *getValue() const { return cast<const Record *>(definition); }
178 };
179 /// This class represents a single record value symbol.
180 struct TableGenRecordValSymbol : public TableGenIndexSymbol {
181  TableGenRecordValSymbol(const Record *record, const RecordVal *value)
182  : TableGenIndexSymbol(value), record(record) {}
183  ~TableGenRecordValSymbol() override = default;
184 
185  static bool classof(const TableGenIndexSymbol *symbol) {
186  return isa<const RecordVal *>(symbol->definition);
187  }
188 
189  /// Return the value of this symbol.
190  const RecordVal *getValue() const {
191  return cast<const RecordVal *>(definition);
192  }
193 
194  /// The parent record of this symbol.
195  const Record *record;
196 };
197 
198 /// This class provides an index for definitions/uses within a TableGen
199 /// document. It provides efficient lookup of a definition given an input source
200 /// range.
201 class TableGenIndex {
202 public:
203  TableGenIndex() : intervalMap(allocator) {}
204 
205  /// Initialize the index with the given RecordKeeper.
206  void initialize(const RecordKeeper &records);
207 
208  /// Lookup a symbol for the given location. Returns nullptr if no symbol could
209  /// be found. If provided, `overlappedRange` is set to the range that the
210  /// provided `loc` overlapped with.
211  const TableGenIndexSymbol *lookup(SMLoc loc,
212  SMRange *overlappedRange = nullptr) const;
213 
214 private:
215  /// The type of interval map used to store source references. SMRange is
216  /// half-open, so we also need to use a half-open interval map.
217  using MapT = llvm::IntervalMap<
218  const char *, const TableGenIndexSymbol *,
219  llvm::IntervalMapImpl::NodeSizer<const char *,
220  const TableGenIndexSymbol *>::LeafSize,
221  llvm::IntervalMapHalfOpenInfo<const char *>>;
222 
223  /// Get or insert a symbol for the given record.
224  TableGenIndexSymbol *getOrInsertDef(const Record *record) {
225  auto it = defToSymbol.try_emplace(record, nullptr);
226  if (it.second)
227  it.first->second = std::make_unique<TableGenRecordSymbol>(record);
228  return &*it.first->second;
229  }
230  /// Get or insert a symbol for the given record value.
231  TableGenIndexSymbol *getOrInsertDef(const Record *record,
232  const RecordVal *value) {
233  auto it = defToSymbol.try_emplace(value, nullptr);
234  if (it.second) {
235  it.first->second =
236  std::make_unique<TableGenRecordValSymbol>(record, value);
237  }
238  return &*it.first->second;
239  }
240 
241  /// An allocator for the interval map.
242  MapT::Allocator allocator;
243 
244  /// An interval map containing a corresponding definition mapped to a source
245  /// interval.
246  MapT intervalMap;
247 
248  /// A mapping between definitions and their corresponding symbol.
250 };
251 } // namespace
252 
253 void TableGenIndex::initialize(const RecordKeeper &records) {
254  intervalMap.clear();
255  defToSymbol.clear();
256 
257  auto insertRef = [&](TableGenIndexSymbol *sym, SMRange refLoc,
258  bool isDef = false) {
259  const char *startLoc = refLoc.Start.getPointer();
260  const char *endLoc = refLoc.End.getPointer();
261 
262  // If the location we got was empty, try to lex a token from the start
263  // location.
264  if (startLoc == endLoc) {
265  refLoc = convertTokenLocToRange(SMLoc::getFromPointer(startLoc));
266  startLoc = refLoc.Start.getPointer();
267  endLoc = refLoc.End.getPointer();
268 
269  // If the location is still empty, bail on trying to use this reference
270  // location.
271  if (startLoc == endLoc)
272  return;
273  }
274 
275  // Check to see if a symbol is already attached to this location.
276  // IntervalMap doesn't allow overlapping inserts, and we don't really
277  // want multiple symbols attached to a source location anyways. This
278  // shouldn't really happen in practice, but we should handle it gracefully.
279  if (!intervalMap.overlaps(startLoc, endLoc))
280  intervalMap.insert(startLoc, endLoc, sym);
281 
282  if (!isDef)
283  sym->references.push_back(refLoc);
284  };
285  auto classes =
286  llvm::make_pointee_range(llvm::make_second_range(records.getClasses()));
287  auto defs =
288  llvm::make_pointee_range(llvm::make_second_range(records.getDefs()));
289  for (const Record &def : llvm::concat<Record>(classes, defs)) {
290  auto *sym = getOrInsertDef(&def);
291  insertRef(sym, sym->defLoc, /*isDef=*/true);
292 
293  // Add references to the definition.
294  for (SMLoc loc : def.getLoc().drop_front())
295  insertRef(sym, convertTokenLocToRange(loc));
296  for (SMRange loc : def.getReferenceLocs())
297  insertRef(sym, loc);
298 
299  // Add definitions for any values.
300  for (const RecordVal &value : def.getValues()) {
301  auto *sym = getOrInsertDef(&def, &value);
302  insertRef(sym, sym->defLoc, /*isDef=*/true);
303  for (SMRange refLoc : value.getReferenceLocs())
304  insertRef(sym, refLoc);
305  }
306  }
307 }
308 
309 const TableGenIndexSymbol *
310 TableGenIndex::lookup(SMLoc loc, SMRange *overlappedRange) const {
311  auto it = intervalMap.find(loc.getPointer());
312  if (!it.valid() || loc.getPointer() < it.start())
313  return nullptr;
314 
315  if (overlappedRange) {
316  *overlappedRange = SMRange(SMLoc::getFromPointer(it.start()),
317  SMLoc::getFromPointer(it.stop()));
318  }
319  return it.value();
320 }
321 
322 //===----------------------------------------------------------------------===//
323 // TableGenTextFile
324 //===----------------------------------------------------------------------===//
325 
326 namespace {
327 /// This class represents a text file containing one or more TableGen documents.
328 class TableGenTextFile {
329 public:
330  TableGenTextFile(const llvm::lsp::URIForFile &uri, StringRef fileContents,
331  int64_t version,
332  const std::vector<std::string> &extraIncludeDirs,
333  std::vector<llvm::lsp::Diagnostic> &diagnostics);
334 
335  /// Return the current version of this text file.
336  int64_t getVersion() const { return version; }
337 
338  /// Update the file to the new version using the provided set of content
339  /// changes. Returns failure if the update was unsuccessful.
340  LogicalResult
341  update(const llvm::lsp::URIForFile &uri, int64_t newVersion,
343  std::vector<llvm::lsp::Diagnostic> &diagnostics);
344 
345  //===--------------------------------------------------------------------===//
346  // Definitions and References
347  //===--------------------------------------------------------------------===//
348 
349  void getLocationsOf(const llvm::lsp::URIForFile &uri,
350  const llvm::lsp::Position &defPos,
351  std::vector<llvm::lsp::Location> &locations);
352  void findReferencesOf(const llvm::lsp::URIForFile &uri,
353  const llvm::lsp::Position &pos,
354  std::vector<llvm::lsp::Location> &references);
355 
356  //===--------------------------------------------------------------------===//
357  // Document Links
358  //===--------------------------------------------------------------------===//
359 
360  void getDocumentLinks(const llvm::lsp::URIForFile &uri,
361  std::vector<llvm::lsp::DocumentLink> &links);
362 
363  //===--------------------------------------------------------------------===//
364  // Hover
365  //===--------------------------------------------------------------------===//
366 
367  std::optional<llvm::lsp::Hover>
368  findHover(const llvm::lsp::URIForFile &uri,
369  const llvm::lsp::Position &hoverPos);
370  llvm::lsp::Hover buildHoverForRecord(const Record *record,
371  const SMRange &hoverRange);
372  llvm::lsp::Hover buildHoverForTemplateArg(const Record *record,
373  const RecordVal *value,
374  const SMRange &hoverRange);
375  llvm::lsp::Hover buildHoverForField(const Record *record,
376  const RecordVal *value,
377  const SMRange &hoverRange);
378 
379 private:
380  /// Initialize the text file from the given file contents.
381  void initialize(const llvm::lsp::URIForFile &uri, int64_t newVersion,
382  std::vector<llvm::lsp::Diagnostic> &diagnostics);
383 
384  /// The full string contents of the file.
385  std::string contents;
386 
387  /// The version of this file.
388  int64_t version;
389 
390  /// The include directories for this file.
391  std::vector<std::string> includeDirs;
392 
393  /// The source manager containing the contents of the input file.
394  SourceMgr sourceMgr;
395 
396  /// The record keeper containing the parsed tablegen constructs.
397  std::unique_ptr<RecordKeeper> recordKeeper;
398 
399  /// The index of the parsed file.
400  TableGenIndex index;
401 
402  /// The set of includes of the parsed file.
403  SmallVector<lsp::SourceMgrInclude> parsedIncludes;
404 };
405 } // namespace
406 
407 TableGenTextFile::TableGenTextFile(
408  const llvm::lsp::URIForFile &uri, StringRef fileContents, int64_t version,
409  const std::vector<std::string> &extraIncludeDirs,
410  std::vector<llvm::lsp::Diagnostic> &diagnostics)
411  : contents(fileContents.str()), version(version) {
412  // Build the set of include directories for this file.
413  llvm::SmallString<32> uriDirectory(uri.file());
414  llvm::sys::path::remove_filename(uriDirectory);
415  includeDirs.push_back(uriDirectory.str().str());
416  llvm::append_range(includeDirs, extraIncludeDirs);
417 
418  // Initialize the file.
419  initialize(uri, version, diagnostics);
420 }
421 
422 LogicalResult TableGenTextFile::update(
423  const llvm::lsp::URIForFile &uri, int64_t newVersion,
425  std::vector<llvm::lsp::Diagnostic> &diagnostics) {
426  if (failed(llvm::lsp::TextDocumentContentChangeEvent::applyTo(changes,
427  contents))) {
428  llvm::lsp::Logger::error("Failed to update contents of {0}", uri.file());
429  return failure();
430  }
431 
432  // If the file contents were properly changed, reinitialize the text file.
433  initialize(uri, newVersion, diagnostics);
434  return success();
435 }
436 
437 void TableGenTextFile::initialize(
438  const llvm::lsp::URIForFile &uri, int64_t newVersion,
439  std::vector<llvm::lsp::Diagnostic> &diagnostics) {
440  version = newVersion;
441  sourceMgr = SourceMgr();
442  recordKeeper = std::make_unique<RecordKeeper>();
443 
444  // Build a buffer for this file.
445  auto memBuffer = llvm::MemoryBuffer::getMemBuffer(contents, uri.file());
446  if (!memBuffer) {
447  llvm::lsp::Logger::error("Failed to create memory buffer for file",
448  uri.file());
449  return;
450  }
451  sourceMgr.setIncludeDirs(includeDirs);
452  sourceMgr.setVirtualFileSystem(llvm::vfs::getRealFileSystem());
453  sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
454 
455  // This class provides a context argument for the SourceMgr diagnostic
456  // handler.
457  struct DiagHandlerContext {
458  std::vector<llvm::lsp::Diagnostic> &diagnostics;
459  const llvm::lsp::URIForFile &uri;
460  } handlerContext{diagnostics, uri};
461 
462  // Set the diagnostic handler for the tablegen source manager.
463  sourceMgr.setDiagHandler(
464  [](const llvm::SMDiagnostic &diag, void *rawHandlerContext) {
465  auto *ctx = reinterpret_cast<DiagHandlerContext *>(rawHandlerContext);
466  if (auto lspDiag = getLspDiagnoticFromDiag(diag, ctx->uri))
467  ctx->diagnostics.push_back(*lspDiag);
468  },
469  &handlerContext);
470  bool failedToParse = llvm::TableGenParseFile(sourceMgr, *recordKeeper);
471 
472  // Process all of the include files.
473  lsp::gatherIncludeFiles(sourceMgr, parsedIncludes);
474  if (failedToParse)
475  return;
476 
477  // If we successfully parsed the file, we can now build the index.
478  index.initialize(*recordKeeper);
479 }
480 
481 //===----------------------------------------------------------------------===//
482 // TableGenTextFile: Definitions and References
483 //===----------------------------------------------------------------------===//
484 
485 void TableGenTextFile::getLocationsOf(
486  const llvm::lsp::URIForFile &uri, const llvm::lsp::Position &defPos,
487  std::vector<llvm::lsp::Location> &locations) {
488  SMLoc posLoc = defPos.getAsSMLoc(sourceMgr);
489  const TableGenIndexSymbol *symbol = index.lookup(posLoc);
490  if (!symbol)
491  return;
492 
493  // If this symbol is a record value and the def position is already the def of
494  // the symbol, check to see if the value has a base definition. This allows
495  // for a "go-to-def" on a "let" to resolve the definition in the base class.
496  auto *valSym = dyn_cast<TableGenRecordValSymbol>(symbol);
497  if (valSym && lsp::contains(valSym->defLoc, posLoc)) {
498  if (auto *val = getBaseValue(valSym->record, valSym->getValue()).second) {
499  locations.push_back(getLocationFromLoc(sourceMgr, val->getLoc(), uri));
500  return;
501  }
502  }
503 
504  locations.push_back(getLocationFromLoc(sourceMgr, symbol->defLoc, uri));
505 }
506 
507 void TableGenTextFile::findReferencesOf(
508  const llvm::lsp::URIForFile &uri, const llvm::lsp::Position &pos,
509  std::vector<llvm::lsp::Location> &references) {
510  SMLoc posLoc = pos.getAsSMLoc(sourceMgr);
511  const TableGenIndexSymbol *symbol = index.lookup(posLoc);
512  if (!symbol)
513  return;
514 
515  references.push_back(getLocationFromLoc(sourceMgr, symbol->defLoc, uri));
516  for (SMRange refLoc : symbol->references)
517  references.push_back(getLocationFromLoc(sourceMgr, refLoc, uri));
518 }
519 
520 //===--------------------------------------------------------------------===//
521 // TableGenTextFile: Document Links
522 //===--------------------------------------------------------------------===//
523 
524 void TableGenTextFile::getDocumentLinks(
525  const llvm::lsp::URIForFile &uri,
526  std::vector<llvm::lsp::DocumentLink> &links) {
527  for (const lsp::SourceMgrInclude &include : parsedIncludes)
528  links.emplace_back(include.range, include.uri);
529 }
530 
531 //===----------------------------------------------------------------------===//
532 // TableGenTextFile: Hover
533 //===----------------------------------------------------------------------===//
534 
535 std::optional<llvm::lsp::Hover>
536 TableGenTextFile::findHover(const llvm::lsp::URIForFile &uri,
537  const llvm::lsp::Position &hoverPos) {
538  // Check for a reference to an include.
539  for (const lsp::SourceMgrInclude &include : parsedIncludes)
540  if (include.range.contains(hoverPos))
541  return include.buildHover();
542 
543  // Find the symbol at the given location.
544  SMRange hoverRange;
545  SMLoc posLoc = hoverPos.getAsSMLoc(sourceMgr);
546  const TableGenIndexSymbol *symbol = index.lookup(posLoc, &hoverRange);
547  if (!symbol)
548  return std::nullopt;
549 
550  // Build hover for a Record.
551  if (auto *record = dyn_cast<TableGenRecordSymbol>(symbol))
552  return buildHoverForRecord(record->getValue(), hoverRange);
553 
554  // Build hover for a RecordVal, which is either a template argument or a
555  // field.
556  auto *recordVal = cast<TableGenRecordValSymbol>(symbol);
557  const RecordVal *value = recordVal->getValue();
558  if (value->isTemplateArg())
559  return buildHoverForTemplateArg(recordVal->record, value, hoverRange);
560  return buildHoverForField(recordVal->record, value, hoverRange);
561 }
562 
563 llvm::lsp::Hover
564 TableGenTextFile::buildHoverForRecord(const Record *record,
565  const SMRange &hoverRange) {
566  llvm::lsp::Hover hover(llvm::lsp::Range(sourceMgr, hoverRange));
567  {
568  llvm::raw_string_ostream hoverOS(hover.contents.value);
569 
570  // Format the type of record this is.
571  if (record->isClass()) {
572  hoverOS << "**class** `" << record->getName() << "`";
573  } else if (record->isAnonymous()) {
574  hoverOS << "**anonymous class**";
575  } else {
576  hoverOS << "**def** `" << record->getName() << "`";
577  }
578  hoverOS << "\n***\n";
579 
580  // Check if this record has summary/description fields. These are often used
581  // to hold documentation for the record.
582  auto printAndFormatField = [&](StringRef fieldName) {
583  // Check that the record actually has the given field, and that it's a
584  // string.
585  const RecordVal *value = record->getValue(fieldName);
586  if (!value || !value->getValue())
587  return;
588  auto *stringValue = dyn_cast<llvm::StringInit>(value->getValue());
589  if (!stringValue)
590  return;
591 
592  raw_indented_ostream ros(hoverOS);
593  ros.printReindented(stringValue->getValue().rtrim(" \t"));
594  hoverOS << "\n***\n";
595  };
596  printAndFormatField("summary");
597  printAndFormatField("description");
598 
599  // Check for documentation in the source file.
600  if (std::optional<std::string> doc =
601  lsp::extractSourceDocComment(sourceMgr, record->getLoc().front())) {
602  hoverOS << "\n" << *doc << "\n";
603  }
604  }
605  return hover;
606 }
607 
608 llvm::lsp::Hover TableGenTextFile::buildHoverForTemplateArg(
609  const Record *record, const RecordVal *value, const SMRange &hoverRange) {
610  llvm::lsp::Hover hover(llvm::lsp::Range(sourceMgr, hoverRange));
611  {
612  llvm::raw_string_ostream hoverOS(hover.contents.value);
613  StringRef name = value->getName().rsplit(':').second;
614 
615  hoverOS << "**template arg** `" << name << "`\n***\nType: `";
616  value->getType()->print(hoverOS);
617  hoverOS << "`\n";
618  }
619  return hover;
620 }
621 
622 llvm::lsp::Hover TableGenTextFile::buildHoverForField(
623  const Record *record, const RecordVal *value, const SMRange &hoverRange) {
624  llvm::lsp::Hover hover(llvm::lsp::Range(sourceMgr, hoverRange));
625  {
626  llvm::raw_string_ostream hoverOS(hover.contents.value);
627  hoverOS << "**field** `" << value->getName() << "`\n***\nType: `";
628  value->getType()->print(hoverOS);
629  hoverOS << "`\n***\n";
630 
631  // Check for documentation in the source file.
632  if (std::optional<std::string> doc =
633  lsp::extractSourceDocComment(sourceMgr, value->getLoc())) {
634  hoverOS << "\n" << *doc << "\n";
635  hoverOS << "\n***\n";
636  }
637 
638  // Check to see if there is a base value that we can use for
639  // documentation.
640  auto [baseRecord, baseValue] = getBaseValue(record, value);
641  if (baseValue) {
642  if (std::optional<std::string> doc =
643  lsp::extractSourceDocComment(sourceMgr, baseValue->getLoc())) {
644  hoverOS << "\n *From `" << baseRecord->getName() << "`*:\n\n"
645  << *doc << "\n";
646  }
647  }
648  }
649  return hover;
650 }
651 
652 //===----------------------------------------------------------------------===//
653 // TableGenServer::Impl
654 //===----------------------------------------------------------------------===//
655 
657  explicit Impl(const Options &options)
658  : options(options), compilationDatabase(options.compilationDatabases) {}
659 
660  /// TableGen LSP options.
661  const Options &options;
662 
663  /// The compilation database containing additional information for files
664  /// passed to the server.
666 
667  /// The files held by the server, mapped by their URI file name.
668  llvm::StringMap<std::unique_ptr<TableGenTextFile>> files;
669 };
670 
671 //===----------------------------------------------------------------------===//
672 // TableGenServer
673 //===----------------------------------------------------------------------===//
674 
675 lsp::TableGenServer::TableGenServer(const Options &options)
676  : impl(std::make_unique<Impl>(options)) {}
678 
679 void lsp::TableGenServer::addDocument(const URIForFile &uri, StringRef contents,
680  int64_t version,
681  std::vector<Diagnostic> &diagnostics) {
682  // Build the set of additional include directories.
683  std::vector<std::string> additionalIncludeDirs = impl->options.extraDirs;
684  const auto &fileInfo = impl->compilationDatabase.getFileInfo(uri.file());
685  llvm::append_range(additionalIncludeDirs, fileInfo.includeDirs);
686 
687  impl->files[uri.file()] = std::make_unique<TableGenTextFile>(
688  uri, contents, version, additionalIncludeDirs, diagnostics);
689 }
690 
692  const URIForFile &uri, ArrayRef<TextDocumentContentChangeEvent> changes,
693  int64_t version, std::vector<Diagnostic> &diagnostics) {
694  // Check that we actually have a document for this uri.
695  auto it = impl->files.find(uri.file());
696  if (it == impl->files.end())
697  return;
698 
699  // Try to update the document. If we fail, erase the file from the server. A
700  // failed updated generally means we've fallen out of sync somewhere.
701  if (failed(it->second->update(uri, version, changes, diagnostics)))
702  impl->files.erase(it);
703 }
704 
705 std::optional<int64_t>
706 lsp::TableGenServer::removeDocument(const URIForFile &uri) {
707  auto it = impl->files.find(uri.file());
708  if (it == impl->files.end())
709  return std::nullopt;
710 
711  int64_t version = it->second->getVersion();
712  impl->files.erase(it);
713  return version;
714 }
715 
716 void lsp::TableGenServer::getLocationsOf(const URIForFile &uri,
717  const Position &defPos,
718  std::vector<Location> &locations) {
719  auto fileIt = impl->files.find(uri.file());
720  if (fileIt != impl->files.end())
721  fileIt->second->getLocationsOf(uri, defPos, locations);
722 }
723 
724 void lsp::TableGenServer::findReferencesOf(const URIForFile &uri,
725  const Position &pos,
726  std::vector<Location> &references) {
727  auto fileIt = impl->files.find(uri.file());
728  if (fileIt != impl->files.end())
729  fileIt->second->findReferencesOf(uri, pos, references);
730 }
731 
733  const URIForFile &uri, std::vector<DocumentLink> &documentLinks) {
734  auto fileIt = impl->files.find(uri.file());
735  if (fileIt != impl->files.end())
736  return fileIt->second->getDocumentLinks(uri, documentLinks);
737 }
738 
739 std::optional<llvm::lsp::Hover>
740 lsp::TableGenServer::findHover(const URIForFile &uri,
741  const Position &hoverPos) {
742  auto fileIt = impl->files.find(uri.file());
743  if (fileIt != impl->files.end())
744  return fileIt->second->findHover(uri, hoverPos);
745  return std::nullopt;
746 }
static std::string toString(bytecode::Section::ID sectionID)
Stringify the given section ID.
static bool contains(SMRange range, SMLoc loc)
Returns true if the given range contains the given source location.
Definition: MLIRServer.cpp:111
@ Error
static std::string diag(const llvm::Value &value)
static llvm::ManagedStatic< PassManagerOptions > options
static std::pair< const Record *, const RecordVal * > getBaseValue(const Record *record, const RecordVal *value)
Get the base definition of the given record value, or nullptr if one couldn't be found.
static llvm::lsp::URIForFile getURIFromLoc(const SourceMgr &mgr, SMLoc loc, const llvm::lsp::URIForFile &mainFileURI)
Returns a language server uri for the given source location.
static llvm::lsp::Location getLocationFromLoc(SourceMgr &mgr, SMRange loc, const llvm::lsp::URIForFile &uri)
Returns a language server location from the given source range.
static std::optional< llvm::lsp::Diagnostic > getLspDiagnoticFromDiag(const llvm::SMDiagnostic &diag, const llvm::lsp::URIForFile &uri)
Convert the given TableGen diagnostic to the LSP form.
static SMRange convertTokenLocToRange(SMLoc loc)
Returns the range of a lexical token given a SMLoc corresponding to the start of an token location.
This class contains a collection of compilation information for files provided to the language server...
std::optional< int64_t > removeDocument(const URIForFile &uri)
Remove the document with the given uri.
void updateDocument(const URIForFile &uri, ArrayRef< TextDocumentContentChangeEvent > changes, int64_t version, std::vector< Diagnostic > &diagnostics)
Update the document, with the provided version, at the given URI.
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.
void getDocumentLinks(const URIForFile &uri, std::vector< DocumentLink > &documentLinks)
Return the document links referenced by the given file.
void addDocument(const URIForFile &uri, StringRef contents, int64_t version, std::vector< Diagnostic > &diagnostics)
Add the document, with the provided version, at the given URI.
void getLocationsOf(const URIForFile &uri, const Position &defPos, std::vector< Location > &locations)
Return the locations of the object pointed at by the given position.
void findReferencesOf(const URIForFile &uri, const Position &pos, std::vector< Location > &references)
Find all references of the object pointed at by the given position.
raw_ostream subclass that simplifies indention a sequence of code.
void gatherIncludeFiles(llvm::SourceMgr &sourceMgr, SmallVectorImpl< SourceMgrInclude > &includes)
Given a source manager, gather all of the processed include files.
std::optional< std::string > extractSourceDocComment(llvm::SourceMgr &sourceMgr, SMLoc loc)
Extract a documentation comment for the given location within the source manager.
SMRange convertTokenLocToRange(SMLoc loc, StringRef identifierChars="")
Returns the range of a lexical token given a SMLoc corresponding to the start of an token location.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition: Remarks.h:561
Include the generated interface declarations.
Impl(const Options &options)
lsp::CompilationDatabase compilationDatabase
The compilation database containing additional information for files passed to the server.
const Options & options
TableGen LSP options.
llvm::StringMap< std::unique_ptr< TableGenTextFile > > files
The files held by the server, mapped by their URI file name.
This class represents a single include within a root file.