brintos

brintos / llvm-project-archived public Read only

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