MLIR 24.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/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 the
32/// start of an token location. The range is computed heuristically, and
33/// 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::URIForFile
41getURIFromLoc(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;
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::Location
58getLocationFromLoc(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::Location
64getLocationFromLoc(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 treat
99 // them as remarks given that we can't determine the diagnostic to relate
100 // 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 one
111/// 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 value
136 // wasn't overridden).
137 if (!baseValue.second || baseValue.second->getLoc() == value->getLoc())
138 return {nullptr, nullptr};
139 return baseValue;
140}
141
142//===----------------------------------------------------------------------===//
143// TableGenIndex
144//===----------------------------------------------------------------------===//
145
146namespace {
147/// This class represents a single symbol definition within a TableGen index. It
148/// contains the definition of the symbol, the location of the symbol, and any
149/// 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 TableGen
199/// document. It provides efficient lookup of a definition given an input source
200/// 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 could
209 /// be found. If provided, `overlappedRange` is set to the range that the
210 /// 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 is
216 /// 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 source
245 /// interval.
246 MapT intervalMap;
247
248 /// A mapping between definitions and their corresponding symbol.
250};
251} // namespace
252
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 start
263 // 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 reference
270 // 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 really
277 // want multiple symbols attached to a source location anyways. This
278 // 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 = llvm::make_pointee_range(records.getClasses());
286 auto defs = llvm::make_pointee_range(records.getDefs());
287 for (const Record &def : llvm::concat<const Record>(classes, defs)) {
288 auto *sym = getOrInsertDef(&def);
289 insertRef(sym, sym->defLoc, /*isDef=*/true);
290
291 // Add references to the definition.
292 for (SMLoc loc : def.getLoc().drop_front())
293 insertRef(sym, convertTokenLocToRange(loc));
294 for (SMRange loc : def.getReferenceLocs())
295 insertRef(sym, loc);
296
297 // Add definitions for any template arguments and fields.
298 for (const RecordVal &value : llvm::concat<const RecordVal>(
299 def.getTemplateArgs(), def.getValues())) {
300 auto *sym = getOrInsertDef(&def, &value);
301 insertRef(sym, sym->defLoc, /*isDef=*/true);
302 for (SMRange refLoc : def.getValueReferenceLocs(value.getNameInit()))
303 insertRef(sym, refLoc);
304 }
305 }
306}
307
308const TableGenIndexSymbol *
309TableGenIndex::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
325namespace {
326/// This class represents a text file containing one or more TableGen documents.
327class TableGenTextFile {
328public:
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,
341 ArrayRef<llvm::lsp::TextDocumentContentChangeEvent> changes,
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
378private:
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
406TableGenTextFile::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
421LogicalResult 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
436void 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.setVirtualFileSystem(llvm::vfs::getRealFileSystem());
452 sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
453
454 // This class provides a context argument for the SourceMgr diagnostic
455 // handler.
456 struct DiagHandlerContext {
457 std::vector<llvm::lsp::Diagnostic> &diagnostics;
458 const llvm::lsp::URIForFile &uri;
459 } handlerContext{diagnostics, uri};
460
461 // Set the diagnostic handler for the tablegen source manager.
462 sourceMgr.setDiagHandler(
463 [](const llvm::SMDiagnostic &diag, void *rawHandlerContext) {
464 auto *ctx = reinterpret_cast<DiagHandlerContext *>(rawHandlerContext);
465 if (auto lspDiag = getLspDiagnoticFromDiag(diag, ctx->uri))
466 ctx->diagnostics.push_back(*lspDiag);
467 },
468 &handlerContext);
469 bool failedToParse = llvm::TableGenParseFile(sourceMgr, *recordKeeper);
470
471 // Process all of the include files.
472 lsp::gatherIncludeFiles(sourceMgr, parsedIncludes);
473 if (failedToParse)
474 return;
475
476 // If we successfully parsed the file, we can now build the index.
477 index.initialize(*recordKeeper);
478}
479
480//===----------------------------------------------------------------------===//
481// TableGenTextFile: Definitions and References
482//===----------------------------------------------------------------------===//
483
484void TableGenTextFile::getLocationsOf(
485 const llvm::lsp::URIForFile &uri, const llvm::lsp::Position &defPos,
486 std::vector<llvm::lsp::Location> &locations) {
487 SMLoc posLoc = defPos.getAsSMLoc(sourceMgr);
488 const TableGenIndexSymbol *symbol = index.lookup(posLoc);
489 if (!symbol)
490 return;
491
492 // If this symbol is a record value and the def position is already the def of
493 // the symbol, check to see if the value has a base definition. This allows
494 // for a "go-to-def" on a "let" to resolve the definition in the base class.
495 auto *valSym = dyn_cast<TableGenRecordValSymbol>(symbol);
496 if (valSym && lsp::contains(valSym->defLoc, posLoc)) {
497 if (auto *val = getBaseValue(valSym->record, valSym->getValue()).second) {
498 locations.push_back(getLocationFromLoc(sourceMgr, val->getLoc(), uri));
499 return;
500 }
501 }
502
503 locations.push_back(getLocationFromLoc(sourceMgr, symbol->defLoc, uri));
504}
505
506void TableGenTextFile::findReferencesOf(
507 const llvm::lsp::URIForFile &uri, const llvm::lsp::Position &pos,
508 std::vector<llvm::lsp::Location> &references) {
509 SMLoc posLoc = pos.getAsSMLoc(sourceMgr);
510 const TableGenIndexSymbol *symbol = index.lookup(posLoc);
511 if (!symbol)
512 return;
513
514 references.push_back(getLocationFromLoc(sourceMgr, symbol->defLoc, uri));
515 for (SMRange refLoc : symbol->references)
516 references.push_back(getLocationFromLoc(sourceMgr, refLoc, uri));
517}
518
519//===--------------------------------------------------------------------===//
520// TableGenTextFile: Document Links
521//===--------------------------------------------------------------------===//
522
523void TableGenTextFile::getDocumentLinks(
524 const llvm::lsp::URIForFile &uri,
525 std::vector<llvm::lsp::DocumentLink> &links) {
526 for (const lsp::SourceMgrInclude &include : parsedIncludes)
527 links.emplace_back(include.range, include.uri);
528}
529
530//===----------------------------------------------------------------------===//
531// TableGenTextFile: Hover
532//===----------------------------------------------------------------------===//
533
534std::optional<llvm::lsp::Hover>
535TableGenTextFile::findHover(const llvm::lsp::URIForFile &uri,
536 const llvm::lsp::Position &hoverPos) {
537 // Check for a reference to an include.
538 for (const lsp::SourceMgrInclude &include : parsedIncludes)
539 if (include.range.contains(hoverPos))
540 return include.buildHover();
541
542 // Find the symbol at the given location.
543 SMRange hoverRange;
544 SMLoc posLoc = hoverPos.getAsSMLoc(sourceMgr);
545 const TableGenIndexSymbol *symbol = index.lookup(posLoc, &hoverRange);
546 if (!symbol)
547 return std::nullopt;
548
549 // Build hover for a Record.
550 if (auto *record = dyn_cast<TableGenRecordSymbol>(symbol))
551 return buildHoverForRecord(record->getValue(), hoverRange);
552
553 // Build hover for a RecordVal, which is either a template argument or a
554 // field.
555 auto *recordVal = cast<TableGenRecordValSymbol>(symbol);
556 const RecordVal *value = recordVal->getValue();
557 if (value->isTemplateArg())
558 return buildHoverForTemplateArg(recordVal->record, value, hoverRange);
559 return buildHoverForField(recordVal->record, value, hoverRange);
560}
561
562llvm::lsp::Hover
563TableGenTextFile::buildHoverForRecord(const Record *record,
564 const SMRange &hoverRange) {
565 llvm::lsp::Hover hover(llvm::lsp::Range(sourceMgr, hoverRange));
566 {
567 llvm::raw_string_ostream hoverOS(hover.contents.value);
568
569 // Format the type of record this is.
570 if (record->isClass()) {
571 hoverOS << "**class** `" << record->getName() << "`";
572 } else if (record->isAnonymous()) {
573 hoverOS << "**anonymous class**";
574 } else {
575 hoverOS << "**def** `" << record->getName() << "`";
576 }
577 hoverOS << "\n***\n";
578
579 // Check if this record has summary/description fields. These are often used
580 // to hold documentation for the record.
581 auto printAndFormatField = [&](StringRef fieldName) {
582 // Check that the record actually has the given field, and that it's a
583 // string.
584 const RecordVal *value = record->getValue(fieldName);
585 if (!value)
586 return;
587 auto *stringValue = dyn_cast<llvm::StringInit>(value->getValue());
588 if (!stringValue)
589 return;
590
591 raw_indented_ostream ros(hoverOS);
592 ros.printReindented(stringValue->getValue().rtrim(" \t"));
593 hoverOS << "\n***\n";
594 };
595 printAndFormatField("summary");
596 printAndFormatField("description");
597
598 // Check for documentation in the source file.
599 if (std::optional<std::string> doc =
600 lsp::extractSourceDocComment(sourceMgr, record->getLoc().front())) {
601 hoverOS << "\n" << *doc << "\n";
602 }
603 }
604 return hover;
605}
606
607llvm::lsp::Hover TableGenTextFile::buildHoverForTemplateArg(
608 const Record *record, const RecordVal *value, const SMRange &hoverRange) {
609 llvm::lsp::Hover hover(llvm::lsp::Range(sourceMgr, hoverRange));
610 {
611 llvm::raw_string_ostream hoverOS(hover.contents.value);
612 StringRef name = value->getName().rsplit(':').second;
613
614 hoverOS << "**template arg** `" << name << "`\n***\nType: `";
615 value->getType()->print(hoverOS);
616 hoverOS << "`\n";
617 }
618 return hover;
619}
620
621llvm::lsp::Hover TableGenTextFile::buildHoverForField(
622 const Record *record, const RecordVal *value, const SMRange &hoverRange) {
623 llvm::lsp::Hover hover(llvm::lsp::Range(sourceMgr, hoverRange));
624 {
625 llvm::raw_string_ostream hoverOS(hover.contents.value);
626 hoverOS << "**field** `" << value->getName() << "`\n***\nType: `";
627 value->getType()->print(hoverOS);
628 hoverOS << "`\n***\n";
629
630 // Check for documentation in the source file.
631 if (std::optional<std::string> doc =
632 lsp::extractSourceDocComment(sourceMgr, value->getLoc())) {
633 hoverOS << "\n" << *doc << "\n";
634 hoverOS << "\n***\n";
635 }
636
637 // Check to see if there is a base value that we can use for
638 // documentation.
639 auto [baseRecord, baseValue] = getBaseValue(record, value);
640 if (baseValue) {
641 if (std::optional<std::string> doc =
642 lsp::extractSourceDocComment(sourceMgr, baseValue->getLoc())) {
643 hoverOS << "\n *From `" << baseRecord->getName() << "`*:\n\n"
644 << *doc << "\n";
645 }
646 }
647 }
648 return hover;
649}
650
651//===----------------------------------------------------------------------===//
652// TableGenServer::Impl
653//===----------------------------------------------------------------------===//
654
656 explicit Impl(const Options &options)
657 : options(options), compilationDatabase(options.compilationDatabases) {}
658
659 /// TableGen LSP options.
661
662 /// The compilation database containing additional information for files
663 /// passed to the server.
665
666 /// The files held by the server, mapped by their URI file name.
667 llvm::StringMap<std::unique_ptr<TableGenTextFile>> files;
668};
669
670//===----------------------------------------------------------------------===//
671// TableGenServer
672//===----------------------------------------------------------------------===//
673
677
678void lsp::TableGenServer::addDocument(const URIForFile &uri, StringRef contents,
679 int64_t version,
680 std::vector<Diagnostic> &diagnostics) {
681 // Build the set of additional include directories.
682 std::vector<std::string> additionalIncludeDirs = impl->options.extraDirs;
683 const auto &fileInfo = impl->compilationDatabase.getFileInfo(uri.file());
684 llvm::append_range(additionalIncludeDirs, fileInfo.includeDirs);
685
686 impl->files[uri.file()] = std::make_unique<TableGenTextFile>(
687 uri, contents, version, additionalIncludeDirs, diagnostics);
688}
689
691 const URIForFile &uri, ArrayRef<TextDocumentContentChangeEvent> changes,
692 int64_t version, std::vector<Diagnostic> &diagnostics) {
693 // Check that we actually have a document for this uri.
694 auto it = impl->files.find(uri.file());
695 if (it == impl->files.end())
696 return;
697
698 // Try to update the document. If we fail, erase the file from the server. A
699 // failed updated generally means we've fallen out of sync somewhere.
700 if (failed(it->second->update(uri, version, changes, diagnostics)))
701 impl->files.erase(it);
702}
703
704std::optional<int64_t>
706 auto it = impl->files.find(uri.file());
707 if (it == impl->files.end())
708 return std::nullopt;
709
710 int64_t version = it->second->getVersion();
711 impl->files.erase(it);
712 return version;
713}
714
715void lsp::TableGenServer::getLocationsOf(const URIForFile &uri,
716 const Position &defPos,
717 std::vector<Location> &locations) {
718 auto fileIt = impl->files.find(uri.file());
719 if (fileIt != impl->files.end())
720 fileIt->second->getLocationsOf(uri, defPos, locations);
721}
722
723void lsp::TableGenServer::findReferencesOf(const URIForFile &uri,
724 const Position &pos,
725 std::vector<Location> &references) {
726 auto fileIt = impl->files.find(uri.file());
727 if (fileIt != impl->files.end())
728 fileIt->second->findReferencesOf(uri, pos, references);
729}
730
732 const URIForFile &uri, std::vector<DocumentLink> &documentLinks) {
733 auto fileIt = impl->files.find(uri.file());
734 if (fileIt != impl->files.end())
735 return fileIt->second->getDocumentLinks(uri, documentLinks);
736}
737
738std::optional<llvm::lsp::Hover>
739lsp::TableGenServer::findHover(const URIForFile &uri,
740 const Position &hoverPos) {
741 auto fileIt = impl->files.find(uri.file());
742 if (fileIt != impl->files.end())
743 return fileIt->second->findHover(uri, hoverPos);
744 return std::nullopt;
745}
return success()
LogicalResult initialize(unsigned origNumLoops, ArrayRef< ReassociationIndices > foldedIterationDims)
static std::optional< lsp::Location > getLocationFromLoc(StringRef uriScheme, FileLineColLoc loc, StringRef workspaceRoot)
Returns a language server location from the given MLIR file location.
static lsp::Diagnostic getLspDiagnoticFromDiag(llvm::SourceMgr &sourceMgr, Diagnostic &diag, const lsp::URIForFile &uri, StringRef workspaceRoot)
Convert the given MLIR 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.
static llvm::ManagedStatic< PassManagerOptions > options
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::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 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.
TableGenServer(const Options &options)
void findReferencesOf(const URIForFile &uri, const Position &pos, std::vector< Location > &references)
Find all references of the object pointed at by the given position.
void gatherIncludeFiles(llvm::SourceMgr &sourceMgr, SmallVectorImpl< SourceMgrInclude > &includes)
Given a source manager, gather all of the processed include files.
bool contains(SMRange range, SMLoc loc)
Returns true if the given range contains the given source location.
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:853
Include the generated interface declarations.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:123
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.