16 #include "llvm/ADT/IntervalMap.h"
17 #include "llvm/ADT/PointerUnion.h"
18 #include "llvm/ADT/StringMap.h"
19 #include "llvm/Support/Path.h"
20 #include "llvm/TableGen/Parser.h"
21 #include "llvm/TableGen/Record.h"
26 using llvm::RecordKeeper;
27 using llvm::RecordVal;
28 using llvm::SourceMgr;
41 int bufferId = mgr.FindBufferContainingLoc(loc);
42 if (bufferId == 0 || bufferId ==
static_cast<int>(mgr.getMainFileID()))
45 mgr.getBufferInfo(bufferId).Buffer->getBufferIdentifier());
65 static std::optional<lsp::Diagnostic>
68 auto *sourceMgr =
const_cast<SourceMgr *
>(
diag.getSourceMgr());
69 if (!sourceMgr || !
diag.getLoc().isValid())
73 lspDiag.
source =
"tablegen";
85 switch (
diag.getKind()) {
86 case SourceMgr::DK_Warning:
89 case SourceMgr::DK_Error:
92 case SourceMgr::DK_Note:
96 case SourceMgr::DK_Remark:
107 static std::pair<const Record *, const RecordVal *>
109 if (value->isTemplateArg())
110 return {
nullptr,
nullptr};
114 StringRef valueName = value->getName();
115 auto findValueInSupers = [&](
const Record *&record) ->
const RecordVal * {
116 for (
auto [parentRecord, loc] : record->getSuperClasses()) {
117 if (
auto *newBase = parentRecord->getValue(valueName)) {
118 record = parentRecord;
126 std::pair<const Record *, const RecordVal *> baseValue = {};
127 while (
const RecordVal *newBase = findValueInSupers(record))
128 baseValue = {record, newBase};
132 if (!baseValue.second || baseValue.second->getLoc() == value->getLoc())
133 return {
nullptr,
nullptr};
145 struct TableGenIndexSymbol {
146 TableGenIndexSymbol(
const Record *record)
147 : definition(record),
149 TableGenIndexSymbol(
const RecordVal *value)
151 virtual ~TableGenIndexSymbol() =
default;
163 struct TableGenRecordSymbol :
public TableGenIndexSymbol {
164 TableGenRecordSymbol(
const Record *record) : TableGenIndexSymbol(record) {}
165 ~TableGenRecordSymbol()
override =
default;
167 static bool classof(
const TableGenIndexSymbol *symbol) {
168 return symbol->definition.is<
const Record *>();
172 const Record *getValue()
const {
return definition.get<
const Record *>(); }
175 struct TableGenRecordValSymbol :
public TableGenIndexSymbol {
176 TableGenRecordValSymbol(
const Record *record,
const RecordVal *value)
177 : TableGenIndexSymbol(value), record(record) {}
178 ~TableGenRecordValSymbol()
override =
default;
180 static bool classof(
const TableGenIndexSymbol *symbol) {
181 return symbol->definition.is<
const RecordVal *>();
185 const RecordVal *getValue()
const {
186 return definition.get<
const RecordVal *>();
190 const Record *record;
196 class TableGenIndex {
198 TableGenIndex() : intervalMap(allocator) {}
201 void initialize(
const RecordKeeper &records);
206 const TableGenIndexSymbol *lookup(SMLoc loc,
207 SMRange *overlappedRange =
nullptr)
const;
212 using MapT = llvm::IntervalMap<
213 const char *,
const TableGenIndexSymbol *,
214 llvm::IntervalMapImpl::NodeSizer<
const char *,
215 const TableGenIndexSymbol *>::LeafSize,
216 llvm::IntervalMapHalfOpenInfo<const char *>>;
219 TableGenIndexSymbol *getOrInsertDef(
const Record *record) {
220 auto it = defToSymbol.try_emplace(record,
nullptr);
222 it.first->second = std::make_unique<TableGenRecordSymbol>(record);
223 return &*it.first->second;
226 TableGenIndexSymbol *getOrInsertDef(
const Record *record,
227 const RecordVal *value) {
228 auto it = defToSymbol.try_emplace(value,
nullptr);
231 std::make_unique<TableGenRecordValSymbol>(record, value);
233 return &*it.first->second;
237 MapT::Allocator allocator;
248 void TableGenIndex::initialize(
const RecordKeeper &records) {
252 auto insertRef = [&](TableGenIndexSymbol *sym, SMRange refLoc,
253 bool isDef =
false) {
254 const char *startLoc = refLoc.Start.getPointer();
255 const char *endLoc = refLoc.End.getPointer();
259 if (startLoc == endLoc) {
261 startLoc = refLoc.Start.getPointer();
262 endLoc = refLoc.End.getPointer();
266 if (startLoc == endLoc)
274 if (!intervalMap.overlaps(startLoc, endLoc))
275 intervalMap.insert(startLoc, endLoc, sym);
278 sym->references.push_back(refLoc);
281 llvm::make_pointee_range(llvm::make_second_range(records.getClasses()));
283 llvm::make_pointee_range(llvm::make_second_range(records.getDefs()));
284 for (
const Record &def : llvm::concat<Record>(classes, defs)) {
285 auto *sym = getOrInsertDef(&def);
286 insertRef(sym, sym->defLoc,
true);
289 for (SMLoc loc : def.getLoc().drop_front())
291 for (SMRange loc : def.getReferenceLocs())
295 for (
const RecordVal &value : def.getValues()) {
296 auto *sym = getOrInsertDef(&def, &value);
297 insertRef(sym, sym->defLoc,
true);
298 for (SMRange refLoc : value.getReferenceLocs())
299 insertRef(sym, refLoc);
304 const TableGenIndexSymbol *
305 TableGenIndex::lookup(SMLoc loc, SMRange *overlappedRange)
const {
306 auto it = intervalMap.find(loc.getPointer());
307 if (!it.valid() || loc.getPointer() < it.start())
310 if (overlappedRange) {
311 *overlappedRange = SMRange(SMLoc::getFromPointer(it.start()),
312 SMLoc::getFromPointer(it.stop()));
323 class TableGenTextFile {
327 const std::vector<std::string> &extraIncludeDirs,
328 std::vector<lsp::Diagnostic> &diagnostics);
331 int64_t getVersion()
const {
return version; }
337 std::vector<lsp::Diagnostic> &diagnostics);
344 std::vector<lsp::Location> &locations);
346 std::vector<lsp::Location> &references);
353 std::vector<lsp::DocumentLink> &links);
361 lsp::Hover buildHoverForRecord(
const Record *record,
362 const SMRange &hoverRange);
363 lsp::Hover buildHoverForTemplateArg(
const Record *record,
364 const RecordVal *value,
365 const SMRange &hoverRange);
366 lsp::Hover buildHoverForField(
const Record *record,
const RecordVal *value,
367 const SMRange &hoverRange);
372 std::vector<lsp::Diagnostic> &diagnostics);
375 std::string contents;
381 std::vector<std::string> includeDirs;
387 std::unique_ptr<RecordKeeper> recordKeeper;
397 TableGenTextFile::TableGenTextFile(
399 const std::vector<std::string> &extraIncludeDirs,
400 std::vector<lsp::Diagnostic> &diagnostics)
401 : contents(fileContents.str()), version(version) {
404 llvm::sys::path::remove_filename(uriDirectory);
405 includeDirs.push_back(uriDirectory.str().str());
406 includeDirs.insert(includeDirs.end(), extraIncludeDirs.begin(),
407 extraIncludeDirs.end());
410 initialize(uri, version, diagnostics);
414 TableGenTextFile::update(
const lsp::URIForFile &uri, int64_t newVersion,
416 std::vector<lsp::Diagnostic> &diagnostics) {
417 if (failed(lsp::TextDocumentContentChangeEvent::applyTo(changes, contents))) {
418 lsp::Logger::error(
"Failed to update contents of {0}", uri.
file());
423 initialize(uri, newVersion, diagnostics);
429 std::vector<lsp::Diagnostic> &diagnostics) {
430 version = newVersion;
431 sourceMgr = SourceMgr();
432 recordKeeper = std::make_unique<RecordKeeper>();
435 auto memBuffer = llvm::MemoryBuffer::getMemBuffer(contents, uri.
file());
437 lsp::Logger::error(
"Failed to create memory buffer for file", uri.
file());
440 sourceMgr.setIncludeDirs(includeDirs);
441 sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
445 struct DiagHandlerContext {
446 std::vector<lsp::Diagnostic> &diagnostics;
448 } handlerContext{diagnostics, uri};
451 sourceMgr.setDiagHandler(
452 [](
const llvm::SMDiagnostic &
diag,
void *rawHandlerContext) {
453 auto *ctx =
reinterpret_cast<DiagHandlerContext *
>(rawHandlerContext);
455 ctx->diagnostics.push_back(*lspDiag);
458 bool failedToParse = llvm::TableGenParseFile(sourceMgr, *recordKeeper);
466 index.initialize(*recordKeeper);
475 std::vector<lsp::Location> &locations) {
477 const TableGenIndexSymbol *symbol = index.lookup(posLoc);
484 auto *valSym = dyn_cast<TableGenRecordValSymbol>(symbol);
486 if (
auto *val =
getBaseValue(valSym->record, valSym->getValue()).second) {
495 void TableGenTextFile::findReferencesOf(
497 std::vector<lsp::Location> &references) {
499 const TableGenIndexSymbol *symbol = index.lookup(posLoc);
504 for (SMRange refLoc : symbol->references)
513 std::vector<lsp::DocumentLink> &links) {
515 links.emplace_back(include.range, include.uri);
522 std::optional<lsp::Hover>
527 if (include.range.contains(hoverPos))
528 return include.buildHover();
532 SMLoc posLoc = hoverPos.
getAsSMLoc(sourceMgr);
533 const TableGenIndexSymbol *symbol = index.lookup(posLoc, &hoverRange);
538 if (
auto *record = dyn_cast<TableGenRecordSymbol>(symbol))
539 return buildHoverForRecord(record->getValue(), hoverRange);
543 auto *recordVal = cast<TableGenRecordValSymbol>(symbol);
544 const RecordVal *value = recordVal->getValue();
545 if (value->isTemplateArg())
546 return buildHoverForTemplateArg(recordVal->record, value, hoverRange);
547 return buildHoverForField(recordVal->record, value, hoverRange);
550 lsp::Hover TableGenTextFile::buildHoverForRecord(
const Record *record,
551 const SMRange &hoverRange) {
554 llvm::raw_string_ostream hoverOS(hover.contents.value);
557 if (record->isClass()) {
558 hoverOS <<
"**class** `" << record->getName() <<
"`";
559 }
else if (record->isAnonymous()) {
560 hoverOS <<
"**anonymous class**";
562 hoverOS <<
"**def** `" << record->getName() <<
"`";
564 hoverOS <<
"\n***\n";
568 auto printAndFormatField = [&](StringRef fieldName) {
571 const RecordVal *value = record->getValue(fieldName);
572 if (!value || !value->getValue())
574 auto *stringValue = dyn_cast<llvm::StringInit>(value->getValue());
579 ros.printReindented(stringValue->getValue().rtrim(
" \t"));
580 hoverOS <<
"\n***\n";
582 printAndFormatField(
"summary");
583 printAndFormatField(
"description");
586 if (std::optional<std::string> doc =
588 hoverOS <<
"\n" << *doc <<
"\n";
594 lsp::Hover TableGenTextFile::buildHoverForTemplateArg(
595 const Record *record,
const RecordVal *value,
const SMRange &hoverRange) {
598 llvm::raw_string_ostream hoverOS(hover.contents.value);
599 StringRef name = value->getName().rsplit(
':').second;
601 hoverOS <<
"**template arg** `" << name <<
"`\n***\nType: `";
602 value->getType()->print(hoverOS);
608 lsp::Hover TableGenTextFile::buildHoverForField(
const Record *record,
609 const RecordVal *value,
610 const SMRange &hoverRange) {
613 llvm::raw_string_ostream hoverOS(hover.contents.value);
614 hoverOS <<
"**field** `" << value->getName() <<
"`\n***\nType: `";
615 value->getType()->print(hoverOS);
616 hoverOS <<
"`\n***\n";
619 if (std::optional<std::string> doc =
621 hoverOS <<
"\n" << *doc <<
"\n";
622 hoverOS <<
"\n***\n";
627 auto [baseRecord, baseValue] =
getBaseValue(record, value);
629 if (std::optional<std::string> doc =
631 hoverOS <<
"\n *From `" << baseRecord->getName() <<
"`*:\n\n"
655 llvm::StringMap<std::unique_ptr<TableGenTextFile>>
files;
668 std::vector<Diagnostic> &diagnostics) {
670 std::vector<std::string> additionalIncludeDirs =
impl->options.extraDirs;
671 const auto &fileInfo =
impl->compilationDatabase.getFileInfo(uri.
file());
672 llvm::append_range(additionalIncludeDirs, fileInfo.includeDirs);
674 impl->files[uri.
file()] = std::make_unique<TableGenTextFile>(
675 uri, contents, version, additionalIncludeDirs, diagnostics);
680 int64_t version, std::vector<Diagnostic> &diagnostics) {
682 auto it =
impl->files.find(uri.
file());
683 if (it ==
impl->files.end())
688 if (failed(it->second->update(uri, version, changes, diagnostics)))
689 impl->files.erase(it);
692 std::optional<int64_t>
694 auto it =
impl->files.find(uri.
file());
695 if (it ==
impl->files.end())
698 int64_t version = it->second->getVersion();
699 impl->files.erase(it);
705 std::vector<Location> &locations) {
706 auto fileIt =
impl->files.find(uri.
file());
707 if (fileIt !=
impl->files.end())
708 fileIt->second->getLocationsOf(uri, defPos, locations);
713 std::vector<Location> &references) {
714 auto fileIt =
impl->files.find(uri.
file());
715 if (fileIt !=
impl->files.end())
716 fileIt->second->findReferencesOf(uri, pos, references);
720 const URIForFile &uri, std::vector<DocumentLink> &documentLinks) {
721 auto fileIt =
impl->files.find(uri.
file());
722 if (fileIt !=
impl->files.end())
723 return fileIt->second->getDocumentLinks(uri, documentLinks);
726 std::optional<lsp::Hover>
729 auto fileIt =
impl->files.find(uri.
file());
730 if (fileIt !=
impl->files.end())
731 return fileIt->second->findHover(uri, hoverPos);
static std::string toString(bytecode::Section::ID sectionID)
Stringify the given section ID.
static bool contains(SMRange range, SMLoc loc)
Returns true if the given range contains the given source location.
static std::string diag(const llvm::Value &value)
static llvm::ManagedStatic< PassManagerOptions > options
static std::pair< const Record *, const RecordVal * > getBaseValue(const Record *record, const RecordVal *value)
Get the base definition of the given record value, or nullptr if one couldn't be found.
static lsp::Location getLocationFromLoc(SourceMgr &mgr, SMRange loc, const lsp::URIForFile &uri)
Returns a language server location from the given source range.
static std::optional< lsp::Diagnostic > getLspDiagnoticFromDiag(const llvm::SMDiagnostic &diag, const lsp::URIForFile &uri)
Convert the given TableGen diagnostic to the LSP form.
static lsp::URIForFile getURIFromLoc(const SourceMgr &mgr, SMLoc loc, const lsp::URIForFile &mainFileURI)
Returns a language server uri for the given source location.
static SMRange convertTokenLocToRange(SMLoc loc)
Returns the range of a lexical token given a SMLoc corresponding to the start of an token location.
This class contains a collection of compilation information for files provided to the language server...
static void error(const char *fmt, Ts &&...vals)
std::optional< int64_t > removeDocument(const URIForFile &uri)
Remove the document with the given uri.
void updateDocument(const URIForFile &uri, ArrayRef< TextDocumentContentChangeEvent > changes, int64_t version, std::vector< Diagnostic > &diagnostics)
Update the document, with the provided version, at the given URI.
std::optional< Hover > findHover(const URIForFile &uri, const Position &hoverPos)
Find a hover description for the given hover position, or std::nullopt if one couldn't be found.
void getDocumentLinks(const URIForFile &uri, std::vector< DocumentLink > &documentLinks)
Return the document links referenced by the given file.
void addDocument(const URIForFile &uri, StringRef contents, int64_t version, std::vector< Diagnostic > &diagnostics)
Add the document, with the provided version, at the given URI.
void getLocationsOf(const URIForFile &uri, const Position &defPos, std::vector< Location > &locations)
Return the locations of the object pointed at by the given position.
void findReferencesOf(const URIForFile &uri, const Position &pos, std::vector< Location > &references)
Find all references of the object pointed at by the given position.
URI in "file" scheme for a file.
static llvm::Expected< URIForFile > fromFile(StringRef absoluteFilepath, StringRef scheme="file")
Try to build a URIForFile from the given absolute file path and optional scheme.
StringRef file() const
Returns the absolute path to the file.
raw_ostream subclass that simplifies indention a sequence of code.
void gatherIncludeFiles(llvm::SourceMgr &sourceMgr, SmallVectorImpl< SourceMgrInclude > &includes)
Given a source manager, gather all of the processed include files.
std::optional< std::string > extractSourceDocComment(llvm::SourceMgr &sourceMgr, SMLoc loc)
Extract a documentation comment for the given location within the source manager.
SMRange convertTokenLocToRange(SMLoc loc, StringRef identifierChars="")
Returns the range of a lexical token given a SMLoc corresponding to the start of an token location.
Include the generated interface declarations.
Impl(const Options &options)
lsp::CompilationDatabase compilationDatabase
The compilation database containing additional information for files passed to the server.
const Options & options
TableGen LSP options.
llvm::StringMap< std::unique_ptr< TableGenTextFile > > files
The files held by the server, mapped by their URI file name.
std::string source
A human-readable string describing the source of this diagnostic, e.g.
DiagnosticSeverity severity
The diagnostic's severity.
Range range
The source range where the message applies.
std::string message
The diagnostic's message.
std::optional< std::string > category
The diagnostic's category.
URIForFile uri
The text document's URI.
SMLoc getAsSMLoc(llvm::SourceMgr &mgr) const
Convert this position into a source location in the main file of the given source manager.
This class represents a single include within a root file.