MLIR  16.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 
11 #include "../lsp-server-support/CompilationDatabase.h"
12 #include "../lsp-server-support/Logging.h"
13 #include "../lsp-server-support/Protocol.h"
14 #include "../lsp-server-support/SourceMgrUtils.h"
16 #include "llvm/ADT/IntervalMap.h"
17 #include "llvm/ADT/PointerUnion.h"
18 #include "llvm/ADT/StringMap.h"
19 #include "llvm/ADT/StringSet.h"
20 #include "llvm/ADT/TypeSwitch.h"
21 #include "llvm/Support/FileSystem.h"
22 #include "llvm/Support/Path.h"
23 #include "llvm/TableGen/Parser.h"
24 #include "llvm/TableGen/Record.h"
25 
26 using namespace mlir;
27 
28 /// Returns a language server uri for the given source location. `mainFileURI`
29 /// corresponds to the uri for the main file of the source manager.
30 static lsp::URIForFile getURIFromLoc(const llvm::SourceMgr &mgr, SMLoc loc,
31  const lsp::URIForFile &mainFileURI) {
32  int bufferId = mgr.FindBufferContainingLoc(loc);
33  if (bufferId == 0 || bufferId == static_cast<int>(mgr.getMainFileID()))
34  return mainFileURI;
36  mgr.getBufferInfo(bufferId).Buffer->getBufferIdentifier());
37  if (fileForLoc)
38  return *fileForLoc;
39  lsp::Logger::error("Failed to create URI for include file: {0}",
40  llvm::toString(fileForLoc.takeError()));
41  return mainFileURI;
42 }
43 
44 /// Returns a language server location from the given source range.
45 static lsp::Location getLocationFromLoc(llvm::SourceMgr &mgr, SMRange loc,
46  const lsp::URIForFile &uri) {
47  return lsp::Location(getURIFromLoc(mgr, loc.Start, uri),
48  lsp::Range(mgr, loc));
49 }
50 static lsp::Location getLocationFromLoc(llvm::SourceMgr &mgr, SMLoc loc,
51  const lsp::URIForFile &uri) {
52  return getLocationFromLoc(mgr, lsp::convertTokenLocToRange(loc), uri);
53 }
54 
55 /// Convert the given TableGen diagnostic to the LSP form.
56 static Optional<lsp::Diagnostic>
57 getLspDiagnoticFromDiag(const llvm::SMDiagnostic &diag,
58  const lsp::URIForFile &uri) {
59  auto *sourceMgr = const_cast<llvm::SourceMgr *>(diag.getSourceMgr());
60  if (!sourceMgr || !diag.getLoc().isValid())
61  return llvm::None;
62 
63  lsp::Diagnostic lspDiag;
64  lspDiag.source = "tablegen";
65  lspDiag.category = "Parse Error";
66 
67  // Try to grab a file location for this diagnostic.
68  lsp::Location loc = getLocationFromLoc(*sourceMgr, diag.getLoc(), uri);
69  lspDiag.range = loc.range;
70 
71  // Skip diagnostics that weren't emitted within the main file.
72  if (loc.uri != uri)
73  return llvm::None;
74 
75  // Convert the severity for the diagnostic.
76  switch (diag.getKind()) {
77  case llvm::SourceMgr::DK_Warning:
79  break;
80  case llvm::SourceMgr::DK_Error:
82  break;
83  case llvm::SourceMgr::DK_Note:
84  // Notes are emitted separately from the main diagnostic, so we just treat
85  // them as remarks given that we can't determine the diagnostic to relate
86  // them to.
87  case llvm::SourceMgr::DK_Remark:
89  break;
90  }
91  lspDiag.message = diag.getMessage().str();
92 
93  return lspDiag;
94 }
95 
96 //===----------------------------------------------------------------------===//
97 // TableGenIndex
98 //===----------------------------------------------------------------------===//
99 
100 namespace {
101 /// This class represents a single symbol definition within a TableGen index. It
102 /// contains the definition of the symbol, the location of the symbol, and any
103 /// recorded references.
104 struct TableGenIndexSymbol {
105  TableGenIndexSymbol(const llvm::Record *record)
106  : definition(record),
107  defLoc(lsp::convertTokenLocToRange(record->getLoc().front())) {}
108  TableGenIndexSymbol(const llvm::RecordVal *value)
109  : definition(value),
110  defLoc(lsp::convertTokenLocToRange(value->getLoc())) {}
111 
112  /// The main definition of the symbol.
114 
115  /// The source location of the definition.
116  SMRange defLoc;
117 
118  /// The source location of the references of the definition.
119  SmallVector<SMRange> references;
120 };
121 
122 /// This class provides an index for definitions/uses within a TableGen
123 /// document. It provides efficient lookup of a definition given an input source
124 /// range.
125 class TableGenIndex {
126 public:
127  TableGenIndex() : intervalMap(allocator) {}
128 
129  /// Initialize the index with the given RecordKeeper.
130  void initialize(const llvm::RecordKeeper &records);
131 
132  /// Lookup a symbol for the given location. Returns nullptr if no symbol could
133  /// be found. If provided, `overlappedRange` is set to the range that the
134  /// provided `loc` overlapped with.
135  const TableGenIndexSymbol *lookup(SMLoc loc,
136  SMRange *overlappedRange = nullptr) const;
137 
138 private:
139  /// The type of interval map used to store source references. SMRange is
140  /// half-open, so we also need to use a half-open interval map.
141  using MapT = llvm::IntervalMap<
142  const char *, const TableGenIndexSymbol *,
143  llvm::IntervalMapImpl::NodeSizer<const char *,
144  const TableGenIndexSymbol *>::LeafSize,
145  llvm::IntervalMapHalfOpenInfo<const char *>>;
146 
147  /// An allocator for the interval map.
148  MapT::Allocator allocator;
149 
150  /// An interval map containing a corresponding definition mapped to a source
151  /// interval.
152  MapT intervalMap;
153 
154  /// A mapping between definitions and their corresponding symbol.
156 };
157 } // namespace
158 
159 void TableGenIndex::initialize(const llvm::RecordKeeper &records) {
160  auto getOrInsertDef = [&](const auto *def) -> TableGenIndexSymbol * {
161  auto it = defToSymbol.try_emplace(def, nullptr);
162  if (it.second)
163  it.first->second = std::make_unique<TableGenIndexSymbol>(def);
164  return &*it.first->second;
165  };
166  auto insertRef = [&](TableGenIndexSymbol *sym, SMRange refLoc,
167  bool isDef = false) {
168  const char *startLoc = refLoc.Start.getPointer();
169  const char *endLoc = refLoc.End.getPointer();
170 
171  // If the location we got was empty, try to lex a token from the start
172  // location.
173  if (startLoc == endLoc) {
174  refLoc = lsp::convertTokenLocToRange(SMLoc::getFromPointer(startLoc));
175  startLoc = refLoc.Start.getPointer();
176  endLoc = refLoc.End.getPointer();
177 
178  // If the location is still empty, bail on trying to use this reference
179  // location.
180  if (startLoc == endLoc)
181  return;
182  }
183 
184  // Check to see if a symbol is already attached to this location.
185  // IntervalMap doesn't allow overlapping inserts, and we don't really
186  // want multiple symbols attached to a source location anyways. This
187  // shouldn't really happen in practice, but we should handle it gracefully.
188  if (!intervalMap.overlaps(startLoc, endLoc))
189  intervalMap.insert(startLoc, endLoc, sym);
190 
191  if (!isDef)
192  sym->references.push_back(refLoc);
193  };
194  auto classes =
195  llvm::make_pointee_range(llvm::make_second_range(records.getClasses()));
196  auto defs =
197  llvm::make_pointee_range(llvm::make_second_range(records.getDefs()));
198  for (const llvm::Record &def : llvm::concat<llvm::Record>(classes, defs)) {
199  auto *sym = getOrInsertDef(&def);
200  insertRef(sym, sym->defLoc, /*isDef=*/true);
201 
202  // Add references to the definition.
203  for (SMLoc loc : def.getLoc().drop_front())
204  insertRef(sym, lsp::convertTokenLocToRange(loc));
205 
206  // Add references to any super classes.
207  for (auto &it : def.getSuperClasses())
208  insertRef(getOrInsertDef(it.first),
209  lsp::convertTokenLocToRange(it.second.Start));
210 
211  // Add definitions for any values.
212  for (const llvm::RecordVal &value : def.getValues()) {
213  auto *sym = getOrInsertDef(&value);
214  insertRef(sym, sym->defLoc, /*isDef=*/true);
215  }
216  }
217 }
218 
219 const TableGenIndexSymbol *
220 TableGenIndex::lookup(SMLoc loc, SMRange *overlappedRange) const {
221  auto it = intervalMap.find(loc.getPointer());
222  if (!it.valid() || loc.getPointer() < it.start())
223  return nullptr;
224 
225  if (overlappedRange) {
226  *overlappedRange = SMRange(SMLoc::getFromPointer(it.start()),
227  SMLoc::getFromPointer(it.stop()));
228  }
229  return it.value();
230 }
231 
232 //===----------------------------------------------------------------------===//
233 // TableGenTextFile
234 //===----------------------------------------------------------------------===//
235 
236 namespace {
237 /// This class represents a text file containing one or more TableGen documents.
238 class TableGenTextFile {
239 public:
240  TableGenTextFile(const lsp::URIForFile &uri, StringRef fileContents,
241  int64_t version,
242  const std::vector<std::string> &extraIncludeDirs,
243  std::vector<lsp::Diagnostic> &diagnostics);
244 
245  /// Return the current version of this text file.
246  int64_t getVersion() const { return version; }
247 
248  /// Update the file to the new version using the provided set of content
249  /// changes. Returns failure if the update was unsuccessful.
250  LogicalResult update(const lsp::URIForFile &uri, int64_t newVersion,
252  std::vector<lsp::Diagnostic> &diagnostics);
253 
254  //===--------------------------------------------------------------------===//
255  // Definitions and References
256  //===--------------------------------------------------------------------===//
257 
258  void getLocationsOf(const lsp::URIForFile &uri, const lsp::Position &defPos,
259  std::vector<lsp::Location> &locations);
260  void findReferencesOf(const lsp::URIForFile &uri, const lsp::Position &pos,
261  std::vector<lsp::Location> &references);
262 
263  //===--------------------------------------------------------------------===//
264  // Document Links
265  //===--------------------------------------------------------------------===//
266 
267  void getDocumentLinks(const lsp::URIForFile &uri,
268  std::vector<lsp::DocumentLink> &links);
269 
270  //===--------------------------------------------------------------------===//
271  // Hover
272  //===--------------------------------------------------------------------===//
273 
274  Optional<lsp::Hover> findHover(const lsp::URIForFile &uri,
275  const lsp::Position &hoverPos);
276 
277 private:
278  /// Initialize the text file from the given file contents.
279  void initialize(const lsp::URIForFile &uri, int64_t newVersion,
280  std::vector<lsp::Diagnostic> &diagnostics);
281 
282  /// The full string contents of the file.
283  std::string contents;
284 
285  /// The version of this file.
286  int64_t version;
287 
288  /// The include directories for this file.
289  std::vector<std::string> includeDirs;
290 
291  /// The source manager containing the contents of the input file.
292  llvm::SourceMgr sourceMgr;
293 
294  /// The record keeper containing the parsed tablegen constructs.
295  std::unique_ptr<llvm::RecordKeeper> recordKeeper;
296 
297  /// The index of the parsed file.
298  TableGenIndex index;
299 
300  /// The set of includes of the parsed file.
301  SmallVector<lsp::SourceMgrInclude> parsedIncludes;
302 };
303 } // namespace
304 
305 TableGenTextFile::TableGenTextFile(
306  const lsp::URIForFile &uri, StringRef fileContents, int64_t version,
307  const std::vector<std::string> &extraIncludeDirs,
308  std::vector<lsp::Diagnostic> &diagnostics)
309  : contents(fileContents.str()), version(version) {
310  // Build the set of include directories for this file.
311  llvm::SmallString<32> uriDirectory(uri.file());
312  llvm::sys::path::remove_filename(uriDirectory);
313  includeDirs.push_back(uriDirectory.str().str());
314  includeDirs.insert(includeDirs.end(), extraIncludeDirs.begin(),
315  extraIncludeDirs.end());
316 
317  // Initialize the file.
318  initialize(uri, version, diagnostics);
319 }
320 
322 TableGenTextFile::update(const lsp::URIForFile &uri, int64_t newVersion,
324  std::vector<lsp::Diagnostic> &diagnostics) {
325  if (failed(lsp::TextDocumentContentChangeEvent::applyTo(changes, contents))) {
326  lsp::Logger::error("Failed to update contents of {0}", uri.file());
327  return failure();
328  }
329 
330  // If the file contents were properly changed, reinitialize the text file.
331  initialize(uri, newVersion, diagnostics);
332  return success();
333 }
334 
335 void TableGenTextFile::initialize(const lsp::URIForFile &uri,
336  int64_t newVersion,
337  std::vector<lsp::Diagnostic> &diagnostics) {
338  version = newVersion;
339  sourceMgr = llvm::SourceMgr();
340  recordKeeper = std::make_unique<llvm::RecordKeeper>();
341 
342  // Build a buffer for this file.
343  auto memBuffer = llvm::MemoryBuffer::getMemBuffer(contents, uri.file());
344  if (!memBuffer) {
345  lsp::Logger::error("Failed to create memory buffer for file", uri.file());
346  return;
347  }
348  sourceMgr.setIncludeDirs(includeDirs);
349  sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
350 
351  // This class provides a context argument for the llvm::SourceMgr diagnostic
352  // handler.
353  struct DiagHandlerContext {
354  std::vector<lsp::Diagnostic> &diagnostics;
355  const lsp::URIForFile &uri;
356  } handlerContext{diagnostics, uri};
357 
358  // Set the diagnostic handler for the tablegen source manager.
359  sourceMgr.setDiagHandler(
360  [](const llvm::SMDiagnostic &diag, void *rawHandlerContext) {
361  auto *ctx = reinterpret_cast<DiagHandlerContext *>(rawHandlerContext);
362  if (auto lspDiag = getLspDiagnoticFromDiag(diag, ctx->uri))
363  ctx->diagnostics.push_back(*lspDiag);
364  },
365  &handlerContext);
366  bool failedToParse = llvm::TableGenParseFile(sourceMgr, *recordKeeper);
367 
368  // Process all of the include files.
369  lsp::gatherIncludeFiles(sourceMgr, parsedIncludes);
370  if (failedToParse)
371  return;
372 
373  // If we successfully parsed the file, we can now build the index.
374  index.initialize(*recordKeeper);
375 }
376 
377 //===----------------------------------------------------------------------===//
378 // TableGenTextFile: Definitions and References
379 //===----------------------------------------------------------------------===//
380 
381 void TableGenTextFile::getLocationsOf(const lsp::URIForFile &uri,
382  const lsp::Position &defPos,
383  std::vector<lsp::Location> &locations) {
384  SMLoc posLoc = defPos.getAsSMLoc(sourceMgr);
385  const TableGenIndexSymbol *symbol = index.lookup(posLoc);
386  if (!symbol)
387  return;
388 
389  locations.push_back(getLocationFromLoc(sourceMgr, symbol->defLoc, uri));
390 }
391 
392 void TableGenTextFile::findReferencesOf(
393  const lsp::URIForFile &uri, const lsp::Position &pos,
394  std::vector<lsp::Location> &references) {
395  SMLoc posLoc = pos.getAsSMLoc(sourceMgr);
396  const TableGenIndexSymbol *symbol = index.lookup(posLoc);
397  if (!symbol)
398  return;
399 
400  references.push_back(getLocationFromLoc(sourceMgr, symbol->defLoc, uri));
401  for (SMRange refLoc : symbol->references)
402  references.push_back(getLocationFromLoc(sourceMgr, refLoc, uri));
403 }
404 
405 //===--------------------------------------------------------------------===//
406 // TableGenTextFile: Document Links
407 //===--------------------------------------------------------------------===//
408 
409 void TableGenTextFile::getDocumentLinks(const lsp::URIForFile &uri,
410  std::vector<lsp::DocumentLink> &links) {
411  for (const lsp::SourceMgrInclude &include : parsedIncludes)
412  links.emplace_back(include.range, include.uri);
413 }
414 
415 //===----------------------------------------------------------------------===//
416 // TableGenTextFile: Hover
417 //===----------------------------------------------------------------------===//
418 
419 Optional<lsp::Hover>
420 TableGenTextFile::findHover(const lsp::URIForFile &uri,
421  const lsp::Position &hoverPos) {
422  // Check for a reference to an include.
423  for (const lsp::SourceMgrInclude &include : parsedIncludes)
424  if (include.range.contains(hoverPos))
425  return include.buildHover();
426  return llvm::None;
427 }
428 
429 //===----------------------------------------------------------------------===//
430 // TableGenServer::Impl
431 //===----------------------------------------------------------------------===//
432 
434  explicit Impl(const Options &options)
435  : options(options), compilationDatabase(options.compilationDatabases) {}
436 
437  /// TableGen LSP options.
438  const Options &options;
439 
440  /// The compilation database containing additional information for files
441  /// passed to the server.
443 
444  /// The files held by the server, mapped by their URI file name.
445  llvm::StringMap<std::unique_ptr<TableGenTextFile>> files;
446 };
447 
448 //===----------------------------------------------------------------------===//
449 // TableGenServer
450 //===----------------------------------------------------------------------===//
451 
453  : impl(std::make_unique<Impl>(options)) {}
455 
456 void lsp::TableGenServer::addDocument(const URIForFile &uri, StringRef contents,
457  int64_t version,
458  std::vector<Diagnostic> &diagnostics) {
459  // Build the set of additional include directories.
460  std::vector<std::string> additionalIncludeDirs = impl->options.extraDirs;
461  const auto &fileInfo = impl->compilationDatabase.getFileInfo(uri.file());
462  llvm::append_range(additionalIncludeDirs, fileInfo.includeDirs);
463 
464  impl->files[uri.file()] = std::make_unique<TableGenTextFile>(
465  uri, contents, version, additionalIncludeDirs, diagnostics);
466 }
467 
470  int64_t version, std::vector<Diagnostic> &diagnostics) {
471  // Check that we actually have a document for this uri.
472  auto it = impl->files.find(uri.file());
473  if (it == impl->files.end())
474  return;
475 
476  // Try to update the document. If we fail, erase the file from the server. A
477  // failed updated generally means we've fallen out of sync somewhere.
478  if (failed(it->second->update(uri, version, changes, diagnostics)))
479  impl->files.erase(it);
480 }
481 
483  auto it = impl->files.find(uri.file());
484  if (it == impl->files.end())
485  return llvm::None;
486 
487  int64_t version = it->second->getVersion();
488  impl->files.erase(it);
489  return version;
490 }
491 
493  const Position &defPos,
494  std::vector<Location> &locations) {
495  auto fileIt = impl->files.find(uri.file());
496  if (fileIt != impl->files.end())
497  fileIt->second->getLocationsOf(uri, defPos, locations);
498 }
499 
501  const Position &pos,
502  std::vector<Location> &references) {
503  auto fileIt = impl->files.find(uri.file());
504  if (fileIt != impl->files.end())
505  fileIt->second->findReferencesOf(uri, pos, references);
506 }
507 
509  const URIForFile &uri, std::vector<DocumentLink> &documentLinks) {
510  auto fileIt = impl->files.find(uri.file());
511  if (fileIt != impl->files.end())
512  return fileIt->second->getDocumentLinks(uri, documentLinks);
513 }
514 
516  const Position &hoverPos) {
517  auto fileIt = impl->files.find(uri.file());
518  if (fileIt != impl->files.end())
519  return fileIt->second->findHover(uri, hoverPos);
520  return llvm::None;
521 }
StringRef file() const
Returns the absolute path to the file.
Definition: Protocol.h:110
Include the generated interface declarations.
Impl(const Options &options)
Documents should not be synced at all.
static std::string diag(llvm::Value &v)
lsp::CompilationDatabase compilationDatabase
The compilation database containing additional information for files passed to the server...
static void error(const char *fmt, Ts &&...vals)
Definition: Logging.h:42
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value...
Definition: LogicalResult.h:72
DiagnosticSeverity severity
The diagnostic&#39;s severity.
Definition: Protocol.h:656
Optional< std::string > category
The diagnostic&#39;s category.
Definition: Protocol.h:673
URI in "file" scheme for a file.
Definition: Protocol.h:99
static constexpr const bool value
static Optional< lsp::Diagnostic > getLspDiagnoticFromDiag(const llvm::SMDiagnostic &diag, const lsp::URIForFile &uri)
Convert the given TableGen diagnostic to the LSP form.
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.
This class represents a single include within a root file.
This class contains a collection of compilation information for files provided to the language server...
LogicalResult applyTo(std::string &contents) const
Try to apply this change to the given contents string.
Definition: Protocol.cpp:479
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:56
std::string source
A human-readable string describing the source of this diagnostic, e.g.
Definition: Protocol.h:660
void findReferencesOf(const URIForFile &uri, const Position &pos, std::vector< Location > &references)
Find all references of the object pointed at by the given position.
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
void getDocumentLinks(const URIForFile &uri, std::vector< DocumentLink > &documentLinks)
Return the document links referenced by the given file.
const Options & options
TableGen LSP options.
Optional< int64_t > removeDocument(const URIForFile &uri)
Remove the document with the given uri.
TableGenServer(const Options &options)
SMRange convertTokenLocToRange(SMLoc loc)
Returns the range of a lexical token given a SMLoc corresponding to the start of an token location...
llvm::StringMap< std::unique_ptr< TableGenTextFile > > files
The files held by the server, mapped by their URI file name.
std::string message
The diagnostic&#39;s message.
Definition: Protocol.h:663
static llvm::ManagedStatic< PassManagerOptions > options
URIForFile uri
The text document&#39;s URI.
Definition: Protocol.h:364
void gatherIncludeFiles(llvm::SourceMgr &sourceMgr, SmallVectorImpl< SourceMgrInclude > &includes)
Given a source manager, gather all of the processed include files.
Range range
The source range where the message applies.
Definition: Protocol.h:652
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.
StringRef toString(IteratorType t)
static lsp::URIForFile getURIFromLoc(const llvm::SourceMgr &mgr, SMLoc loc, const lsp::URIForFile &mainFileURI)
Returns a language server uri for the given source location.
static llvm::Expected< URIForFile > fromFile(StringRef absoluteFilepath)
Try to build a URIForFile from the given absolute file path.
Definition: Protocol.cpp:222
Optional< Hover > findHover(const URIForFile &uri, const Position &hoverPos)
Find a hover description for the given hover position, or None if one couldn&#39;t be found...
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:286
void getLocationsOf(const URIForFile &uri, const Position &defPos, std::vector< Location > &locations)
Return the locations of the object pointed at by the given position.
static lsp::Location getLocationFromLoc(llvm::SourceMgr &mgr, SMRange loc, const lsp::URIForFile &uri)
Returns a language server location from the given source range.