MLIR  19.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 
17 #include "llvm/ADT/IntervalMap.h"
18 #include "llvm/ADT/PointerUnion.h"
19 #include "llvm/ADT/StringMap.h"
20 #include "llvm/ADT/StringSet.h"
21 #include "llvm/ADT/TypeSwitch.h"
22 #include "llvm/Support/FileSystem.h"
23 #include "llvm/Support/Path.h"
24 #include "llvm/TableGen/Parser.h"
25 #include "llvm/TableGen/Record.h"
26 #include <optional>
27 
28 using namespace mlir;
29 
30 /// Returns the range of a lexical token given a SMLoc corresponding to the
31 /// start of an token location. The range is computed heuristically, and
32 /// supports identifier-like tokens, strings, etc.
33 static SMRange convertTokenLocToRange(SMLoc loc) {
34  return lsp::convertTokenLocToRange(loc, "$");
35 }
36 
37 /// Returns a language server uri for the given source location. `mainFileURI`
38 /// corresponds to the uri for the main file of the source manager.
39 static lsp::URIForFile getURIFromLoc(const llvm::SourceMgr &mgr, SMLoc loc,
40  const lsp::URIForFile &mainFileURI) {
41  int bufferId = mgr.FindBufferContainingLoc(loc);
42  if (bufferId == 0 || bufferId == static_cast<int>(mgr.getMainFileID()))
43  return mainFileURI;
45  mgr.getBufferInfo(bufferId).Buffer->getBufferIdentifier());
46  if (fileForLoc)
47  return *fileForLoc;
48  lsp::Logger::error("Failed to create URI for include file: {0}",
49  llvm::toString(fileForLoc.takeError()));
50  return mainFileURI;
51 }
52 
53 /// Returns a language server location from the given source range.
54 static lsp::Location getLocationFromLoc(llvm::SourceMgr &mgr, SMRange loc,
55  const lsp::URIForFile &uri) {
56  return lsp::Location(getURIFromLoc(mgr, loc.Start, uri),
57  lsp::Range(mgr, loc));
58 }
59 static lsp::Location getLocationFromLoc(llvm::SourceMgr &mgr, SMLoc loc,
60  const lsp::URIForFile &uri) {
61  return getLocationFromLoc(mgr, convertTokenLocToRange(loc), uri);
62 }
63 
64 /// Convert the given TableGen diagnostic to the LSP form.
65 static std::optional<lsp::Diagnostic>
66 getLspDiagnoticFromDiag(const llvm::SMDiagnostic &diag,
67  const lsp::URIForFile &uri) {
68  auto *sourceMgr = const_cast<llvm::SourceMgr *>(diag.getSourceMgr());
69  if (!sourceMgr || !diag.getLoc().isValid())
70  return std::nullopt;
71 
72  lsp::Diagnostic lspDiag;
73  lspDiag.source = "tablegen";
74  lspDiag.category = "Parse Error";
75 
76  // Try to grab a file location for this diagnostic.
77  lsp::Location loc = getLocationFromLoc(*sourceMgr, diag.getLoc(), uri);
78  lspDiag.range = loc.range;
79 
80  // Skip diagnostics that weren't emitted within the main file.
81  if (loc.uri != uri)
82  return std::nullopt;
83 
84  // Convert the severity for the diagnostic.
85  switch (diag.getKind()) {
86  case llvm::SourceMgr::DK_Warning:
88  break;
89  case llvm::SourceMgr::DK_Error:
91  break;
92  case llvm::SourceMgr::DK_Note:
93  // Notes are emitted separately from the main diagnostic, so we just treat
94  // them as remarks given that we can't determine the diagnostic to relate
95  // them to.
96  case llvm::SourceMgr::DK_Remark:
98  break;
99  }
100  lspDiag.message = diag.getMessage().str();
101 
102  return lspDiag;
103 }
104 
105 /// Get the base definition of the given record value, or nullptr if one
106 /// couldn't be found.
107 static std::pair<const llvm::Record *, const llvm::RecordVal *>
108 getBaseValue(const llvm::Record *record, const llvm::RecordVal *value) {
109  if (value->isTemplateArg())
110  return {nullptr, nullptr};
111 
112  // Find a base value for the field in the super classes of the given record.
113  // On success, `record` is updated to the new parent record.
114  StringRef valueName = value->getName();
115  auto findValueInSupers =
116  [&](const llvm::Record *&record) -> llvm::RecordVal * {
117  for (auto [parentRecord, loc] : record->getSuperClasses()) {
118  if (auto *newBase = parentRecord->getValue(valueName)) {
119  record = parentRecord;
120  return newBase;
121  }
122  }
123  return nullptr;
124  };
125 
126  // Try to find the lowest definition of the record value.
127  std::pair<const llvm::Record *, const llvm::RecordVal *> baseValue = {};
128  while (const llvm::RecordVal *newBase = findValueInSupers(record))
129  baseValue = {record, newBase};
130 
131  // Check that the base isn't the same as the current value (e.g. if the value
132  // wasn't overridden).
133  if (!baseValue.second || baseValue.second->getLoc() == value->getLoc())
134  return {nullptr, nullptr};
135  return baseValue;
136 }
137 
138 //===----------------------------------------------------------------------===//
139 // TableGenIndex
140 //===----------------------------------------------------------------------===//
141 
142 namespace {
143 /// This class represents a single symbol definition within a TableGen index. It
144 /// contains the definition of the symbol, the location of the symbol, and any
145 /// recorded references.
146 struct TableGenIndexSymbol {
147  TableGenIndexSymbol(const llvm::Record *record)
148  : definition(record),
149  defLoc(convertTokenLocToRange(record->getLoc().front())) {}
150  TableGenIndexSymbol(const llvm::RecordVal *value)
151  : definition(value), defLoc(convertTokenLocToRange(value->getLoc())) {}
152  virtual ~TableGenIndexSymbol() = default;
153 
154  // The main definition of the symbol.
156 
157  /// The source location of the definition.
158  SMRange defLoc;
159 
160  /// The source location of the references of the definition.
161  SmallVector<SMRange> references;
162 };
163 /// This class represents a single record symbol.
164 struct TableGenRecordSymbol : public TableGenIndexSymbol {
165  TableGenRecordSymbol(const llvm::Record *record)
166  : TableGenIndexSymbol(record) {}
167  ~TableGenRecordSymbol() override = default;
168 
169  static bool classof(const TableGenIndexSymbol *symbol) {
170  return symbol->definition.is<const llvm::Record *>();
171  }
172 
173  /// Return the value of this symbol.
174  const llvm::Record *getValue() const {
175  return definition.get<const llvm::Record *>();
176  }
177 };
178 /// This class represents a single record value symbol.
179 struct TableGenRecordValSymbol : public TableGenIndexSymbol {
180  TableGenRecordValSymbol(const llvm::Record *record,
181  const llvm::RecordVal *value)
182  : TableGenIndexSymbol(value), record(record) {}
183  ~TableGenRecordValSymbol() override = default;
184 
185  static bool classof(const TableGenIndexSymbol *symbol) {
186  return symbol->definition.is<const llvm::RecordVal *>();
187  }
188 
189  /// Return the value of this symbol.
190  const llvm::RecordVal *getValue() const {
191  return definition.get<const llvm::RecordVal *>();
192  }
193 
194  /// The parent record of this symbol.
195  const llvm::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 llvm::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 llvm::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 llvm::Record *record,
232  const llvm::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 llvm::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 llvm::Record &def : llvm::concat<llvm::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 llvm::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 lsp::URIForFile &uri, StringRef fileContents,
331  int64_t version,
332  const std::vector<std::string> &extraIncludeDirs,
333  std::vector<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 update(const lsp::URIForFile &uri, int64_t newVersion,
342  std::vector<lsp::Diagnostic> &diagnostics);
343 
344  //===--------------------------------------------------------------------===//
345  // Definitions and References
346  //===--------------------------------------------------------------------===//
347 
348  void getLocationsOf(const lsp::URIForFile &uri, const lsp::Position &defPos,
349  std::vector<lsp::Location> &locations);
350  void findReferencesOf(const lsp::URIForFile &uri, const lsp::Position &pos,
351  std::vector<lsp::Location> &references);
352 
353  //===--------------------------------------------------------------------===//
354  // Document Links
355  //===--------------------------------------------------------------------===//
356 
357  void getDocumentLinks(const lsp::URIForFile &uri,
358  std::vector<lsp::DocumentLink> &links);
359 
360  //===--------------------------------------------------------------------===//
361  // Hover
362  //===--------------------------------------------------------------------===//
363 
364  std::optional<lsp::Hover> findHover(const lsp::URIForFile &uri,
365  const lsp::Position &hoverPos);
366  lsp::Hover buildHoverForRecord(const llvm::Record *record,
367  const SMRange &hoverRange);
368  lsp::Hover buildHoverForTemplateArg(const llvm::Record *record,
369  const llvm::RecordVal *value,
370  const SMRange &hoverRange);
371  lsp::Hover buildHoverForField(const llvm::Record *record,
372  const llvm::RecordVal *value,
373  const SMRange &hoverRange);
374 
375 private:
376  /// Initialize the text file from the given file contents.
377  void initialize(const lsp::URIForFile &uri, int64_t newVersion,
378  std::vector<lsp::Diagnostic> &diagnostics);
379 
380  /// The full string contents of the file.
381  std::string contents;
382 
383  /// The version of this file.
384  int64_t version;
385 
386  /// The include directories for this file.
387  std::vector<std::string> includeDirs;
388 
389  /// The source manager containing the contents of the input file.
390  llvm::SourceMgr sourceMgr;
391 
392  /// The record keeper containing the parsed tablegen constructs.
393  std::unique_ptr<llvm::RecordKeeper> recordKeeper;
394 
395  /// The index of the parsed file.
396  TableGenIndex index;
397 
398  /// The set of includes of the parsed file.
399  SmallVector<lsp::SourceMgrInclude> parsedIncludes;
400 };
401 } // namespace
402 
403 TableGenTextFile::TableGenTextFile(
404  const lsp::URIForFile &uri, StringRef fileContents, int64_t version,
405  const std::vector<std::string> &extraIncludeDirs,
406  std::vector<lsp::Diagnostic> &diagnostics)
407  : contents(fileContents.str()), version(version) {
408  // Build the set of include directories for this file.
409  llvm::SmallString<32> uriDirectory(uri.file());
410  llvm::sys::path::remove_filename(uriDirectory);
411  includeDirs.push_back(uriDirectory.str().str());
412  includeDirs.insert(includeDirs.end(), extraIncludeDirs.begin(),
413  extraIncludeDirs.end());
414 
415  // Initialize the file.
416  initialize(uri, version, diagnostics);
417 }
418 
420 TableGenTextFile::update(const lsp::URIForFile &uri, int64_t newVersion,
422  std::vector<lsp::Diagnostic> &diagnostics) {
423  if (failed(lsp::TextDocumentContentChangeEvent::applyTo(changes, contents))) {
424  lsp::Logger::error("Failed to update contents of {0}", uri.file());
425  return failure();
426  }
427 
428  // If the file contents were properly changed, reinitialize the text file.
429  initialize(uri, newVersion, diagnostics);
430  return success();
431 }
432 
433 void TableGenTextFile::initialize(const lsp::URIForFile &uri,
434  int64_t newVersion,
435  std::vector<lsp::Diagnostic> &diagnostics) {
436  version = newVersion;
437  sourceMgr = llvm::SourceMgr();
438  recordKeeper = std::make_unique<llvm::RecordKeeper>();
439 
440  // Build a buffer for this file.
441  auto memBuffer = llvm::MemoryBuffer::getMemBuffer(contents, uri.file());
442  if (!memBuffer) {
443  lsp::Logger::error("Failed to create memory buffer for file", uri.file());
444  return;
445  }
446  sourceMgr.setIncludeDirs(includeDirs);
447  sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
448 
449  // This class provides a context argument for the llvm::SourceMgr diagnostic
450  // handler.
451  struct DiagHandlerContext {
452  std::vector<lsp::Diagnostic> &diagnostics;
453  const lsp::URIForFile &uri;
454  } handlerContext{diagnostics, uri};
455 
456  // Set the diagnostic handler for the tablegen source manager.
457  sourceMgr.setDiagHandler(
458  [](const llvm::SMDiagnostic &diag, void *rawHandlerContext) {
459  auto *ctx = reinterpret_cast<DiagHandlerContext *>(rawHandlerContext);
460  if (auto lspDiag = getLspDiagnoticFromDiag(diag, ctx->uri))
461  ctx->diagnostics.push_back(*lspDiag);
462  },
463  &handlerContext);
464  bool failedToParse = llvm::TableGenParseFile(sourceMgr, *recordKeeper);
465 
466  // Process all of the include files.
467  lsp::gatherIncludeFiles(sourceMgr, parsedIncludes);
468  if (failedToParse)
469  return;
470 
471  // If we successfully parsed the file, we can now build the index.
472  index.initialize(*recordKeeper);
473 }
474 
475 //===----------------------------------------------------------------------===//
476 // TableGenTextFile: Definitions and References
477 //===----------------------------------------------------------------------===//
478 
479 void TableGenTextFile::getLocationsOf(const lsp::URIForFile &uri,
480  const lsp::Position &defPos,
481  std::vector<lsp::Location> &locations) {
482  SMLoc posLoc = defPos.getAsSMLoc(sourceMgr);
483  const TableGenIndexSymbol *symbol = index.lookup(posLoc);
484  if (!symbol)
485  return;
486 
487  // If this symbol is a record value and the def position is already the def of
488  // the symbol, check to see if the value has a base definition. This allows
489  // for a "go-to-def" on a "let" to resolve the definition in the base class.
490  auto *valSym = dyn_cast<TableGenRecordValSymbol>(symbol);
491  if (valSym && lsp::contains(valSym->defLoc, posLoc)) {
492  if (auto *val = getBaseValue(valSym->record, valSym->getValue()).second) {
493  locations.push_back(getLocationFromLoc(sourceMgr, val->getLoc(), uri));
494  return;
495  }
496  }
497 
498  locations.push_back(getLocationFromLoc(sourceMgr, symbol->defLoc, uri));
499 }
500 
501 void TableGenTextFile::findReferencesOf(
502  const lsp::URIForFile &uri, const lsp::Position &pos,
503  std::vector<lsp::Location> &references) {
504  SMLoc posLoc = pos.getAsSMLoc(sourceMgr);
505  const TableGenIndexSymbol *symbol = index.lookup(posLoc);
506  if (!symbol)
507  return;
508 
509  references.push_back(getLocationFromLoc(sourceMgr, symbol->defLoc, uri));
510  for (SMRange refLoc : symbol->references)
511  references.push_back(getLocationFromLoc(sourceMgr, refLoc, uri));
512 }
513 
514 //===--------------------------------------------------------------------===//
515 // TableGenTextFile: Document Links
516 //===--------------------------------------------------------------------===//
517 
518 void TableGenTextFile::getDocumentLinks(const lsp::URIForFile &uri,
519  std::vector<lsp::DocumentLink> &links) {
520  for (const lsp::SourceMgrInclude &include : parsedIncludes)
521  links.emplace_back(include.range, include.uri);
522 }
523 
524 //===----------------------------------------------------------------------===//
525 // TableGenTextFile: Hover
526 //===----------------------------------------------------------------------===//
527 
528 std::optional<lsp::Hover>
529 TableGenTextFile::findHover(const lsp::URIForFile &uri,
530  const lsp::Position &hoverPos) {
531  // Check for a reference to an include.
532  for (const lsp::SourceMgrInclude &include : parsedIncludes)
533  if (include.range.contains(hoverPos))
534  return include.buildHover();
535 
536  // Find the symbol at the given location.
537  SMRange hoverRange;
538  SMLoc posLoc = hoverPos.getAsSMLoc(sourceMgr);
539  const TableGenIndexSymbol *symbol = index.lookup(posLoc, &hoverRange);
540  if (!symbol)
541  return std::nullopt;
542 
543  // Build hover for a Record.
544  if (auto *record = dyn_cast<TableGenRecordSymbol>(symbol))
545  return buildHoverForRecord(record->getValue(), hoverRange);
546 
547  // Build hover for a RecordVal, which is either a template argument or a
548  // field.
549  auto *recordVal = cast<TableGenRecordValSymbol>(symbol);
550  const llvm::RecordVal *value = recordVal->getValue();
551  if (value->isTemplateArg())
552  return buildHoverForTemplateArg(recordVal->record, value, hoverRange);
553  return buildHoverForField(recordVal->record, value, hoverRange);
554 }
555 
556 lsp::Hover TableGenTextFile::buildHoverForRecord(const llvm::Record *record,
557  const SMRange &hoverRange) {
558  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
559  {
560  llvm::raw_string_ostream hoverOS(hover.contents.value);
561 
562  // Format the type of record this is.
563  if (record->isClass()) {
564  hoverOS << "**class** `" << record->getName() << "`";
565  } else if (record->isAnonymous()) {
566  hoverOS << "**anonymous class**";
567  } else {
568  hoverOS << "**def** `" << record->getName() << "`";
569  }
570  hoverOS << "\n***\n";
571 
572  // Check if this record has summary/description fields. These are often used
573  // to hold documentation for the record.
574  auto printAndFormatField = [&](StringRef fieldName) {
575  // Check that the record actually has the given field, and that it's a
576  // string.
577  const llvm::RecordVal *value = record->getValue(fieldName);
578  if (!value || !value->getValue())
579  return;
580  auto *stringValue = dyn_cast<llvm::StringInit>(value->getValue());
581  if (!stringValue)
582  return;
583 
584  raw_indented_ostream ros(hoverOS);
585  ros.printReindented(stringValue->getValue().rtrim(" \t"));
586  hoverOS << "\n***\n";
587  };
588  printAndFormatField("summary");
589  printAndFormatField("description");
590 
591  // Check for documentation in the source file.
592  if (std::optional<std::string> doc =
593  lsp::extractSourceDocComment(sourceMgr, record->getLoc().front())) {
594  hoverOS << "\n" << *doc << "\n";
595  }
596  }
597  return hover;
598 }
599 
601 TableGenTextFile::buildHoverForTemplateArg(const llvm::Record *record,
602  const llvm::RecordVal *value,
603  const SMRange &hoverRange) {
604  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
605  {
606  llvm::raw_string_ostream hoverOS(hover.contents.value);
607  StringRef name = value->getName().rsplit(':').second;
608 
609  hoverOS << "**template arg** `" << name << "`\n***\nType: `";
610  value->getType()->print(hoverOS);
611  hoverOS << "`\n";
612  }
613  return hover;
614 }
615 
616 lsp::Hover TableGenTextFile::buildHoverForField(const llvm::Record *record,
617  const llvm::RecordVal *value,
618  const SMRange &hoverRange) {
619  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
620  {
621  llvm::raw_string_ostream hoverOS(hover.contents.value);
622  hoverOS << "**field** `" << value->getName() << "`\n***\nType: `";
623  value->getType()->print(hoverOS);
624  hoverOS << "`\n***\n";
625 
626  // Check for documentation in the source file.
627  if (std::optional<std::string> doc =
628  lsp::extractSourceDocComment(sourceMgr, value->getLoc())) {
629  hoverOS << "\n" << *doc << "\n";
630  hoverOS << "\n***\n";
631  }
632 
633  // Check to see if there is a base value that we can use for
634  // documentation.
635  auto [baseRecord, baseValue] = getBaseValue(record, value);
636  if (baseValue) {
637  if (std::optional<std::string> doc =
638  lsp::extractSourceDocComment(sourceMgr, baseValue->getLoc())) {
639  hoverOS << "\n *From `" << baseRecord->getName() << "`*:\n\n"
640  << *doc << "\n";
641  }
642  }
643  }
644  return hover;
645 }
646 
647 //===----------------------------------------------------------------------===//
648 // TableGenServer::Impl
649 //===----------------------------------------------------------------------===//
650 
652  explicit Impl(const Options &options)
653  : options(options), compilationDatabase(options.compilationDatabases) {}
654 
655  /// TableGen LSP options.
656  const Options &options;
657 
658  /// The compilation database containing additional information for files
659  /// passed to the server.
661 
662  /// The files held by the server, mapped by their URI file name.
663  llvm::StringMap<std::unique_ptr<TableGenTextFile>> files;
664 };
665 
666 //===----------------------------------------------------------------------===//
667 // TableGenServer
668 //===----------------------------------------------------------------------===//
669 
670 lsp::TableGenServer::TableGenServer(const Options &options)
671  : impl(std::make_unique<Impl>(options)) {}
673 
674 void lsp::TableGenServer::addDocument(const URIForFile &uri, StringRef contents,
675  int64_t version,
676  std::vector<Diagnostic> &diagnostics) {
677  // Build the set of additional include directories.
678  std::vector<std::string> additionalIncludeDirs = impl->options.extraDirs;
679  const auto &fileInfo = impl->compilationDatabase.getFileInfo(uri.file());
680  llvm::append_range(additionalIncludeDirs, fileInfo.includeDirs);
681 
682  impl->files[uri.file()] = std::make_unique<TableGenTextFile>(
683  uri, contents, version, additionalIncludeDirs, diagnostics);
684 }
685 
688  int64_t version, std::vector<Diagnostic> &diagnostics) {
689  // Check that we actually have a document for this uri.
690  auto it = impl->files.find(uri.file());
691  if (it == impl->files.end())
692  return;
693 
694  // Try to update the document. If we fail, erase the file from the server. A
695  // failed updated generally means we've fallen out of sync somewhere.
696  if (failed(it->second->update(uri, version, changes, diagnostics)))
697  impl->files.erase(it);
698 }
699 
700 std::optional<int64_t>
702  auto it = impl->files.find(uri.file());
703  if (it == impl->files.end())
704  return std::nullopt;
705 
706  int64_t version = it->second->getVersion();
707  impl->files.erase(it);
708  return version;
709 }
710 
712  const Position &defPos,
713  std::vector<Location> &locations) {
714  auto fileIt = impl->files.find(uri.file());
715  if (fileIt != impl->files.end())
716  fileIt->second->getLocationsOf(uri, defPos, locations);
717 }
718 
720  const Position &pos,
721  std::vector<Location> &references) {
722  auto fileIt = impl->files.find(uri.file());
723  if (fileIt != impl->files.end())
724  fileIt->second->findReferencesOf(uri, pos, references);
725 }
726 
728  const URIForFile &uri, std::vector<DocumentLink> &documentLinks) {
729  auto fileIt = impl->files.find(uri.file());
730  if (fileIt != impl->files.end())
731  return fileIt->second->getDocumentLinks(uri, documentLinks);
732 }
733 
734 std::optional<lsp::Hover>
736  const Position &hoverPos) {
737  auto fileIt = impl->files.find(uri.file());
738  if (fileIt != impl->files.end())
739  return fileIt->second->findHover(uri, hoverPos);
740  return std::nullopt;
741 }
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
static std::string diag(const llvm::Value &value)
static llvm::ManagedStatic< PassManagerOptions > options
static lsp::URIForFile getURIFromLoc(const llvm::SourceMgr &mgr, SMLoc loc, const lsp::URIForFile &mainFileURI)
Returns a language server uri for the given source location.
static std::pair< const llvm::Record *, const llvm::RecordVal * > getBaseValue(const llvm::Record *record, const llvm::RecordVal *value)
Get the base definition of the given record value, or nullptr if one couldn't be found.
static lsp::Location getLocationFromLoc(llvm::SourceMgr &mgr, SMRange loc, const lsp::URIForFile &uri)
Returns a language server location from the given source range.
static std::optional< lsp::Diagnostic > getLspDiagnoticFromDiag(const llvm::SMDiagnostic &diag, const 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...
static void error(const char *fmt, Ts &&...vals)
Definition: Logging.h:42
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.
URI in "file" scheme for a file.
Definition: Protocol.h:100
static llvm::Expected< URIForFile > fromFile(StringRef absoluteFilepath, StringRef scheme="file")
Try to build a URIForFile from the given absolute file path and optional scheme.
Definition: Protocol.cpp:233
StringRef file() const
Returns the absolute path to the file.
Definition: Protocol.h:113
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.
Include the generated interface declarations.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:56
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value.
Definition: LogicalResult.h:72
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 an efficient way to signal success or failure.
Definition: LogicalResult.h:26
std::string source
A human-readable string describing the source of this diagnostic, e.g.
Definition: Protocol.h:690
DiagnosticSeverity severity
The diagnostic's severity.
Definition: Protocol.h:686
Range range
The source range where the message applies.
Definition: Protocol.h:682
std::string message
The diagnostic's message.
Definition: Protocol.h:693
std::optional< std::string > category
The diagnostic's category.
Definition: Protocol.h:703
URIForFile uri
The text document's URI.
Definition: Protocol.h:394
SMLoc getAsSMLoc(llvm::SourceMgr &mgr) const
Convert this position into a source location in the main file of the given source manager.
Definition: Protocol.h:316
This class represents a single include within a root file.