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