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"
37 int bufferId = mgr.FindBufferContainingLoc(loc);
38 if (bufferId == 0 || bufferId ==
static_cast<int>(mgr.getMainFileID()))
41 mgr.getBufferInfo(bufferId).Buffer->getBufferIdentifier());
61 static std::optional<lsp::Diagnostic>
64 auto *sourceMgr =
const_cast<llvm::SourceMgr *
>(
diag.getSourceMgr());
65 if (!sourceMgr || !
diag.getLoc().isValid())
69 lspDiag.
source =
"tablegen";
81 switch (
diag.getKind()) {
82 case llvm::SourceMgr::DK_Warning:
85 case llvm::SourceMgr::DK_Error:
88 case llvm::SourceMgr::DK_Note:
92 case llvm::SourceMgr::DK_Remark:
103 static std::pair<const llvm::Record *, const llvm::RecordVal *>
104 getBaseValue(
const llvm::Record *record,
const llvm::RecordVal *value) {
105 if (value->isTemplateArg())
106 return {
nullptr,
nullptr};
110 StringRef valueName = value->getName();
111 auto findValueInSupers =
112 [&](
const llvm::Record *&record) -> llvm::RecordVal * {
113 for (
auto [parentRecord, loc] : record->getSuperClasses()) {
114 if (
auto *newBase = parentRecord->getValue(valueName)) {
115 record = parentRecord;
123 std::pair<const llvm::Record *, const llvm::RecordVal *> baseValue = {};
124 while (
const llvm::RecordVal *newBase = findValueInSupers(record))
125 baseValue = {record, newBase};
129 if (!baseValue.second || baseValue.second->getLoc() == value->getLoc())
130 return {
nullptr,
nullptr};
142 struct TableGenIndexSymbol {
143 TableGenIndexSymbol(
const llvm::Record *record)
144 : definition(record),
146 TableGenIndexSymbol(
const llvm::RecordVal *value)
148 virtual ~TableGenIndexSymbol() =
default;
160 struct TableGenRecordSymbol :
public TableGenIndexSymbol {
161 TableGenRecordSymbol(
const llvm::Record *record)
162 : TableGenIndexSymbol(record) {}
163 ~TableGenRecordSymbol()
override =
default;
165 static bool classof(
const TableGenIndexSymbol *symbol) {
166 return symbol->definition.is<
const llvm::Record *>();
170 const llvm::Record *getValue()
const {
171 return definition.get<
const llvm::Record *>();
175 struct TableGenRecordValSymbol :
public TableGenIndexSymbol {
176 TableGenRecordValSymbol(
const llvm::Record *record,
177 const llvm::RecordVal *value)
178 : TableGenIndexSymbol(value), record(record) {}
179 ~TableGenRecordValSymbol()
override =
default;
181 static bool classof(
const TableGenIndexSymbol *symbol) {
182 return symbol->definition.is<
const llvm::RecordVal *>();
186 const llvm::RecordVal *getValue()
const {
187 return definition.get<
const llvm::RecordVal *>();
191 const llvm::Record *record;
197 class TableGenIndex {
199 TableGenIndex() : intervalMap(allocator) {}
202 void initialize(
const llvm::RecordKeeper &records);
207 const TableGenIndexSymbol *lookup(SMLoc loc,
208 SMRange *overlappedRange =
nullptr)
const;
213 using MapT = llvm::IntervalMap<
214 const char *,
const TableGenIndexSymbol *,
215 llvm::IntervalMapImpl::NodeSizer<
const char *,
216 const TableGenIndexSymbol *>::LeafSize,
217 llvm::IntervalMapHalfOpenInfo<const char *>>;
220 TableGenIndexSymbol *getOrInsertDef(
const llvm::Record *record) {
221 auto it = defToSymbol.try_emplace(record,
nullptr);
223 it.first->second = std::make_unique<TableGenRecordSymbol>(record);
224 return &*it.first->second;
227 TableGenIndexSymbol *getOrInsertDef(
const llvm::Record *record,
228 const llvm::RecordVal *value) {
229 auto it = defToSymbol.try_emplace(value,
nullptr);
232 std::make_unique<TableGenRecordValSymbol>(record, value);
234 return &*it.first->second;
238 MapT::Allocator allocator;
249 void TableGenIndex::initialize(
const llvm::RecordKeeper &records) {
253 auto insertRef = [&](TableGenIndexSymbol *sym, SMRange refLoc,
254 bool isDef =
false) {
255 const char *startLoc = refLoc.Start.getPointer();
256 const char *endLoc = refLoc.End.getPointer();
260 if (startLoc == endLoc) {
262 startLoc = refLoc.Start.getPointer();
263 endLoc = refLoc.End.getPointer();
267 if (startLoc == endLoc)
275 if (!intervalMap.overlaps(startLoc, endLoc))
276 intervalMap.insert(startLoc, endLoc, sym);
279 sym->references.push_back(refLoc);
282 llvm::make_pointee_range(llvm::make_second_range(records.getClasses()));
284 llvm::make_pointee_range(llvm::make_second_range(records.getDefs()));
285 for (
const llvm::Record &def : llvm::concat<llvm::Record>(classes, defs)) {
286 auto *sym = getOrInsertDef(&def);
287 insertRef(sym, sym->defLoc,
true);
290 for (SMLoc loc : def.getLoc().drop_front())
292 for (SMRange loc : def.getReferenceLocs())
296 for (
const llvm::RecordVal &value : def.getValues()) {
297 auto *sym = getOrInsertDef(&def, &value);
298 insertRef(sym, sym->defLoc,
true);
299 for (SMRange refLoc : value.getReferenceLocs())
300 insertRef(sym, refLoc);
305 const TableGenIndexSymbol *
306 TableGenIndex::lookup(SMLoc loc, SMRange *overlappedRange)
const {
307 auto it = intervalMap.find(loc.getPointer());
308 if (!it.valid() || loc.getPointer() < it.start())
311 if (overlappedRange) {
312 *overlappedRange = SMRange(SMLoc::getFromPointer(it.start()),
313 SMLoc::getFromPointer(it.stop()));
324 class TableGenTextFile {
328 const std::vector<std::string> &extraIncludeDirs,
329 std::vector<lsp::Diagnostic> &diagnostics);
332 int64_t getVersion()
const {
return version; }
338 std::vector<lsp::Diagnostic> &diagnostics);
345 std::vector<lsp::Location> &locations);
347 std::vector<lsp::Location> &references);
354 std::vector<lsp::DocumentLink> &links);
362 lsp::Hover buildHoverForRecord(
const llvm::Record *record,
363 const SMRange &hoverRange);
364 lsp::Hover buildHoverForTemplateArg(
const llvm::Record *record,
365 const llvm::RecordVal *value,
366 const SMRange &hoverRange);
367 lsp::Hover buildHoverForField(
const llvm::Record *record,
368 const llvm::RecordVal *value,
369 const SMRange &hoverRange);
374 std::vector<lsp::Diagnostic> &diagnostics);
377 std::string contents;
383 std::vector<std::string> includeDirs;
386 llvm::SourceMgr sourceMgr;
389 std::unique_ptr<llvm::RecordKeeper> recordKeeper;
399 TableGenTextFile::TableGenTextFile(
401 const std::vector<std::string> &extraIncludeDirs,
402 std::vector<lsp::Diagnostic> &diagnostics)
403 : contents(fileContents.str()), version(version) {
406 llvm::sys::path::remove_filename(uriDirectory);
407 includeDirs.push_back(uriDirectory.str().str());
408 includeDirs.insert(includeDirs.end(), extraIncludeDirs.begin(),
409 extraIncludeDirs.end());
412 initialize(uri, version, diagnostics);
416 TableGenTextFile::update(
const lsp::URIForFile &uri, int64_t newVersion,
418 std::vector<lsp::Diagnostic> &diagnostics) {
419 if (failed(lsp::TextDocumentContentChangeEvent::applyTo(changes, contents))) {
420 lsp::Logger::error(
"Failed to update contents of {0}", uri.
file());
425 initialize(uri, newVersion, diagnostics);
431 std::vector<lsp::Diagnostic> &diagnostics) {
432 version = newVersion;
433 sourceMgr = llvm::SourceMgr();
434 recordKeeper = std::make_unique<llvm::RecordKeeper>();
437 auto memBuffer = llvm::MemoryBuffer::getMemBuffer(contents, uri.
file());
439 lsp::Logger::error(
"Failed to create memory buffer for file", uri.
file());
442 sourceMgr.setIncludeDirs(includeDirs);
443 sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
447 struct DiagHandlerContext {
448 std::vector<lsp::Diagnostic> &diagnostics;
450 } handlerContext{diagnostics, uri};
453 sourceMgr.setDiagHandler(
454 [](
const llvm::SMDiagnostic &
diag,
void *rawHandlerContext) {
455 auto *ctx =
reinterpret_cast<DiagHandlerContext *
>(rawHandlerContext);
457 ctx->diagnostics.push_back(*lspDiag);
460 bool failedToParse = llvm::TableGenParseFile(sourceMgr, *recordKeeper);
468 index.initialize(*recordKeeper);
477 std::vector<lsp::Location> &locations) {
479 const TableGenIndexSymbol *symbol = index.lookup(posLoc);
486 auto *valSym = dyn_cast<TableGenRecordValSymbol>(symbol);
488 if (
auto *val =
getBaseValue(valSym->record, valSym->getValue()).second) {
497 void TableGenTextFile::findReferencesOf(
499 std::vector<lsp::Location> &references) {
501 const TableGenIndexSymbol *symbol = index.lookup(posLoc);
506 for (SMRange refLoc : symbol->references)
515 std::vector<lsp::DocumentLink> &links) {
517 links.emplace_back(include.range, include.uri);
524 std::optional<lsp::Hover>
529 if (include.range.contains(hoverPos))
530 return include.buildHover();
534 SMLoc posLoc = hoverPos.
getAsSMLoc(sourceMgr);
535 const TableGenIndexSymbol *symbol = index.lookup(posLoc, &hoverRange);
540 if (
auto *record = dyn_cast<TableGenRecordSymbol>(symbol))
541 return buildHoverForRecord(record->getValue(), hoverRange);
545 auto *recordVal = cast<TableGenRecordValSymbol>(symbol);
546 const llvm::RecordVal *value = recordVal->getValue();
547 if (value->isTemplateArg())
548 return buildHoverForTemplateArg(recordVal->record, value, hoverRange);
549 return buildHoverForField(recordVal->record, value, hoverRange);
552 lsp::Hover TableGenTextFile::buildHoverForRecord(
const llvm::Record *record,
553 const SMRange &hoverRange) {
556 llvm::raw_string_ostream hoverOS(hover.contents.value);
559 if (record->isClass()) {
560 hoverOS <<
"**class** `" << record->getName() <<
"`";
561 }
else if (record->isAnonymous()) {
562 hoverOS <<
"**anonymous class**";
564 hoverOS <<
"**def** `" << record->getName() <<
"`";
566 hoverOS <<
"\n***\n";
570 auto printAndFormatField = [&](StringRef fieldName) {
573 const llvm::RecordVal *value = record->getValue(fieldName);
574 if (!value || !value->getValue())
576 auto *stringValue = dyn_cast<llvm::StringInit>(value->getValue());
581 ros.printReindented(stringValue->getValue().rtrim(
" \t"));
582 hoverOS <<
"\n***\n";
584 printAndFormatField(
"summary");
585 printAndFormatField(
"description");
588 if (std::optional<std::string> doc =
590 hoverOS <<
"\n" << *doc <<
"\n";
597 TableGenTextFile::buildHoverForTemplateArg(
const llvm::Record *record,
598 const llvm::RecordVal *value,
599 const SMRange &hoverRange) {
602 llvm::raw_string_ostream hoverOS(hover.contents.value);
603 StringRef name = value->getName().rsplit(
':').second;
605 hoverOS <<
"**template arg** `" << name <<
"`\n***\nType: `";
606 value->getType()->print(hoverOS);
612 lsp::Hover TableGenTextFile::buildHoverForField(
const llvm::Record *record,
613 const llvm::RecordVal *value,
614 const SMRange &hoverRange) {
617 llvm::raw_string_ostream hoverOS(hover.contents.value);
618 hoverOS <<
"**field** `" << value->getName() <<
"`\n***\nType: `";
619 value->getType()->print(hoverOS);
620 hoverOS <<
"`\n***\n";
623 if (std::optional<std::string> doc =
625 hoverOS <<
"\n" << *doc <<
"\n";
626 hoverOS <<
"\n***\n";
631 auto [baseRecord, baseValue] =
getBaseValue(record, value);
633 if (std::optional<std::string> doc =
635 hoverOS <<
"\n *From `" << baseRecord->getName() <<
"`*:\n\n"
659 llvm::StringMap<std::unique_ptr<TableGenTextFile>>
files;
672 std::vector<Diagnostic> &diagnostics) {
674 std::vector<std::string> additionalIncludeDirs =
impl->options.extraDirs;
675 const auto &fileInfo =
impl->compilationDatabase.getFileInfo(uri.
file());
676 llvm::append_range(additionalIncludeDirs, fileInfo.includeDirs);
678 impl->files[uri.
file()] = std::make_unique<TableGenTextFile>(
679 uri, contents, version, additionalIncludeDirs, diagnostics);
684 int64_t version, std::vector<Diagnostic> &diagnostics) {
686 auto it =
impl->files.find(uri.
file());
687 if (it ==
impl->files.end())
692 if (failed(it->second->update(uri, version, changes, diagnostics)))
693 impl->files.erase(it);
696 std::optional<int64_t>
698 auto it =
impl->files.find(uri.
file());
699 if (it ==
impl->files.end())
702 int64_t version = it->second->getVersion();
703 impl->files.erase(it);
709 std::vector<Location> &locations) {
710 auto fileIt =
impl->files.find(uri.
file());
711 if (fileIt !=
impl->files.end())
712 fileIt->second->getLocationsOf(uri, defPos, locations);
717 std::vector<Location> &references) {
718 auto fileIt =
impl->files.find(uri.
file());
719 if (fileIt !=
impl->files.end())
720 fileIt->second->findReferencesOf(uri, pos, references);
724 const URIForFile &uri, std::vector<DocumentLink> &documentLinks) {
725 auto fileIt =
impl->files.find(uri.
file());
726 if (fileIt !=
impl->files.end())
727 return fileIt->second->getDocumentLinks(uri, documentLinks);
730 std::optional<lsp::Hover>
733 auto fileIt =
impl->files.find(uri.
file());
734 if (fileIt !=
impl->files.end())
735 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.
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.