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