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"
27using llvm::RecordKeeper;
40static llvm::lsp::URIForFile
42 const llvm::lsp::URIForFile &mainFileURI) {
43 int bufferId = mgr.FindBufferContainingLoc(loc);
44 if (bufferId == 0 || bufferId ==
static_cast<int>(mgr.getMainFileID()))
47 llvm::lsp::URIForFile::fromFile(
48 mgr.getBufferInfo(bufferId).Buffer->getBufferIdentifier());
51 llvm::lsp::Logger::error(
"Failed to create URI for include file: {0}",
52 llvm::toString(fileForLoc.takeError()));
57static llvm::lsp::Location
59 const llvm::lsp::URIForFile &uri) {
60 return llvm::lsp::Location(
getURIFromLoc(mgr, loc.Start, uri),
61 llvm::lsp::Range(mgr, loc));
63static llvm::lsp::Location
65 const llvm::lsp::URIForFile &uri) {
70static std::optional<llvm::lsp::Diagnostic>
72 const llvm::lsp::URIForFile &uri) {
73 auto *sourceMgr =
const_cast<SourceMgr *
>(
diag.getSourceMgr());
74 if (!sourceMgr || !
diag.getLoc().isValid())
77 llvm::lsp::Diagnostic lspDiag;
78 lspDiag.source =
"tablegen";
79 lspDiag.category =
"Parse Error";
83 lspDiag.range = loc.range;
90 switch (
diag.getKind()) {
91 case SourceMgr::DK_Warning:
92 lspDiag.severity = llvm::lsp::DiagnosticSeverity::Warning;
94 case SourceMgr::DK_Error:
95 lspDiag.severity = llvm::lsp::DiagnosticSeverity::Error;
97 case SourceMgr::DK_Note:
101 case SourceMgr::DK_Remark:
102 lspDiag.severity = llvm::lsp::DiagnosticSeverity::Information;
105 lspDiag.message =
diag.getMessage().str();
112static std::pair<const Record *, const RecordVal *>
114 if (value->isTemplateArg())
115 return {
nullptr,
nullptr};
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;
131 std::pair<const Record *, const RecordVal *> baseValue = {};
132 while (
const RecordVal *newBase = findValueInSupers(record))
133 baseValue = {record, newBase};
137 if (!baseValue.second || baseValue.second->getLoc() == value->getLoc())
138 return {
nullptr,
nullptr};
150struct TableGenIndexSymbol {
151 TableGenIndexSymbol(
const Record *record)
152 : definition(record),
154 TableGenIndexSymbol(
const RecordVal *value)
156 virtual ~TableGenIndexSymbol() =
default;
159 PointerUnion<const Record *, const RecordVal *> definition;
165 SmallVector<SMRange> references;
168struct TableGenRecordSymbol :
public TableGenIndexSymbol {
169 TableGenRecordSymbol(
const Record *record) : TableGenIndexSymbol(record) {}
170 ~TableGenRecordSymbol()
override =
default;
172 static bool classof(
const TableGenIndexSymbol *symbol) {
173 return isa<const Record *>(symbol->definition);
177 const Record *getValue()
const {
return cast<const Record *>(definition); }
180struct TableGenRecordValSymbol :
public TableGenIndexSymbol {
181 TableGenRecordValSymbol(
const Record *record,
const RecordVal *value)
182 : TableGenIndexSymbol(value), record(record) {}
183 ~TableGenRecordValSymbol()
override =
default;
185 static bool classof(
const TableGenIndexSymbol *symbol) {
186 return isa<const RecordVal *>(symbol->definition);
190 const RecordVal *getValue()
const {
191 return cast<const RecordVal *>(definition);
195 const Record *record;
203 TableGenIndex() : intervalMap(allocator) {}
211 const TableGenIndexSymbol *lookup(SMLoc loc,
212 SMRange *overlappedRange =
nullptr)
const;
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 *>>;
224 TableGenIndexSymbol *getOrInsertDef(
const Record *record) {
225 auto it = defToSymbol.try_emplace(record,
nullptr);
227 it.first->second = std::make_unique<TableGenRecordSymbol>(record);
228 return &*it.first->second;
231 TableGenIndexSymbol *getOrInsertDef(
const Record *record,
232 const RecordVal *value) {
233 auto it = defToSymbol.try_emplace(value,
nullptr);
236 std::make_unique<TableGenRecordValSymbol>(record, value);
238 return &*it.first->second;
242 MapT::Allocator allocator;
253void TableGenIndex::initialize(
const RecordKeeper &records) {
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();
264 if (startLoc == endLoc) {
266 startLoc = refLoc.Start.getPointer();
267 endLoc = refLoc.End.getPointer();
271 if (startLoc == endLoc)
279 if (!intervalMap.overlaps(startLoc, endLoc))
280 intervalMap.insert(startLoc, endLoc, sym);
283 sym->references.push_back(refLoc);
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,
true);
292 for (SMLoc loc : def.getLoc().drop_front())
294 for (SMRange loc : def.getReferenceLocs())
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,
true);
302 for (SMRange refLoc : def.getValueReferenceLocs(value.getNameInit()))
303 insertRef(sym, refLoc);
308const TableGenIndexSymbol *
309TableGenIndex::lookup(SMLoc loc, SMRange *overlappedRange)
const {
310 auto it = intervalMap.find(loc.getPointer());
311 if (!it.valid() || loc.getPointer() < it.start())
314 if (overlappedRange) {
315 *overlappedRange = SMRange(SMLoc::getFromPointer(it.start()),
316 SMLoc::getFromPointer(it.stop()));
327class TableGenTextFile {
329 TableGenTextFile(
const llvm::lsp::URIForFile &uri, StringRef fileContents,
331 const std::vector<std::string> &extraIncludeDirs,
332 std::vector<llvm::lsp::Diagnostic> &diagnostics);
335 int64_t getVersion()
const {
return version; }
340 update(
const llvm::lsp::URIForFile &uri, int64_t newVersion,
341 ArrayRef<llvm::lsp::TextDocumentContentChangeEvent> changes,
342 std::vector<llvm::lsp::Diagnostic> &diagnostics);
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);
359 void getDocumentLinks(
const llvm::lsp::URIForFile &uri,
360 std::vector<llvm::lsp::DocumentLink> &links);
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);
380 void initialize(
const llvm::lsp::URIForFile &uri, int64_t newVersion,
381 std::vector<llvm::lsp::Diagnostic> &diagnostics);
384 std::string contents;
390 std::vector<std::string> includeDirs;
396 std::unique_ptr<RecordKeeper> recordKeeper;
402 SmallVector<lsp::SourceMgrInclude> parsedIncludes;
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) {
413 llvm::sys::path::remove_filename(uriDirectory);
414 includeDirs.push_back(uriDirectory.str().str());
415 llvm::append_range(includeDirs, extraIncludeDirs);
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,
427 llvm::lsp::Logger::error(
"Failed to update contents of {0}", uri.file());
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>();
444 auto memBuffer = llvm::MemoryBuffer::getMemBuffer(contents, uri.file());
446 llvm::lsp::Logger::error(
"Failed to create memory buffer for file",
450 sourceMgr.setIncludeDirs(includeDirs);
451 sourceMgr.setVirtualFileSystem(llvm::vfs::getRealFileSystem());
452 sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
456 struct DiagHandlerContext {
457 std::vector<llvm::lsp::Diagnostic> &diagnostics;
458 const llvm::lsp::URIForFile &uri;
459 } handlerContext{diagnostics, uri};
462 sourceMgr.setDiagHandler(
463 [](
const llvm::SMDiagnostic &diag,
void *rawHandlerContext) {
464 auto *ctx =
reinterpret_cast<DiagHandlerContext *
>(rawHandlerContext);
466 ctx->diagnostics.push_back(*lspDiag);
469 bool failedToParse = llvm::TableGenParseFile(sourceMgr, *recordKeeper);
477 index.initialize(*recordKeeper);
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);
495 auto *valSym = dyn_cast<TableGenRecordValSymbol>(symbol);
497 if (
auto *val =
getBaseValue(valSym->record, valSym->getValue()).second) {
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);
515 for (SMRange refLoc : symbol->references)
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);
534std::optional<llvm::lsp::Hover>
535TableGenTextFile::findHover(
const llvm::lsp::URIForFile &uri,
536 const llvm::lsp::Position &hoverPos) {
538 for (
const lsp::SourceMgrInclude &include : parsedIncludes)
539 if (include.range.contains(hoverPos))
540 return include.buildHover();
544 SMLoc posLoc = hoverPos.getAsSMLoc(sourceMgr);
545 const TableGenIndexSymbol *symbol = index.lookup(posLoc, &hoverRange);
550 if (
auto *record = dyn_cast<TableGenRecordSymbol>(symbol))
551 return buildHoverForRecord(record->getValue(), hoverRange);
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);
563TableGenTextFile::buildHoverForRecord(
const Record *record,
564 const SMRange &hoverRange) {
565 llvm::lsp::Hover hover(llvm::lsp::Range(sourceMgr, hoverRange));
567 llvm::raw_string_ostream hoverOS(hover.contents.value);
570 if (record->isClass()) {
571 hoverOS <<
"**class** `" << record->getName() <<
"`";
572 }
else if (record->isAnonymous()) {
573 hoverOS <<
"**anonymous class**";
575 hoverOS <<
"**def** `" << record->getName() <<
"`";
577 hoverOS <<
"\n***\n";
581 auto printAndFormatField = [&](StringRef fieldName) {
584 const RecordVal *value = record->getValue(fieldName);
587 auto *stringValue = dyn_cast<llvm::StringInit>(value->getValue());
591 raw_indented_ostream ros(hoverOS);
592 ros.printReindented(stringValue->getValue().rtrim(
" \t"));
593 hoverOS <<
"\n***\n";
595 printAndFormatField(
"summary");
596 printAndFormatField(
"description");
599 if (std::optional<std::string> doc =
601 hoverOS <<
"\n" << *doc <<
"\n";
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));
611 llvm::raw_string_ostream hoverOS(hover.contents.value);
612 StringRef name = value->getName().rsplit(
':').second;
614 hoverOS <<
"**template arg** `" << name <<
"`\n***\nType: `";
615 value->getType()->print(hoverOS);
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));
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";
631 if (std::optional<std::string> doc =
633 hoverOS <<
"\n" << *doc <<
"\n";
634 hoverOS <<
"\n***\n";
639 auto [baseRecord, baseValue] =
getBaseValue(record, value);
641 if (std::optional<std::string> doc =
643 hoverOS <<
"\n *From `" << baseRecord->getName() <<
"`*:\n\n"
667 llvm::StringMap<std::unique_ptr<TableGenTextFile>>
files;
680 std::vector<Diagnostic> &diagnostics) {
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);
686 impl->files[uri.file()] = std::make_unique<TableGenTextFile>(
687 uri, contents, version, additionalIncludeDirs, diagnostics);
692 int64_t version, std::vector<Diagnostic> &diagnostics) {
694 auto it =
impl->files.find(uri.file());
695 if (it ==
impl->files.end())
700 if (failed(it->second->update(uri, version, changes, diagnostics)))
701 impl->files.erase(it);
704std::optional<int64_t>
706 auto it =
impl->files.find(uri.file());
707 if (it ==
impl->files.end())
710 int64_t version = it->second->getVersion();
711 impl->files.erase(it);
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);
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);
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);
738std::optional<llvm::lsp::Hover>
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);
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.
Include the generated interface declarations.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
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.