17 #include "llvm/ADT/IntervalMap.h"
18 #include "llvm/ADT/PointerUnion.h"
19 #include "llvm/ADT/StringMap.h"
20 #include "llvm/ADT/StringSet.h"
21 #include "llvm/ADT/TypeSwitch.h"
22 #include "llvm/Support/FileSystem.h"
23 #include "llvm/Support/Path.h"
24 #include "llvm/TableGen/Parser.h"
25 #include "llvm/TableGen/Record.h"
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<llvm::SourceMgr *
>(
diag.getSourceMgr());
69 if (!sourceMgr || !
diag.getLoc().isValid())
73 lspDiag.
source =
"tablegen";
85 switch (
diag.getKind()) {
86 case llvm::SourceMgr::DK_Warning:
89 case llvm::SourceMgr::DK_Error:
92 case llvm::SourceMgr::DK_Note:
96 case llvm::SourceMgr::DK_Remark:
107 static std::pair<const llvm::Record *, const llvm::RecordVal *>
108 getBaseValue(
const llvm::Record *record,
const llvm::RecordVal *value) {
109 if (value->isTemplateArg())
110 return {
nullptr,
nullptr};
114 StringRef valueName = value->getName();
115 auto findValueInSupers =
116 [&](
const llvm::Record *&record) -> llvm::RecordVal * {
117 for (
auto [parentRecord, loc] : record->getSuperClasses()) {
118 if (
auto *newBase = parentRecord->getValue(valueName)) {
119 record = parentRecord;
127 std::pair<const llvm::Record *, const llvm::RecordVal *> baseValue = {};
128 while (
const llvm::RecordVal *newBase = findValueInSupers(record))
129 baseValue = {record, newBase};
133 if (!baseValue.second || baseValue.second->getLoc() == value->getLoc())
134 return {
nullptr,
nullptr};
146 struct TableGenIndexSymbol {
147 TableGenIndexSymbol(
const llvm::Record *record)
148 : definition(record),
150 TableGenIndexSymbol(
const llvm::RecordVal *value)
152 virtual ~TableGenIndexSymbol() =
default;
164 struct TableGenRecordSymbol :
public TableGenIndexSymbol {
165 TableGenRecordSymbol(
const llvm::Record *record)
166 : TableGenIndexSymbol(record) {}
167 ~TableGenRecordSymbol()
override =
default;
169 static bool classof(
const TableGenIndexSymbol *symbol) {
170 return symbol->definition.is<
const llvm::Record *>();
174 const llvm::Record *getValue()
const {
175 return definition.get<
const llvm::Record *>();
179 struct TableGenRecordValSymbol :
public TableGenIndexSymbol {
180 TableGenRecordValSymbol(
const llvm::Record *record,
181 const llvm::RecordVal *value)
182 : TableGenIndexSymbol(value), record(record) {}
183 ~TableGenRecordValSymbol()
override =
default;
185 static bool classof(
const TableGenIndexSymbol *symbol) {
186 return symbol->definition.is<
const llvm::RecordVal *>();
190 const llvm::RecordVal *getValue()
const {
191 return definition.get<
const llvm::RecordVal *>();
195 const llvm::Record *record;
201 class TableGenIndex {
203 TableGenIndex() : intervalMap(allocator) {}
206 void initialize(
const llvm::RecordKeeper &records);
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 llvm::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 llvm::Record *record,
232 const llvm::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;
253 void TableGenIndex::initialize(
const llvm::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);
286 llvm::make_pointee_range(llvm::make_second_range(records.getClasses()));
288 llvm::make_pointee_range(llvm::make_second_range(records.getDefs()));
289 for (
const llvm::Record &def : llvm::concat<llvm::Record>(classes, defs)) {
290 auto *sym = getOrInsertDef(&def);
291 insertRef(sym, sym->defLoc,
true);
294 for (SMLoc loc : def.getLoc().drop_front())
296 for (SMRange loc : def.getReferenceLocs())
300 for (
const llvm::RecordVal &value : def.getValues()) {
301 auto *sym = getOrInsertDef(&def, &value);
302 insertRef(sym, sym->defLoc,
true);
303 for (SMRange refLoc : value.getReferenceLocs())
304 insertRef(sym, refLoc);
309 const TableGenIndexSymbol *
310 TableGenIndex::lookup(SMLoc loc, SMRange *overlappedRange)
const {
311 auto it = intervalMap.find(loc.getPointer());
312 if (!it.valid() || loc.getPointer() < it.start())
315 if (overlappedRange) {
316 *overlappedRange = SMRange(SMLoc::getFromPointer(it.start()),
317 SMLoc::getFromPointer(it.stop()));
328 class TableGenTextFile {
332 const std::vector<std::string> &extraIncludeDirs,
333 std::vector<lsp::Diagnostic> &diagnostics);
336 int64_t getVersion()
const {
return version; }
342 std::vector<lsp::Diagnostic> &diagnostics);
349 std::vector<lsp::Location> &locations);
351 std::vector<lsp::Location> &references);
358 std::vector<lsp::DocumentLink> &links);
366 lsp::Hover buildHoverForRecord(
const llvm::Record *record,
367 const SMRange &hoverRange);
368 lsp::Hover buildHoverForTemplateArg(
const llvm::Record *record,
369 const llvm::RecordVal *value,
370 const SMRange &hoverRange);
371 lsp::Hover buildHoverForField(
const llvm::Record *record,
372 const llvm::RecordVal *value,
373 const SMRange &hoverRange);
378 std::vector<lsp::Diagnostic> &diagnostics);
381 std::string contents;
387 std::vector<std::string> includeDirs;
390 llvm::SourceMgr sourceMgr;
393 std::unique_ptr<llvm::RecordKeeper> recordKeeper;
403 TableGenTextFile::TableGenTextFile(
405 const std::vector<std::string> &extraIncludeDirs,
406 std::vector<lsp::Diagnostic> &diagnostics)
407 : contents(fileContents.str()), version(version) {
410 llvm::sys::path::remove_filename(uriDirectory);
411 includeDirs.push_back(uriDirectory.str().str());
412 includeDirs.insert(includeDirs.end(), extraIncludeDirs.begin(),
413 extraIncludeDirs.end());
416 initialize(uri, version, diagnostics);
420 TableGenTextFile::update(
const lsp::URIForFile &uri, int64_t newVersion,
422 std::vector<lsp::Diagnostic> &diagnostics) {
423 if (
failed(lsp::TextDocumentContentChangeEvent::applyTo(changes, contents))) {
424 lsp::Logger::error(
"Failed to update contents of {0}", uri.
file());
429 initialize(uri, newVersion, diagnostics);
435 std::vector<lsp::Diagnostic> &diagnostics) {
436 version = newVersion;
437 sourceMgr = llvm::SourceMgr();
438 recordKeeper = std::make_unique<llvm::RecordKeeper>();
441 auto memBuffer = llvm::MemoryBuffer::getMemBuffer(contents, uri.
file());
443 lsp::Logger::error(
"Failed to create memory buffer for file", uri.
file());
446 sourceMgr.setIncludeDirs(includeDirs);
447 sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
451 struct DiagHandlerContext {
452 std::vector<lsp::Diagnostic> &diagnostics;
454 } handlerContext{diagnostics, uri};
457 sourceMgr.setDiagHandler(
458 [](
const llvm::SMDiagnostic &
diag,
void *rawHandlerContext) {
459 auto *ctx =
reinterpret_cast<DiagHandlerContext *
>(rawHandlerContext);
461 ctx->diagnostics.push_back(*lspDiag);
464 bool failedToParse = llvm::TableGenParseFile(sourceMgr, *recordKeeper);
472 index.initialize(*recordKeeper);
481 std::vector<lsp::Location> &locations) {
483 const TableGenIndexSymbol *symbol = index.lookup(posLoc);
490 auto *valSym = dyn_cast<TableGenRecordValSymbol>(symbol);
492 if (
auto *val =
getBaseValue(valSym->record, valSym->getValue()).second) {
501 void TableGenTextFile::findReferencesOf(
503 std::vector<lsp::Location> &references) {
505 const TableGenIndexSymbol *symbol = index.lookup(posLoc);
510 for (SMRange refLoc : symbol->references)
519 std::vector<lsp::DocumentLink> &links) {
521 links.emplace_back(include.range, include.uri);
528 std::optional<lsp::Hover>
533 if (include.range.contains(hoverPos))
534 return include.buildHover();
538 SMLoc posLoc = hoverPos.
getAsSMLoc(sourceMgr);
539 const TableGenIndexSymbol *symbol = index.lookup(posLoc, &hoverRange);
544 if (
auto *record = dyn_cast<TableGenRecordSymbol>(symbol))
545 return buildHoverForRecord(record->getValue(), hoverRange);
549 auto *recordVal = cast<TableGenRecordValSymbol>(symbol);
550 const llvm::RecordVal *value = recordVal->getValue();
551 if (value->isTemplateArg())
552 return buildHoverForTemplateArg(recordVal->record, value, hoverRange);
553 return buildHoverForField(recordVal->record, value, hoverRange);
556 lsp::Hover TableGenTextFile::buildHoverForRecord(
const llvm::Record *record,
557 const SMRange &hoverRange) {
560 llvm::raw_string_ostream hoverOS(hover.contents.value);
563 if (record->isClass()) {
564 hoverOS <<
"**class** `" << record->getName() <<
"`";
565 }
else if (record->isAnonymous()) {
566 hoverOS <<
"**anonymous class**";
568 hoverOS <<
"**def** `" << record->getName() <<
"`";
570 hoverOS <<
"\n***\n";
574 auto printAndFormatField = [&](StringRef fieldName) {
577 const llvm::RecordVal *value = record->getValue(fieldName);
578 if (!value || !value->getValue())
580 auto *stringValue = dyn_cast<llvm::StringInit>(value->getValue());
585 ros.printReindented(stringValue->getValue().rtrim(
" \t"));
586 hoverOS <<
"\n***\n";
588 printAndFormatField(
"summary");
589 printAndFormatField(
"description");
592 if (std::optional<std::string> doc =
594 hoverOS <<
"\n" << *doc <<
"\n";
601 TableGenTextFile::buildHoverForTemplateArg(
const llvm::Record *record,
602 const llvm::RecordVal *value,
603 const SMRange &hoverRange) {
606 llvm::raw_string_ostream hoverOS(hover.contents.value);
607 StringRef name = value->getName().rsplit(
':').second;
609 hoverOS <<
"**template arg** `" << name <<
"`\n***\nType: `";
610 value->getType()->print(hoverOS);
616 lsp::Hover TableGenTextFile::buildHoverForField(
const llvm::Record *record,
617 const llvm::RecordVal *value,
618 const SMRange &hoverRange) {
621 llvm::raw_string_ostream hoverOS(hover.contents.value);
622 hoverOS <<
"**field** `" << value->getName() <<
"`\n***\nType: `";
623 value->getType()->print(hoverOS);
624 hoverOS <<
"`\n***\n";
627 if (std::optional<std::string> doc =
629 hoverOS <<
"\n" << *doc <<
"\n";
630 hoverOS <<
"\n***\n";
635 auto [baseRecord, baseValue] =
getBaseValue(record, value);
637 if (std::optional<std::string> doc =
639 hoverOS <<
"\n *From `" << baseRecord->getName() <<
"`*:\n\n"
663 llvm::StringMap<std::unique_ptr<TableGenTextFile>>
files;
676 std::vector<Diagnostic> &diagnostics) {
678 std::vector<std::string> additionalIncludeDirs =
impl->options.extraDirs;
679 const auto &fileInfo =
impl->compilationDatabase.getFileInfo(uri.
file());
680 llvm::append_range(additionalIncludeDirs, fileInfo.includeDirs);
682 impl->files[uri.
file()] = std::make_unique<TableGenTextFile>(
683 uri, contents, version, additionalIncludeDirs, diagnostics);
688 int64_t version, std::vector<Diagnostic> &diagnostics) {
690 auto it =
impl->files.find(uri.
file());
691 if (it ==
impl->files.end())
696 if (
failed(it->second->update(uri, version, changes, diagnostics)))
697 impl->files.erase(it);
700 std::optional<int64_t>
702 auto it =
impl->files.find(uri.
file());
703 if (it ==
impl->files.end())
706 int64_t version = it->second->getVersion();
707 impl->files.erase(it);
713 std::vector<Location> &locations) {
714 auto fileIt =
impl->files.find(uri.
file());
715 if (fileIt !=
impl->files.end())
716 fileIt->second->getLocationsOf(uri, defPos, locations);
721 std::vector<Location> &references) {
722 auto fileIt =
impl->files.find(uri.
file());
723 if (fileIt !=
impl->files.end())
724 fileIt->second->findReferencesOf(uri, pos, references);
728 const URIForFile &uri, std::vector<DocumentLink> &documentLinks) {
729 auto fileIt =
impl->files.find(uri.
file());
730 if (fileIt !=
impl->files.end())
731 return fileIt->second->getDocumentLinks(uri, documentLinks);
734 std::optional<lsp::Hover>
737 auto fileIt =
impl->files.find(uri.
file());
738 if (fileIt !=
impl->files.end())
739 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 lsp::URIForFile getURIFromLoc(const llvm::SourceMgr &mgr, SMLoc loc, const lsp::URIForFile &mainFileURI)
Returns a language server uri for the given source location.
static std::pair< const llvm::Record *, const llvm::RecordVal * > getBaseValue(const llvm::Record *record, const llvm::RecordVal *value)
Get the base definition of the given record value, or nullptr if one couldn't be found.
static lsp::Location getLocationFromLoc(llvm::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 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.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value.
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.
This class represents an efficient way to signal success or failure.
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.