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