[LLVM] Fix Maintainers.md formatting (NFC)
[llvm-project.git] / mlir / lib / Tools / mlir-pdll-lsp-server / PDLLServer.cpp
blob76b066feb69308ffd7603a9381e8fd50f0cdeceb
1 //===- PDLLServer.cpp - PDLL 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 //===----------------------------------------------------------------------===//
9 #include "PDLLServer.h"
11 #include "Protocol.h"
12 #include "mlir/IR/BuiltinOps.h"
13 #include "mlir/Support/ToolUtilities.h"
14 #include "mlir/Tools/PDLL/AST/Context.h"
15 #include "mlir/Tools/PDLL/AST/Nodes.h"
16 #include "mlir/Tools/PDLL/AST/Types.h"
17 #include "mlir/Tools/PDLL/CodeGen/CPPGen.h"
18 #include "mlir/Tools/PDLL/CodeGen/MLIRGen.h"
19 #include "mlir/Tools/PDLL/ODS/Constraint.h"
20 #include "mlir/Tools/PDLL/ODS/Context.h"
21 #include "mlir/Tools/PDLL/ODS/Dialect.h"
22 #include "mlir/Tools/PDLL/ODS/Operation.h"
23 #include "mlir/Tools/PDLL/Parser/CodeComplete.h"
24 #include "mlir/Tools/PDLL/Parser/Parser.h"
25 #include "mlir/Tools/lsp-server-support/CompilationDatabase.h"
26 #include "mlir/Tools/lsp-server-support/Logging.h"
27 #include "mlir/Tools/lsp-server-support/SourceMgrUtils.h"
28 #include "llvm/ADT/IntervalMap.h"
29 #include "llvm/ADT/StringMap.h"
30 #include "llvm/ADT/StringSet.h"
31 #include "llvm/ADT/TypeSwitch.h"
32 #include "llvm/Support/FileSystem.h"
33 #include "llvm/Support/Path.h"
34 #include <optional>
36 using namespace mlir;
37 using namespace mlir::pdll;
39 /// Returns a language server uri for the given source location. `mainFileURI`
40 /// corresponds to the uri for the main file of the source manager.
41 static lsp::URIForFile getURIFromLoc(llvm::SourceMgr &mgr, SMRange loc,
42 const lsp::URIForFile &mainFileURI) {
43 int bufferId = mgr.FindBufferContainingLoc(loc.Start);
44 if (bufferId == 0 || bufferId == static_cast<int>(mgr.getMainFileID()))
45 return mainFileURI;
46 llvm::Expected<lsp::URIForFile> fileForLoc = lsp::URIForFile::fromFile(
47 mgr.getBufferInfo(bufferId).Buffer->getBufferIdentifier());
48 if (fileForLoc)
49 return *fileForLoc;
50 lsp::Logger::error("Failed to create URI for include file: {0}",
51 llvm::toString(fileForLoc.takeError()));
52 return mainFileURI;
55 /// Returns true if the given location is in the main file of the source
56 /// manager.
57 static bool isMainFileLoc(llvm::SourceMgr &mgr, SMRange loc) {
58 return mgr.FindBufferContainingLoc(loc.Start) == mgr.getMainFileID();
61 /// Returns a language server location from the given source range.
62 static lsp::Location getLocationFromLoc(llvm::SourceMgr &mgr, SMRange range,
63 const lsp::URIForFile &uri) {
64 return lsp::Location(getURIFromLoc(mgr, range, uri), lsp::Range(mgr, range));
67 /// Convert the given MLIR diagnostic to the LSP form.
68 static std::optional<lsp::Diagnostic>
69 getLspDiagnoticFromDiag(llvm::SourceMgr &sourceMgr, const ast::Diagnostic &diag,
70 const lsp::URIForFile &uri) {
71 lsp::Diagnostic lspDiag;
72 lspDiag.source = "pdll";
74 // FIXME: Right now all of the diagnostics are treated as parser issues, but
75 // some are parser and some are verifier.
76 lspDiag.category = "Parse Error";
78 // Try to grab a file location for this diagnostic.
79 lsp::Location loc = getLocationFromLoc(sourceMgr, diag.getLocation(), uri);
80 lspDiag.range = loc.range;
82 // Skip diagnostics that weren't emitted within the main file.
83 if (loc.uri != uri)
84 return std::nullopt;
86 // Convert the severity for the diagnostic.
87 switch (diag.getSeverity()) {
88 case ast::Diagnostic::Severity::DK_Note:
89 llvm_unreachable("expected notes to be handled separately");
90 case ast::Diagnostic::Severity::DK_Warning:
91 lspDiag.severity = lsp::DiagnosticSeverity::Warning;
92 break;
93 case ast::Diagnostic::Severity::DK_Error:
94 lspDiag.severity = lsp::DiagnosticSeverity::Error;
95 break;
96 case ast::Diagnostic::Severity::DK_Remark:
97 lspDiag.severity = lsp::DiagnosticSeverity::Information;
98 break;
100 lspDiag.message = diag.getMessage().str();
102 // Attach any notes to the main diagnostic as related information.
103 std::vector<lsp::DiagnosticRelatedInformation> relatedDiags;
104 for (const ast::Diagnostic &note : diag.getNotes()) {
105 relatedDiags.emplace_back(
106 getLocationFromLoc(sourceMgr, note.getLocation(), uri),
107 note.getMessage().str());
109 if (!relatedDiags.empty())
110 lspDiag.relatedInformation = std::move(relatedDiags);
112 return lspDiag;
115 /// Get or extract the documentation for the given decl.
116 static std::optional<std::string>
117 getDocumentationFor(llvm::SourceMgr &sourceMgr, const ast::Decl *decl) {
118 // If the decl already had documentation set, use it.
119 if (std::optional<StringRef> doc = decl->getDocComment())
120 return doc->str();
122 // If the decl doesn't yet have documentation, try to extract it from the
123 // source file.
124 return lsp::extractSourceDocComment(sourceMgr, decl->getLoc().Start);
127 //===----------------------------------------------------------------------===//
128 // PDLIndex
129 //===----------------------------------------------------------------------===//
131 namespace {
132 struct PDLIndexSymbol {
133 explicit PDLIndexSymbol(const ast::Decl *definition)
134 : definition(definition) {}
135 explicit PDLIndexSymbol(const ods::Operation *definition)
136 : definition(definition) {}
138 /// Return the location of the definition of this symbol.
139 SMRange getDefLoc() const {
140 if (const ast::Decl *decl =
141 llvm::dyn_cast_if_present<const ast::Decl *>(definition)) {
142 const ast::Name *declName = decl->getName();
143 return declName ? declName->getLoc() : decl->getLoc();
145 return definition.get<const ods::Operation *>()->getLoc();
148 /// The main definition of the symbol.
149 PointerUnion<const ast::Decl *, const ods::Operation *> definition;
150 /// The set of references to the symbol.
151 std::vector<SMRange> references;
154 /// This class provides an index for definitions/uses within a PDL document.
155 /// It provides efficient lookup of a definition given an input source range.
156 class PDLIndex {
157 public:
158 PDLIndex() : intervalMap(allocator) {}
160 /// Initialize the index with the given ast::Module.
161 void initialize(const ast::Module &module, const ods::Context &odsContext);
163 /// Lookup a symbol for the given location. Returns nullptr if no symbol could
164 /// be found. If provided, `overlappedRange` is set to the range that the
165 /// provided `loc` overlapped with.
166 const PDLIndexSymbol *lookup(SMLoc loc,
167 SMRange *overlappedRange = nullptr) const;
169 private:
170 /// The type of interval map used to store source references. SMRange is
171 /// half-open, so we also need to use a half-open interval map.
172 using MapT =
173 llvm::IntervalMap<const char *, const PDLIndexSymbol *,
174 llvm::IntervalMapImpl::NodeSizer<
175 const char *, const PDLIndexSymbol *>::LeafSize,
176 llvm::IntervalMapHalfOpenInfo<const char *>>;
178 /// An allocator for the interval map.
179 MapT::Allocator allocator;
181 /// An interval map containing a corresponding definition mapped to a source
182 /// interval.
183 MapT intervalMap;
185 /// A mapping between definitions and their corresponding symbol.
186 DenseMap<const void *, std::unique_ptr<PDLIndexSymbol>> defToSymbol;
188 } // namespace
190 void PDLIndex::initialize(const ast::Module &module,
191 const ods::Context &odsContext) {
192 auto getOrInsertDef = [&](const auto *def) -> PDLIndexSymbol * {
193 auto it = defToSymbol.try_emplace(def, nullptr);
194 if (it.second)
195 it.first->second = std::make_unique<PDLIndexSymbol>(def);
196 return &*it.first->second;
198 auto insertDeclRef = [&](PDLIndexSymbol *sym, SMRange refLoc,
199 bool isDef = false) {
200 const char *startLoc = refLoc.Start.getPointer();
201 const char *endLoc = refLoc.End.getPointer();
202 if (!intervalMap.overlaps(startLoc, endLoc)) {
203 intervalMap.insert(startLoc, endLoc, sym);
204 if (!isDef)
205 sym->references.push_back(refLoc);
208 auto insertODSOpRef = [&](StringRef opName, SMRange refLoc) {
209 const ods::Operation *odsOp = odsContext.lookupOperation(opName);
210 if (!odsOp)
211 return;
213 PDLIndexSymbol *symbol = getOrInsertDef(odsOp);
214 insertDeclRef(symbol, odsOp->getLoc(), /*isDef=*/true);
215 insertDeclRef(symbol, refLoc);
218 module.walk([&](const ast::Node *node) {
219 // Handle references to PDL decls.
220 if (const auto *decl = dyn_cast<ast::OpNameDecl>(node)) {
221 if (std::optional<StringRef> name = decl->getName())
222 insertODSOpRef(*name, decl->getLoc());
223 } else if (const ast::Decl *decl = dyn_cast<ast::Decl>(node)) {
224 const ast::Name *name = decl->getName();
225 if (!name)
226 return;
227 PDLIndexSymbol *declSym = getOrInsertDef(decl);
228 insertDeclRef(declSym, name->getLoc(), /*isDef=*/true);
230 if (const auto *varDecl = dyn_cast<ast::VariableDecl>(decl)) {
231 // Record references to any constraints.
232 for (const auto &it : varDecl->getConstraints())
233 insertDeclRef(getOrInsertDef(it.constraint), it.referenceLoc);
235 } else if (const auto *expr = dyn_cast<ast::DeclRefExpr>(node)) {
236 insertDeclRef(getOrInsertDef(expr->getDecl()), expr->getLoc());
241 const PDLIndexSymbol *PDLIndex::lookup(SMLoc loc,
242 SMRange *overlappedRange) const {
243 auto it = intervalMap.find(loc.getPointer());
244 if (!it.valid() || loc.getPointer() < it.start())
245 return nullptr;
247 if (overlappedRange) {
248 *overlappedRange = SMRange(SMLoc::getFromPointer(it.start()),
249 SMLoc::getFromPointer(it.stop()));
251 return it.value();
254 //===----------------------------------------------------------------------===//
255 // PDLDocument
256 //===----------------------------------------------------------------------===//
258 namespace {
259 /// This class represents all of the information pertaining to a specific PDL
260 /// document.
261 struct PDLDocument {
262 PDLDocument(const lsp::URIForFile &uri, StringRef contents,
263 const std::vector<std::string> &extraDirs,
264 std::vector<lsp::Diagnostic> &diagnostics);
265 PDLDocument(const PDLDocument &) = delete;
266 PDLDocument &operator=(const PDLDocument &) = delete;
268 //===--------------------------------------------------------------------===//
269 // Definitions and References
270 //===--------------------------------------------------------------------===//
272 void getLocationsOf(const lsp::URIForFile &uri, const lsp::Position &defPos,
273 std::vector<lsp::Location> &locations);
274 void findReferencesOf(const lsp::URIForFile &uri, const lsp::Position &pos,
275 std::vector<lsp::Location> &references);
277 //===--------------------------------------------------------------------===//
278 // Document Links
279 //===--------------------------------------------------------------------===//
281 void getDocumentLinks(const lsp::URIForFile &uri,
282 std::vector<lsp::DocumentLink> &links);
284 //===--------------------------------------------------------------------===//
285 // Hover
286 //===--------------------------------------------------------------------===//
288 std::optional<lsp::Hover> findHover(const lsp::URIForFile &uri,
289 const lsp::Position &hoverPos);
290 std::optional<lsp::Hover> findHover(const ast::Decl *decl,
291 const SMRange &hoverRange);
292 lsp::Hover buildHoverForOpName(const ods::Operation *op,
293 const SMRange &hoverRange);
294 lsp::Hover buildHoverForVariable(const ast::VariableDecl *varDecl,
295 const SMRange &hoverRange);
296 lsp::Hover buildHoverForPattern(const ast::PatternDecl *decl,
297 const SMRange &hoverRange);
298 lsp::Hover buildHoverForCoreConstraint(const ast::CoreConstraintDecl *decl,
299 const SMRange &hoverRange);
300 template <typename T>
301 lsp::Hover buildHoverForUserConstraintOrRewrite(StringRef typeName,
302 const T *decl,
303 const SMRange &hoverRange);
305 //===--------------------------------------------------------------------===//
306 // Document Symbols
307 //===--------------------------------------------------------------------===//
309 void findDocumentSymbols(std::vector<lsp::DocumentSymbol> &symbols);
311 //===--------------------------------------------------------------------===//
312 // Code Completion
313 //===--------------------------------------------------------------------===//
315 lsp::CompletionList getCodeCompletion(const lsp::URIForFile &uri,
316 const lsp::Position &completePos);
318 //===--------------------------------------------------------------------===//
319 // Signature Help
320 //===--------------------------------------------------------------------===//
322 lsp::SignatureHelp getSignatureHelp(const lsp::URIForFile &uri,
323 const lsp::Position &helpPos);
325 //===--------------------------------------------------------------------===//
326 // Inlay Hints
327 //===--------------------------------------------------------------------===//
329 void getInlayHints(const lsp::URIForFile &uri, const lsp::Range &range,
330 std::vector<lsp::InlayHint> &inlayHints);
331 void getInlayHintsFor(const ast::VariableDecl *decl,
332 const lsp::URIForFile &uri,
333 std::vector<lsp::InlayHint> &inlayHints);
334 void getInlayHintsFor(const ast::CallExpr *expr, const lsp::URIForFile &uri,
335 std::vector<lsp::InlayHint> &inlayHints);
336 void getInlayHintsFor(const ast::OperationExpr *expr,
337 const lsp::URIForFile &uri,
338 std::vector<lsp::InlayHint> &inlayHints);
340 /// Add a parameter hint for the given expression using `label`.
341 void addParameterHintFor(std::vector<lsp::InlayHint> &inlayHints,
342 const ast::Expr *expr, StringRef label);
344 //===--------------------------------------------------------------------===//
345 // PDLL ViewOutput
346 //===--------------------------------------------------------------------===//
348 void getPDLLViewOutput(raw_ostream &os, lsp::PDLLViewOutputKind kind);
350 //===--------------------------------------------------------------------===//
351 // Fields
352 //===--------------------------------------------------------------------===//
354 /// The include directories for this file.
355 std::vector<std::string> includeDirs;
357 /// The source manager containing the contents of the input file.
358 llvm::SourceMgr sourceMgr;
360 /// The ODS and AST contexts.
361 ods::Context odsContext;
362 ast::Context astContext;
364 /// The parsed AST module, or failure if the file wasn't valid.
365 FailureOr<ast::Module *> astModule;
367 /// The index of the parsed module.
368 PDLIndex index;
370 /// The set of includes of the parsed module.
371 SmallVector<lsp::SourceMgrInclude> parsedIncludes;
373 } // namespace
375 PDLDocument::PDLDocument(const lsp::URIForFile &uri, StringRef contents,
376 const std::vector<std::string> &extraDirs,
377 std::vector<lsp::Diagnostic> &diagnostics)
378 : astContext(odsContext) {
379 auto memBuffer = llvm::MemoryBuffer::getMemBufferCopy(contents, uri.file());
380 if (!memBuffer) {
381 lsp::Logger::error("Failed to create memory buffer for file", uri.file());
382 return;
385 // Build the set of include directories for this file.
386 llvm::SmallString<32> uriDirectory(uri.file());
387 llvm::sys::path::remove_filename(uriDirectory);
388 includeDirs.push_back(uriDirectory.str().str());
389 includeDirs.insert(includeDirs.end(), extraDirs.begin(), extraDirs.end());
391 sourceMgr.setIncludeDirs(includeDirs);
392 sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
394 astContext.getDiagEngine().setHandlerFn([&](const ast::Diagnostic &diag) {
395 if (auto lspDiag = getLspDiagnoticFromDiag(sourceMgr, diag, uri))
396 diagnostics.push_back(std::move(*lspDiag));
398 astModule = parsePDLLAST(astContext, sourceMgr, /*enableDocumentation=*/true);
400 // Initialize the set of parsed includes.
401 lsp::gatherIncludeFiles(sourceMgr, parsedIncludes);
403 // If we failed to parse the module, there is nothing left to initialize.
404 if (failed(astModule))
405 return;
407 // Prepare the AST index with the parsed module.
408 index.initialize(**astModule, odsContext);
411 //===----------------------------------------------------------------------===//
412 // PDLDocument: Definitions and References
413 //===----------------------------------------------------------------------===//
415 void PDLDocument::getLocationsOf(const lsp::URIForFile &uri,
416 const lsp::Position &defPos,
417 std::vector<lsp::Location> &locations) {
418 SMLoc posLoc = defPos.getAsSMLoc(sourceMgr);
419 const PDLIndexSymbol *symbol = index.lookup(posLoc);
420 if (!symbol)
421 return;
423 locations.push_back(getLocationFromLoc(sourceMgr, symbol->getDefLoc(), uri));
426 void PDLDocument::findReferencesOf(const lsp::URIForFile &uri,
427 const lsp::Position &pos,
428 std::vector<lsp::Location> &references) {
429 SMLoc posLoc = pos.getAsSMLoc(sourceMgr);
430 const PDLIndexSymbol *symbol = index.lookup(posLoc);
431 if (!symbol)
432 return;
434 references.push_back(getLocationFromLoc(sourceMgr, symbol->getDefLoc(), uri));
435 for (SMRange refLoc : symbol->references)
436 references.push_back(getLocationFromLoc(sourceMgr, refLoc, uri));
439 //===--------------------------------------------------------------------===//
440 // PDLDocument: Document Links
441 //===--------------------------------------------------------------------===//
443 void PDLDocument::getDocumentLinks(const lsp::URIForFile &uri,
444 std::vector<lsp::DocumentLink> &links) {
445 for (const lsp::SourceMgrInclude &include : parsedIncludes)
446 links.emplace_back(include.range, include.uri);
449 //===----------------------------------------------------------------------===//
450 // PDLDocument: Hover
451 //===----------------------------------------------------------------------===//
453 std::optional<lsp::Hover>
454 PDLDocument::findHover(const lsp::URIForFile &uri,
455 const lsp::Position &hoverPos) {
456 SMLoc posLoc = hoverPos.getAsSMLoc(sourceMgr);
458 // Check for a reference to an include.
459 for (const lsp::SourceMgrInclude &include : parsedIncludes)
460 if (include.range.contains(hoverPos))
461 return include.buildHover();
463 // Find the symbol at the given location.
464 SMRange hoverRange;
465 const PDLIndexSymbol *symbol = index.lookup(posLoc, &hoverRange);
466 if (!symbol)
467 return std::nullopt;
469 // Add hover for operation names.
470 if (const auto *op =
471 llvm::dyn_cast_if_present<const ods::Operation *>(symbol->definition))
472 return buildHoverForOpName(op, hoverRange);
473 const auto *decl = symbol->definition.get<const ast::Decl *>();
474 return findHover(decl, hoverRange);
477 std::optional<lsp::Hover> PDLDocument::findHover(const ast::Decl *decl,
478 const SMRange &hoverRange) {
479 // Add hover for variables.
480 if (const auto *varDecl = dyn_cast<ast::VariableDecl>(decl))
481 return buildHoverForVariable(varDecl, hoverRange);
483 // Add hover for patterns.
484 if (const auto *patternDecl = dyn_cast<ast::PatternDecl>(decl))
485 return buildHoverForPattern(patternDecl, hoverRange);
487 // Add hover for core constraints.
488 if (const auto *cst = dyn_cast<ast::CoreConstraintDecl>(decl))
489 return buildHoverForCoreConstraint(cst, hoverRange);
491 // Add hover for user constraints.
492 if (const auto *cst = dyn_cast<ast::UserConstraintDecl>(decl))
493 return buildHoverForUserConstraintOrRewrite("Constraint", cst, hoverRange);
495 // Add hover for user rewrites.
496 if (const auto *rewrite = dyn_cast<ast::UserRewriteDecl>(decl))
497 return buildHoverForUserConstraintOrRewrite("Rewrite", rewrite, hoverRange);
499 return std::nullopt;
502 lsp::Hover PDLDocument::buildHoverForOpName(const ods::Operation *op,
503 const SMRange &hoverRange) {
504 lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
506 llvm::raw_string_ostream hoverOS(hover.contents.value);
507 hoverOS << "**OpName**: `" << op->getName() << "`\n***\n"
508 << op->getSummary() << "\n***\n"
509 << op->getDescription();
511 return hover;
514 lsp::Hover PDLDocument::buildHoverForVariable(const ast::VariableDecl *varDecl,
515 const SMRange &hoverRange) {
516 lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
518 llvm::raw_string_ostream hoverOS(hover.contents.value);
519 hoverOS << "**Variable**: `" << varDecl->getName().getName() << "`\n***\n"
520 << "Type: `" << varDecl->getType() << "`\n";
522 return hover;
525 lsp::Hover PDLDocument::buildHoverForPattern(const ast::PatternDecl *decl,
526 const SMRange &hoverRange) {
527 lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
529 llvm::raw_string_ostream hoverOS(hover.contents.value);
530 hoverOS << "**Pattern**";
531 if (const ast::Name *name = decl->getName())
532 hoverOS << ": `" << name->getName() << "`";
533 hoverOS << "\n***\n";
534 if (std::optional<uint16_t> benefit = decl->getBenefit())
535 hoverOS << "Benefit: " << *benefit << "\n";
536 if (decl->hasBoundedRewriteRecursion())
537 hoverOS << "HasBoundedRewriteRecursion\n";
538 hoverOS << "RootOp: `"
539 << decl->getRootRewriteStmt()->getRootOpExpr()->getType() << "`\n";
541 // Format the documentation for the decl.
542 if (std::optional<std::string> doc = getDocumentationFor(sourceMgr, decl))
543 hoverOS << "\n" << *doc << "\n";
545 return hover;
548 lsp::Hover
549 PDLDocument::buildHoverForCoreConstraint(const ast::CoreConstraintDecl *decl,
550 const SMRange &hoverRange) {
551 lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
553 llvm::raw_string_ostream hoverOS(hover.contents.value);
554 hoverOS << "**Constraint**: `";
555 TypeSwitch<const ast::Decl *>(decl)
556 .Case([&](const ast::AttrConstraintDecl *) { hoverOS << "Attr"; })
557 .Case([&](const ast::OpConstraintDecl *opCst) {
558 hoverOS << "Op";
559 if (std::optional<StringRef> name = opCst->getName())
560 hoverOS << "<" << *name << ">";
562 .Case([&](const ast::TypeConstraintDecl *) { hoverOS << "Type"; })
563 .Case([&](const ast::TypeRangeConstraintDecl *) {
564 hoverOS << "TypeRange";
566 .Case([&](const ast::ValueConstraintDecl *) { hoverOS << "Value"; })
567 .Case([&](const ast::ValueRangeConstraintDecl *) {
568 hoverOS << "ValueRange";
570 hoverOS << "`\n";
572 return hover;
575 template <typename T>
576 lsp::Hover PDLDocument::buildHoverForUserConstraintOrRewrite(
577 StringRef typeName, const T *decl, const SMRange &hoverRange) {
578 lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
580 llvm::raw_string_ostream hoverOS(hover.contents.value);
581 hoverOS << "**" << typeName << "**: `" << decl->getName().getName()
582 << "`\n***\n";
583 ArrayRef<ast::VariableDecl *> inputs = decl->getInputs();
584 if (!inputs.empty()) {
585 hoverOS << "Parameters:\n";
586 for (const ast::VariableDecl *input : inputs)
587 hoverOS << "* " << input->getName().getName() << ": `"
588 << input->getType() << "`\n";
589 hoverOS << "***\n";
591 ast::Type resultType = decl->getResultType();
592 if (auto resultTupleTy = dyn_cast<ast::TupleType>(resultType)) {
593 if (!resultTupleTy.empty()) {
594 hoverOS << "Results:\n";
595 for (auto it : llvm::zip(resultTupleTy.getElementNames(),
596 resultTupleTy.getElementTypes())) {
597 StringRef name = std::get<0>(it);
598 hoverOS << "* " << (name.empty() ? "" : (name + ": ")) << "`"
599 << std::get<1>(it) << "`\n";
601 hoverOS << "***\n";
603 } else {
604 hoverOS << "Results:\n* `" << resultType << "`\n";
605 hoverOS << "***\n";
608 // Format the documentation for the decl.
609 if (std::optional<std::string> doc = getDocumentationFor(sourceMgr, decl))
610 hoverOS << "\n" << *doc << "\n";
612 return hover;
615 //===----------------------------------------------------------------------===//
616 // PDLDocument: Document Symbols
617 //===----------------------------------------------------------------------===//
619 void PDLDocument::findDocumentSymbols(
620 std::vector<lsp::DocumentSymbol> &symbols) {
621 if (failed(astModule))
622 return;
624 for (const ast::Decl *decl : (*astModule)->getChildren()) {
625 if (!isMainFileLoc(sourceMgr, decl->getLoc()))
626 continue;
628 if (const auto *patternDecl = dyn_cast<ast::PatternDecl>(decl)) {
629 const ast::Name *name = patternDecl->getName();
631 SMRange nameLoc = name ? name->getLoc() : patternDecl->getLoc();
632 SMRange bodyLoc(nameLoc.Start, patternDecl->getBody()->getLoc().End);
634 symbols.emplace_back(
635 name ? name->getName() : "<pattern>", lsp::SymbolKind::Class,
636 lsp::Range(sourceMgr, bodyLoc), lsp::Range(sourceMgr, nameLoc));
637 } else if (const auto *cDecl = dyn_cast<ast::UserConstraintDecl>(decl)) {
638 // TODO: Add source information for the code block body.
639 SMRange nameLoc = cDecl->getName().getLoc();
640 SMRange bodyLoc = nameLoc;
642 symbols.emplace_back(
643 cDecl->getName().getName(), lsp::SymbolKind::Function,
644 lsp::Range(sourceMgr, bodyLoc), lsp::Range(sourceMgr, nameLoc));
645 } else if (const auto *cDecl = dyn_cast<ast::UserRewriteDecl>(decl)) {
646 // TODO: Add source information for the code block body.
647 SMRange nameLoc = cDecl->getName().getLoc();
648 SMRange bodyLoc = nameLoc;
650 symbols.emplace_back(
651 cDecl->getName().getName(), lsp::SymbolKind::Function,
652 lsp::Range(sourceMgr, bodyLoc), lsp::Range(sourceMgr, nameLoc));
657 //===----------------------------------------------------------------------===//
658 // PDLDocument: Code Completion
659 //===----------------------------------------------------------------------===//
661 namespace {
662 class LSPCodeCompleteContext : public CodeCompleteContext {
663 public:
664 LSPCodeCompleteContext(SMLoc completeLoc, llvm::SourceMgr &sourceMgr,
665 lsp::CompletionList &completionList,
666 ods::Context &odsContext,
667 ArrayRef<std::string> includeDirs)
668 : CodeCompleteContext(completeLoc), sourceMgr(sourceMgr),
669 completionList(completionList), odsContext(odsContext),
670 includeDirs(includeDirs) {}
672 void codeCompleteTupleMemberAccess(ast::TupleType tupleType) final {
673 ArrayRef<ast::Type> elementTypes = tupleType.getElementTypes();
674 ArrayRef<StringRef> elementNames = tupleType.getElementNames();
675 for (unsigned i = 0, e = tupleType.size(); i < e; ++i) {
676 // Push back a completion item that uses the result index.
677 lsp::CompletionItem item;
678 item.label = llvm::formatv("{0} (field #{0})", i).str();
679 item.insertText = Twine(i).str();
680 item.filterText = item.sortText = item.insertText;
681 item.kind = lsp::CompletionItemKind::Field;
682 item.detail = llvm::formatv("{0}: {1}", i, elementTypes[i]);
683 item.insertTextFormat = lsp::InsertTextFormat::PlainText;
684 completionList.items.emplace_back(item);
686 // If the element has a name, push back a completion item with that name.
687 if (!elementNames[i].empty()) {
688 item.label =
689 llvm::formatv("{1} (field #{0})", i, elementNames[i]).str();
690 item.filterText = item.label;
691 item.insertText = elementNames[i].str();
692 completionList.items.emplace_back(item);
697 void codeCompleteOperationMemberAccess(ast::OperationType opType) final {
698 const ods::Operation *odsOp = opType.getODSOperation();
699 if (!odsOp)
700 return;
702 ArrayRef<ods::OperandOrResult> results = odsOp->getResults();
703 for (const auto &it : llvm::enumerate(results)) {
704 const ods::OperandOrResult &result = it.value();
705 const ods::TypeConstraint &constraint = result.getConstraint();
707 // Push back a completion item that uses the result index.
708 lsp::CompletionItem item;
709 item.label = llvm::formatv("{0} (field #{0})", it.index()).str();
710 item.insertText = Twine(it.index()).str();
711 item.filterText = item.sortText = item.insertText;
712 item.kind = lsp::CompletionItemKind::Field;
713 switch (result.getVariableLengthKind()) {
714 case ods::VariableLengthKind::Single:
715 item.detail = llvm::formatv("{0}: Value", it.index()).str();
716 break;
717 case ods::VariableLengthKind::Optional:
718 item.detail = llvm::formatv("{0}: Value?", it.index()).str();
719 break;
720 case ods::VariableLengthKind::Variadic:
721 item.detail = llvm::formatv("{0}: ValueRange", it.index()).str();
722 break;
724 item.documentation = lsp::MarkupContent{
725 lsp::MarkupKind::Markdown,
726 llvm::formatv("{0}\n\n```c++\n{1}\n```\n", constraint.getSummary(),
727 constraint.getCppClass())
728 .str()};
729 item.insertTextFormat = lsp::InsertTextFormat::PlainText;
730 completionList.items.emplace_back(item);
732 // If the result has a name, push back a completion item with the result
733 // name.
734 if (!result.getName().empty()) {
735 item.label =
736 llvm::formatv("{1} (field #{0})", it.index(), result.getName())
737 .str();
738 item.filterText = item.label;
739 item.insertText = result.getName().str();
740 completionList.items.emplace_back(item);
745 void codeCompleteOperationAttributeName(StringRef opName) final {
746 const ods::Operation *odsOp = odsContext.lookupOperation(opName);
747 if (!odsOp)
748 return;
750 for (const ods::Attribute &attr : odsOp->getAttributes()) {
751 const ods::AttributeConstraint &constraint = attr.getConstraint();
753 lsp::CompletionItem item;
754 item.label = attr.getName().str();
755 item.kind = lsp::CompletionItemKind::Field;
756 item.detail = attr.isOptional() ? "optional" : "";
757 item.documentation = lsp::MarkupContent{
758 lsp::MarkupKind::Markdown,
759 llvm::formatv("{0}\n\n```c++\n{1}\n```\n", constraint.getSummary(),
760 constraint.getCppClass())
761 .str()};
762 item.insertTextFormat = lsp::InsertTextFormat::PlainText;
763 completionList.items.emplace_back(item);
767 void codeCompleteConstraintName(ast::Type currentType,
768 bool allowInlineTypeConstraints,
769 const ast::DeclScope *scope) final {
770 auto addCoreConstraint = [&](StringRef constraint, StringRef mlirType,
771 StringRef snippetText = "") {
772 lsp::CompletionItem item;
773 item.label = constraint.str();
774 item.kind = lsp::CompletionItemKind::Class;
775 item.detail = (constraint + " constraint").str();
776 item.documentation = lsp::MarkupContent{
777 lsp::MarkupKind::Markdown,
778 ("A single entity core constraint of type `" + mlirType + "`").str()};
779 item.sortText = "0";
780 item.insertText = snippetText.str();
781 item.insertTextFormat = snippetText.empty()
782 ? lsp::InsertTextFormat::PlainText
783 : lsp::InsertTextFormat::Snippet;
784 completionList.items.emplace_back(item);
787 // Insert completions for the core constraints. Some core constraints have
788 // additional characteristics, so we may add then even if a type has been
789 // inferred.
790 if (!currentType) {
791 addCoreConstraint("Attr", "mlir::Attribute");
792 addCoreConstraint("Op", "mlir::Operation *");
793 addCoreConstraint("Value", "mlir::Value");
794 addCoreConstraint("ValueRange", "mlir::ValueRange");
795 addCoreConstraint("Type", "mlir::Type");
796 addCoreConstraint("TypeRange", "mlir::TypeRange");
798 if (allowInlineTypeConstraints) {
799 /// Attr<Type>.
800 if (!currentType || isa<ast::AttributeType>(currentType))
801 addCoreConstraint("Attr<type>", "mlir::Attribute", "Attr<$1>");
802 /// Value<Type>.
803 if (!currentType || isa<ast::ValueType>(currentType))
804 addCoreConstraint("Value<type>", "mlir::Value", "Value<$1>");
805 /// ValueRange<TypeRange>.
806 if (!currentType || isa<ast::ValueRangeType>(currentType))
807 addCoreConstraint("ValueRange<type>", "mlir::ValueRange",
808 "ValueRange<$1>");
811 // If a scope was provided, check it for potential constraints.
812 while (scope) {
813 for (const ast::Decl *decl : scope->getDecls()) {
814 if (const auto *cst = dyn_cast<ast::UserConstraintDecl>(decl)) {
815 lsp::CompletionItem item;
816 item.label = cst->getName().getName().str();
817 item.kind = lsp::CompletionItemKind::Interface;
818 item.sortText = "2_" + item.label;
820 // Skip constraints that are not single-arg. We currently only
821 // complete variable constraints.
822 if (cst->getInputs().size() != 1)
823 continue;
825 // Ensure the input type matched the given type.
826 ast::Type constraintType = cst->getInputs()[0]->getType();
827 if (currentType && !currentType.refineWith(constraintType))
828 continue;
830 // Format the constraint signature.
832 llvm::raw_string_ostream strOS(item.detail);
833 strOS << "(";
834 llvm::interleaveComma(
835 cst->getInputs(), strOS, [&](const ast::VariableDecl *var) {
836 strOS << var->getName().getName() << ": " << var->getType();
838 strOS << ") -> " << cst->getResultType();
841 // Format the documentation for the constraint.
842 if (std::optional<std::string> doc =
843 getDocumentationFor(sourceMgr, cst)) {
844 item.documentation =
845 lsp::MarkupContent{lsp::MarkupKind::Markdown, std::move(*doc)};
848 completionList.items.emplace_back(item);
852 scope = scope->getParentScope();
856 void codeCompleteDialectName() final {
857 // Code complete known dialects.
858 for (const ods::Dialect &dialect : odsContext.getDialects()) {
859 lsp::CompletionItem item;
860 item.label = dialect.getName().str();
861 item.kind = lsp::CompletionItemKind::Class;
862 item.insertTextFormat = lsp::InsertTextFormat::PlainText;
863 completionList.items.emplace_back(item);
867 void codeCompleteOperationName(StringRef dialectName) final {
868 const ods::Dialect *dialect = odsContext.lookupDialect(dialectName);
869 if (!dialect)
870 return;
872 for (const auto &it : dialect->getOperations()) {
873 const ods::Operation &op = *it.second;
875 lsp::CompletionItem item;
876 item.label = op.getName().drop_front(dialectName.size() + 1).str();
877 item.kind = lsp::CompletionItemKind::Field;
878 item.insertTextFormat = lsp::InsertTextFormat::PlainText;
879 completionList.items.emplace_back(item);
883 void codeCompletePatternMetadata() final {
884 auto addSimpleConstraint = [&](StringRef constraint, StringRef desc,
885 StringRef snippetText = "") {
886 lsp::CompletionItem item;
887 item.label = constraint.str();
888 item.kind = lsp::CompletionItemKind::Class;
889 item.detail = "pattern metadata";
890 item.documentation =
891 lsp::MarkupContent{lsp::MarkupKind::Markdown, desc.str()};
892 item.insertText = snippetText.str();
893 item.insertTextFormat = snippetText.empty()
894 ? lsp::InsertTextFormat::PlainText
895 : lsp::InsertTextFormat::Snippet;
896 completionList.items.emplace_back(item);
899 addSimpleConstraint("benefit", "The `benefit` of matching the pattern.",
900 "benefit($1)");
901 addSimpleConstraint("recursion",
902 "The pattern properly handles recursive application.");
905 void codeCompleteIncludeFilename(StringRef curPath) final {
906 // Normalize the path to allow for interacting with the file system
907 // utilities.
908 SmallString<128> nativeRelDir(llvm::sys::path::convert_to_slash(curPath));
909 llvm::sys::path::native(nativeRelDir);
911 // Set of already included completion paths.
912 StringSet<> seenResults;
914 // Functor used to add a single include completion item.
915 auto addIncludeCompletion = [&](StringRef path, bool isDirectory) {
916 lsp::CompletionItem item;
917 item.label = path.str();
918 item.kind = isDirectory ? lsp::CompletionItemKind::Folder
919 : lsp::CompletionItemKind::File;
920 if (seenResults.insert(item.label).second)
921 completionList.items.emplace_back(item);
924 // Process the include directories for this file, adding any potential
925 // nested include files or directories.
926 for (StringRef includeDir : includeDirs) {
927 llvm::SmallString<128> dir = includeDir;
928 if (!nativeRelDir.empty())
929 llvm::sys::path::append(dir, nativeRelDir);
931 std::error_code errorCode;
932 for (auto it = llvm::sys::fs::directory_iterator(dir, errorCode),
933 e = llvm::sys::fs::directory_iterator();
934 !errorCode && it != e; it.increment(errorCode)) {
935 StringRef filename = llvm::sys::path::filename(it->path());
937 // To know whether a symlink should be treated as file or a directory,
938 // we have to stat it. This should be cheap enough as there shouldn't be
939 // many symlinks.
940 llvm::sys::fs::file_type fileType = it->type();
941 if (fileType == llvm::sys::fs::file_type::symlink_file) {
942 if (auto fileStatus = it->status())
943 fileType = fileStatus->type();
946 switch (fileType) {
947 case llvm::sys::fs::file_type::directory_file:
948 addIncludeCompletion(filename, /*isDirectory=*/true);
949 break;
950 case llvm::sys::fs::file_type::regular_file: {
951 // Only consider concrete files that can actually be included by PDLL.
952 if (filename.ends_with(".pdll") || filename.ends_with(".td"))
953 addIncludeCompletion(filename, /*isDirectory=*/false);
954 break;
956 default:
957 break;
962 // Sort the completion results to make sure the output is deterministic in
963 // the face of different iteration schemes for different platforms.
964 llvm::sort(completionList.items, [](const lsp::CompletionItem &lhs,
965 const lsp::CompletionItem &rhs) {
966 return lhs.label < rhs.label;
970 private:
971 llvm::SourceMgr &sourceMgr;
972 lsp::CompletionList &completionList;
973 ods::Context &odsContext;
974 ArrayRef<std::string> includeDirs;
976 } // namespace
978 lsp::CompletionList
979 PDLDocument::getCodeCompletion(const lsp::URIForFile &uri,
980 const lsp::Position &completePos) {
981 SMLoc posLoc = completePos.getAsSMLoc(sourceMgr);
982 if (!posLoc.isValid())
983 return lsp::CompletionList();
985 // To perform code completion, we run another parse of the module with the
986 // code completion context provided.
987 ods::Context tmpODSContext;
988 lsp::CompletionList completionList;
989 LSPCodeCompleteContext lspCompleteContext(posLoc, sourceMgr, completionList,
990 tmpODSContext,
991 sourceMgr.getIncludeDirs());
993 ast::Context tmpContext(tmpODSContext);
994 (void)parsePDLLAST(tmpContext, sourceMgr, /*enableDocumentation=*/true,
995 &lspCompleteContext);
997 return completionList;
1000 //===----------------------------------------------------------------------===//
1001 // PDLDocument: Signature Help
1002 //===----------------------------------------------------------------------===//
1004 namespace {
1005 class LSPSignatureHelpContext : public CodeCompleteContext {
1006 public:
1007 LSPSignatureHelpContext(SMLoc completeLoc, llvm::SourceMgr &sourceMgr,
1008 lsp::SignatureHelp &signatureHelp,
1009 ods::Context &odsContext)
1010 : CodeCompleteContext(completeLoc), sourceMgr(sourceMgr),
1011 signatureHelp(signatureHelp), odsContext(odsContext) {}
1013 void codeCompleteCallSignature(const ast::CallableDecl *callable,
1014 unsigned currentNumArgs) final {
1015 signatureHelp.activeParameter = currentNumArgs;
1017 lsp::SignatureInformation signatureInfo;
1019 llvm::raw_string_ostream strOS(signatureInfo.label);
1020 strOS << callable->getName()->getName() << "(";
1021 auto formatParamFn = [&](const ast::VariableDecl *var) {
1022 unsigned paramStart = strOS.str().size();
1023 strOS << var->getName().getName() << ": " << var->getType();
1024 unsigned paramEnd = strOS.str().size();
1025 signatureInfo.parameters.emplace_back(lsp::ParameterInformation{
1026 StringRef(strOS.str()).slice(paramStart, paramEnd).str(),
1027 std::make_pair(paramStart, paramEnd), /*paramDoc*/ std::string()});
1029 llvm::interleaveComma(callable->getInputs(), strOS, formatParamFn);
1030 strOS << ") -> " << callable->getResultType();
1033 // Format the documentation for the callable.
1034 if (std::optional<std::string> doc =
1035 getDocumentationFor(sourceMgr, callable))
1036 signatureInfo.documentation = std::move(*doc);
1038 signatureHelp.signatures.emplace_back(std::move(signatureInfo));
1041 void
1042 codeCompleteOperationOperandsSignature(std::optional<StringRef> opName,
1043 unsigned currentNumOperands) final {
1044 const ods::Operation *odsOp =
1045 opName ? odsContext.lookupOperation(*opName) : nullptr;
1046 codeCompleteOperationOperandOrResultSignature(
1047 opName, odsOp, odsOp ? odsOp->getOperands() : std::nullopt,
1048 currentNumOperands, "operand", "Value");
1051 void codeCompleteOperationResultsSignature(std::optional<StringRef> opName,
1052 unsigned currentNumResults) final {
1053 const ods::Operation *odsOp =
1054 opName ? odsContext.lookupOperation(*opName) : nullptr;
1055 codeCompleteOperationOperandOrResultSignature(
1056 opName, odsOp, odsOp ? odsOp->getResults() : std::nullopt,
1057 currentNumResults, "result", "Type");
1060 void codeCompleteOperationOperandOrResultSignature(
1061 std::optional<StringRef> opName, const ods::Operation *odsOp,
1062 ArrayRef<ods::OperandOrResult> values, unsigned currentValue,
1063 StringRef label, StringRef dataType) {
1064 signatureHelp.activeParameter = currentValue;
1066 // If we have ODS information for the operation, add in the ODS signature
1067 // for the operation. We also verify that the current number of values is
1068 // not more than what is defined in ODS, as this will result in an error
1069 // anyways.
1070 if (odsOp && currentValue < values.size()) {
1071 lsp::SignatureInformation signatureInfo;
1073 // Build the signature label.
1075 llvm::raw_string_ostream strOS(signatureInfo.label);
1076 strOS << "(";
1077 auto formatFn = [&](const ods::OperandOrResult &value) {
1078 unsigned paramStart = strOS.str().size();
1080 strOS << value.getName() << ": ";
1082 StringRef constraintDoc = value.getConstraint().getSummary();
1083 std::string paramDoc;
1084 switch (value.getVariableLengthKind()) {
1085 case ods::VariableLengthKind::Single:
1086 strOS << dataType;
1087 paramDoc = constraintDoc.str();
1088 break;
1089 case ods::VariableLengthKind::Optional:
1090 strOS << dataType << "?";
1091 paramDoc = ("optional: " + constraintDoc).str();
1092 break;
1093 case ods::VariableLengthKind::Variadic:
1094 strOS << dataType << "Range";
1095 paramDoc = ("variadic: " + constraintDoc).str();
1096 break;
1099 unsigned paramEnd = strOS.str().size();
1100 signatureInfo.parameters.emplace_back(lsp::ParameterInformation{
1101 StringRef(strOS.str()).slice(paramStart, paramEnd).str(),
1102 std::make_pair(paramStart, paramEnd), paramDoc});
1104 llvm::interleaveComma(values, strOS, formatFn);
1105 strOS << ")";
1107 signatureInfo.documentation =
1108 llvm::formatv("`op<{0}>` ODS {1} specification", *opName, label)
1109 .str();
1110 signatureHelp.signatures.emplace_back(std::move(signatureInfo));
1113 // If there aren't any arguments yet, we also add the generic signature.
1114 if (currentValue == 0 && (!odsOp || !values.empty())) {
1115 lsp::SignatureInformation signatureInfo;
1116 signatureInfo.label =
1117 llvm::formatv("(<{0}s>: {1}Range)", label, dataType).str();
1118 signatureInfo.documentation =
1119 ("Generic operation " + label + " specification").str();
1120 signatureInfo.parameters.emplace_back(lsp::ParameterInformation{
1121 StringRef(signatureInfo.label).drop_front().drop_back().str(),
1122 std::pair<unsigned, unsigned>(1, signatureInfo.label.size() - 1),
1123 ("All of the " + label + "s of the operation.").str()});
1124 signatureHelp.signatures.emplace_back(std::move(signatureInfo));
1128 private:
1129 llvm::SourceMgr &sourceMgr;
1130 lsp::SignatureHelp &signatureHelp;
1131 ods::Context &odsContext;
1133 } // namespace
1135 lsp::SignatureHelp PDLDocument::getSignatureHelp(const lsp::URIForFile &uri,
1136 const lsp::Position &helpPos) {
1137 SMLoc posLoc = helpPos.getAsSMLoc(sourceMgr);
1138 if (!posLoc.isValid())
1139 return lsp::SignatureHelp();
1141 // To perform code completion, we run another parse of the module with the
1142 // code completion context provided.
1143 ods::Context tmpODSContext;
1144 lsp::SignatureHelp signatureHelp;
1145 LSPSignatureHelpContext completeContext(posLoc, sourceMgr, signatureHelp,
1146 tmpODSContext);
1148 ast::Context tmpContext(tmpODSContext);
1149 (void)parsePDLLAST(tmpContext, sourceMgr, /*enableDocumentation=*/true,
1150 &completeContext);
1152 return signatureHelp;
1155 //===----------------------------------------------------------------------===//
1156 // PDLDocument: Inlay Hints
1157 //===----------------------------------------------------------------------===//
1159 /// Returns true if the given name should be added as a hint for `expr`.
1160 static bool shouldAddHintFor(const ast::Expr *expr, StringRef name) {
1161 if (name.empty())
1162 return false;
1164 // If the argument is a reference of the same name, don't add it as a hint.
1165 if (auto *ref = dyn_cast<ast::DeclRefExpr>(expr)) {
1166 const ast::Name *declName = ref->getDecl()->getName();
1167 if (declName && declName->getName() == name)
1168 return false;
1171 return true;
1174 void PDLDocument::getInlayHints(const lsp::URIForFile &uri,
1175 const lsp::Range &range,
1176 std::vector<lsp::InlayHint> &inlayHints) {
1177 if (failed(astModule))
1178 return;
1179 SMRange rangeLoc = range.getAsSMRange(sourceMgr);
1180 if (!rangeLoc.isValid())
1181 return;
1182 (*astModule)->walk([&](const ast::Node *node) {
1183 SMRange loc = node->getLoc();
1185 // Check that the location of this node is within the input range.
1186 if (!lsp::contains(rangeLoc, loc.Start) &&
1187 !lsp::contains(rangeLoc, loc.End))
1188 return;
1190 // Handle hints for various types of nodes.
1191 llvm::TypeSwitch<const ast::Node *>(node)
1192 .Case<ast::VariableDecl, ast::CallExpr, ast::OperationExpr>(
1193 [&](const auto *node) {
1194 this->getInlayHintsFor(node, uri, inlayHints);
1199 void PDLDocument::getInlayHintsFor(const ast::VariableDecl *decl,
1200 const lsp::URIForFile &uri,
1201 std::vector<lsp::InlayHint> &inlayHints) {
1202 // Check to see if the variable has a constraint list, if it does we don't
1203 // provide initializer hints.
1204 if (!decl->getConstraints().empty())
1205 return;
1207 // Check to see if the variable has an initializer.
1208 if (const ast::Expr *expr = decl->getInitExpr()) {
1209 // Don't add hints for operation expression initialized variables given that
1210 // the type of the variable is easily inferred by the expression operation
1211 // name.
1212 if (isa<ast::OperationExpr>(expr))
1213 return;
1216 lsp::InlayHint hint(lsp::InlayHintKind::Type,
1217 lsp::Position(sourceMgr, decl->getLoc().End));
1219 llvm::raw_string_ostream labelOS(hint.label);
1220 labelOS << ": " << decl->getType();
1223 inlayHints.emplace_back(std::move(hint));
1226 void PDLDocument::getInlayHintsFor(const ast::CallExpr *expr,
1227 const lsp::URIForFile &uri,
1228 std::vector<lsp::InlayHint> &inlayHints) {
1229 // Try to extract the callable of this call.
1230 const auto *callableRef = dyn_cast<ast::DeclRefExpr>(expr->getCallableExpr());
1231 const auto *callable =
1232 callableRef ? dyn_cast<ast::CallableDecl>(callableRef->getDecl())
1233 : nullptr;
1234 if (!callable)
1235 return;
1237 // Add hints for the arguments to the call.
1238 for (const auto &it : llvm::zip(expr->getArguments(), callable->getInputs()))
1239 addParameterHintFor(inlayHints, std::get<0>(it),
1240 std::get<1>(it)->getName().getName());
1243 void PDLDocument::getInlayHintsFor(const ast::OperationExpr *expr,
1244 const lsp::URIForFile &uri,
1245 std::vector<lsp::InlayHint> &inlayHints) {
1246 // Check for ODS information.
1247 ast::OperationType opType = dyn_cast<ast::OperationType>(expr->getType());
1248 const auto *odsOp = opType ? opType.getODSOperation() : nullptr;
1250 auto addOpHint = [&](const ast::Expr *valueExpr, StringRef label) {
1251 // If the value expression used the same location as the operation, don't
1252 // add a hint. This expression was materialized during parsing.
1253 if (expr->getLoc().Start == valueExpr->getLoc().Start)
1254 return;
1255 addParameterHintFor(inlayHints, valueExpr, label);
1258 // Functor used to process hints for the operands and results of the
1259 // operation. They effectively have the same format, and thus can be processed
1260 // using the same logic.
1261 auto addOperandOrResultHints = [&](ArrayRef<ast::Expr *> values,
1262 ArrayRef<ods::OperandOrResult> odsValues,
1263 StringRef allValuesName) {
1264 if (values.empty())
1265 return;
1267 // The values should either map to a single range, or be equivalent to the
1268 // ODS values.
1269 if (values.size() != odsValues.size()) {
1270 // Handle the case of a single element that covers the full range.
1271 if (values.size() == 1)
1272 return addOpHint(values.front(), allValuesName);
1273 return;
1276 for (const auto &it : llvm::zip(values, odsValues))
1277 addOpHint(std::get<0>(it), std::get<1>(it).getName());
1280 // Add hints for the operands and results of the operation.
1281 addOperandOrResultHints(expr->getOperands(),
1282 odsOp ? odsOp->getOperands()
1283 : ArrayRef<ods::OperandOrResult>(),
1284 "operands");
1285 addOperandOrResultHints(expr->getResultTypes(),
1286 odsOp ? odsOp->getResults()
1287 : ArrayRef<ods::OperandOrResult>(),
1288 "results");
1291 void PDLDocument::addParameterHintFor(std::vector<lsp::InlayHint> &inlayHints,
1292 const ast::Expr *expr, StringRef label) {
1293 if (!shouldAddHintFor(expr, label))
1294 return;
1296 lsp::InlayHint hint(lsp::InlayHintKind::Parameter,
1297 lsp::Position(sourceMgr, expr->getLoc().Start));
1298 hint.label = (label + ":").str();
1299 hint.paddingRight = true;
1300 inlayHints.emplace_back(std::move(hint));
1303 //===----------------------------------------------------------------------===//
1304 // PDLL ViewOutput
1305 //===----------------------------------------------------------------------===//
1307 void PDLDocument::getPDLLViewOutput(raw_ostream &os,
1308 lsp::PDLLViewOutputKind kind) {
1309 if (failed(astModule))
1310 return;
1311 if (kind == lsp::PDLLViewOutputKind::AST) {
1312 (*astModule)->print(os);
1313 return;
1316 // Generate the MLIR for the ast module. We also capture diagnostics here to
1317 // show to the user, which may be useful if PDLL isn't capturing constraints
1318 // expected by PDL.
1319 MLIRContext mlirContext;
1320 SourceMgrDiagnosticHandler diagHandler(sourceMgr, &mlirContext, os);
1321 OwningOpRef<ModuleOp> pdlModule =
1322 codegenPDLLToMLIR(&mlirContext, astContext, sourceMgr, **astModule);
1323 if (!pdlModule)
1324 return;
1325 if (kind == lsp::PDLLViewOutputKind::MLIR) {
1326 pdlModule->print(os, OpPrintingFlags().enableDebugInfo());
1327 return;
1330 // Otherwise, generate the output for C++.
1331 assert(kind == lsp::PDLLViewOutputKind::CPP &&
1332 "unexpected PDLLViewOutputKind");
1333 codegenPDLLToCPP(**astModule, *pdlModule, os);
1336 //===----------------------------------------------------------------------===//
1337 // PDLTextFileChunk
1338 //===----------------------------------------------------------------------===//
1340 namespace {
1341 /// This class represents a single chunk of an PDL text file.
1342 struct PDLTextFileChunk {
1343 PDLTextFileChunk(uint64_t lineOffset, const lsp::URIForFile &uri,
1344 StringRef contents,
1345 const std::vector<std::string> &extraDirs,
1346 std::vector<lsp::Diagnostic> &diagnostics)
1347 : lineOffset(lineOffset),
1348 document(uri, contents, extraDirs, diagnostics) {}
1350 /// Adjust the line number of the given range to anchor at the beginning of
1351 /// the file, instead of the beginning of this chunk.
1352 void adjustLocForChunkOffset(lsp::Range &range) {
1353 adjustLocForChunkOffset(range.start);
1354 adjustLocForChunkOffset(range.end);
1356 /// Adjust the line number of the given position to anchor at the beginning of
1357 /// the file, instead of the beginning of this chunk.
1358 void adjustLocForChunkOffset(lsp::Position &pos) { pos.line += lineOffset; }
1360 /// The line offset of this chunk from the beginning of the file.
1361 uint64_t lineOffset;
1362 /// The document referred to by this chunk.
1363 PDLDocument document;
1365 } // namespace
1367 //===----------------------------------------------------------------------===//
1368 // PDLTextFile
1369 //===----------------------------------------------------------------------===//
1371 namespace {
1372 /// This class represents a text file containing one or more PDL documents.
1373 class PDLTextFile {
1374 public:
1375 PDLTextFile(const lsp::URIForFile &uri, StringRef fileContents,
1376 int64_t version, const std::vector<std::string> &extraDirs,
1377 std::vector<lsp::Diagnostic> &diagnostics);
1379 /// Return the current version of this text file.
1380 int64_t getVersion() const { return version; }
1382 /// Update the file to the new version using the provided set of content
1383 /// changes. Returns failure if the update was unsuccessful.
1384 LogicalResult update(const lsp::URIForFile &uri, int64_t newVersion,
1385 ArrayRef<lsp::TextDocumentContentChangeEvent> changes,
1386 std::vector<lsp::Diagnostic> &diagnostics);
1388 //===--------------------------------------------------------------------===//
1389 // LSP Queries
1390 //===--------------------------------------------------------------------===//
1392 void getLocationsOf(const lsp::URIForFile &uri, lsp::Position defPos,
1393 std::vector<lsp::Location> &locations);
1394 void findReferencesOf(const lsp::URIForFile &uri, lsp::Position pos,
1395 std::vector<lsp::Location> &references);
1396 void getDocumentLinks(const lsp::URIForFile &uri,
1397 std::vector<lsp::DocumentLink> &links);
1398 std::optional<lsp::Hover> findHover(const lsp::URIForFile &uri,
1399 lsp::Position hoverPos);
1400 void findDocumentSymbols(std::vector<lsp::DocumentSymbol> &symbols);
1401 lsp::CompletionList getCodeCompletion(const lsp::URIForFile &uri,
1402 lsp::Position completePos);
1403 lsp::SignatureHelp getSignatureHelp(const lsp::URIForFile &uri,
1404 lsp::Position helpPos);
1405 void getInlayHints(const lsp::URIForFile &uri, lsp::Range range,
1406 std::vector<lsp::InlayHint> &inlayHints);
1407 lsp::PDLLViewOutputResult getPDLLViewOutput(lsp::PDLLViewOutputKind kind);
1409 private:
1410 using ChunkIterator = llvm::pointee_iterator<
1411 std::vector<std::unique_ptr<PDLTextFileChunk>>::iterator>;
1413 /// Initialize the text file from the given file contents.
1414 void initialize(const lsp::URIForFile &uri, int64_t newVersion,
1415 std::vector<lsp::Diagnostic> &diagnostics);
1417 /// Find the PDL document that contains the given position, and update the
1418 /// position to be anchored at the start of the found chunk instead of the
1419 /// beginning of the file.
1420 ChunkIterator getChunkItFor(lsp::Position &pos);
1421 PDLTextFileChunk &getChunkFor(lsp::Position &pos) {
1422 return *getChunkItFor(pos);
1425 /// The full string contents of the file.
1426 std::string contents;
1428 /// The version of this file.
1429 int64_t version = 0;
1431 /// The number of lines in the file.
1432 int64_t totalNumLines = 0;
1434 /// The chunks of this file. The order of these chunks is the order in which
1435 /// they appear in the text file.
1436 std::vector<std::unique_ptr<PDLTextFileChunk>> chunks;
1438 /// The extra set of include directories for this file.
1439 std::vector<std::string> extraIncludeDirs;
1441 } // namespace
1443 PDLTextFile::PDLTextFile(const lsp::URIForFile &uri, StringRef fileContents,
1444 int64_t version,
1445 const std::vector<std::string> &extraDirs,
1446 std::vector<lsp::Diagnostic> &diagnostics)
1447 : contents(fileContents.str()), extraIncludeDirs(extraDirs) {
1448 initialize(uri, version, diagnostics);
1451 LogicalResult
1452 PDLTextFile::update(const lsp::URIForFile &uri, int64_t newVersion,
1453 ArrayRef<lsp::TextDocumentContentChangeEvent> changes,
1454 std::vector<lsp::Diagnostic> &diagnostics) {
1455 if (failed(lsp::TextDocumentContentChangeEvent::applyTo(changes, contents))) {
1456 lsp::Logger::error("Failed to update contents of {0}", uri.file());
1457 return failure();
1460 // If the file contents were properly changed, reinitialize the text file.
1461 initialize(uri, newVersion, diagnostics);
1462 return success();
1465 void PDLTextFile::getLocationsOf(const lsp::URIForFile &uri,
1466 lsp::Position defPos,
1467 std::vector<lsp::Location> &locations) {
1468 PDLTextFileChunk &chunk = getChunkFor(defPos);
1469 chunk.document.getLocationsOf(uri, defPos, locations);
1471 // Adjust any locations within this file for the offset of this chunk.
1472 if (chunk.lineOffset == 0)
1473 return;
1474 for (lsp::Location &loc : locations)
1475 if (loc.uri == uri)
1476 chunk.adjustLocForChunkOffset(loc.range);
1479 void PDLTextFile::findReferencesOf(const lsp::URIForFile &uri,
1480 lsp::Position pos,
1481 std::vector<lsp::Location> &references) {
1482 PDLTextFileChunk &chunk = getChunkFor(pos);
1483 chunk.document.findReferencesOf(uri, pos, references);
1485 // Adjust any locations within this file for the offset of this chunk.
1486 if (chunk.lineOffset == 0)
1487 return;
1488 for (lsp::Location &loc : references)
1489 if (loc.uri == uri)
1490 chunk.adjustLocForChunkOffset(loc.range);
1493 void PDLTextFile::getDocumentLinks(const lsp::URIForFile &uri,
1494 std::vector<lsp::DocumentLink> &links) {
1495 chunks.front()->document.getDocumentLinks(uri, links);
1496 for (const auto &it : llvm::drop_begin(chunks)) {
1497 size_t currentNumLinks = links.size();
1498 it->document.getDocumentLinks(uri, links);
1500 // Adjust any links within this file to account for the offset of this
1501 // chunk.
1502 for (auto &link : llvm::drop_begin(links, currentNumLinks))
1503 it->adjustLocForChunkOffset(link.range);
1507 std::optional<lsp::Hover> PDLTextFile::findHover(const lsp::URIForFile &uri,
1508 lsp::Position hoverPos) {
1509 PDLTextFileChunk &chunk = getChunkFor(hoverPos);
1510 std::optional<lsp::Hover> hoverInfo = chunk.document.findHover(uri, hoverPos);
1512 // Adjust any locations within this file for the offset of this chunk.
1513 if (chunk.lineOffset != 0 && hoverInfo && hoverInfo->range)
1514 chunk.adjustLocForChunkOffset(*hoverInfo->range);
1515 return hoverInfo;
1518 void PDLTextFile::findDocumentSymbols(
1519 std::vector<lsp::DocumentSymbol> &symbols) {
1520 if (chunks.size() == 1)
1521 return chunks.front()->document.findDocumentSymbols(symbols);
1523 // If there are multiple chunks in this file, we create top-level symbols for
1524 // each chunk.
1525 for (unsigned i = 0, e = chunks.size(); i < e; ++i) {
1526 PDLTextFileChunk &chunk = *chunks[i];
1527 lsp::Position startPos(chunk.lineOffset);
1528 lsp::Position endPos((i == e - 1) ? totalNumLines - 1
1529 : chunks[i + 1]->lineOffset);
1530 lsp::DocumentSymbol symbol("<file-split-" + Twine(i) + ">",
1531 lsp::SymbolKind::Namespace,
1532 /*range=*/lsp::Range(startPos, endPos),
1533 /*selectionRange=*/lsp::Range(startPos));
1534 chunk.document.findDocumentSymbols(symbol.children);
1536 // Fixup the locations of document symbols within this chunk.
1537 if (i != 0) {
1538 SmallVector<lsp::DocumentSymbol *> symbolsToFix;
1539 for (lsp::DocumentSymbol &childSymbol : symbol.children)
1540 symbolsToFix.push_back(&childSymbol);
1542 while (!symbolsToFix.empty()) {
1543 lsp::DocumentSymbol *symbol = symbolsToFix.pop_back_val();
1544 chunk.adjustLocForChunkOffset(symbol->range);
1545 chunk.adjustLocForChunkOffset(symbol->selectionRange);
1547 for (lsp::DocumentSymbol &childSymbol : symbol->children)
1548 symbolsToFix.push_back(&childSymbol);
1552 // Push the symbol for this chunk.
1553 symbols.emplace_back(std::move(symbol));
1557 lsp::CompletionList PDLTextFile::getCodeCompletion(const lsp::URIForFile &uri,
1558 lsp::Position completePos) {
1559 PDLTextFileChunk &chunk = getChunkFor(completePos);
1560 lsp::CompletionList completionList =
1561 chunk.document.getCodeCompletion(uri, completePos);
1563 // Adjust any completion locations.
1564 for (lsp::CompletionItem &item : completionList.items) {
1565 if (item.textEdit)
1566 chunk.adjustLocForChunkOffset(item.textEdit->range);
1567 for (lsp::TextEdit &edit : item.additionalTextEdits)
1568 chunk.adjustLocForChunkOffset(edit.range);
1570 return completionList;
1573 lsp::SignatureHelp PDLTextFile::getSignatureHelp(const lsp::URIForFile &uri,
1574 lsp::Position helpPos) {
1575 return getChunkFor(helpPos).document.getSignatureHelp(uri, helpPos);
1578 void PDLTextFile::getInlayHints(const lsp::URIForFile &uri, lsp::Range range,
1579 std::vector<lsp::InlayHint> &inlayHints) {
1580 auto startIt = getChunkItFor(range.start);
1581 auto endIt = getChunkItFor(range.end);
1583 // Functor used to get the chunks for a given file, and fixup any locations
1584 auto getHintsForChunk = [&](ChunkIterator chunkIt, lsp::Range range) {
1585 size_t currentNumHints = inlayHints.size();
1586 chunkIt->document.getInlayHints(uri, range, inlayHints);
1588 // If this isn't the first chunk, update any positions to account for line
1589 // number differences.
1590 if (&*chunkIt != &*chunks.front()) {
1591 for (auto &hint : llvm::drop_begin(inlayHints, currentNumHints))
1592 chunkIt->adjustLocForChunkOffset(hint.position);
1595 // Returns the number of lines held by a given chunk.
1596 auto getNumLines = [](ChunkIterator chunkIt) {
1597 return (chunkIt + 1)->lineOffset - chunkIt->lineOffset;
1600 // Check if the range is fully within a single chunk.
1601 if (startIt == endIt)
1602 return getHintsForChunk(startIt, range);
1604 // Otherwise, the range is split between multiple chunks. The first chunk
1605 // has the correct range start, but covers the total document.
1606 getHintsForChunk(startIt, lsp::Range(range.start, getNumLines(startIt)));
1608 // Every chunk in between uses the full document.
1609 for (++startIt; startIt != endIt; ++startIt)
1610 getHintsForChunk(startIt, lsp::Range(0, getNumLines(startIt)));
1612 // The range for the last chunk starts at the beginning of the document, up
1613 // through the end of the input range.
1614 getHintsForChunk(startIt, lsp::Range(0, range.end));
1617 lsp::PDLLViewOutputResult
1618 PDLTextFile::getPDLLViewOutput(lsp::PDLLViewOutputKind kind) {
1619 lsp::PDLLViewOutputResult result;
1621 llvm::raw_string_ostream outputOS(result.output);
1622 llvm::interleave(
1623 llvm::make_pointee_range(chunks),
1624 [&](PDLTextFileChunk &chunk) {
1625 chunk.document.getPDLLViewOutput(outputOS, kind);
1627 [&] { outputOS << "\n"
1628 << kDefaultSplitMarker << "\n\n"; });
1630 return result;
1633 void PDLTextFile::initialize(const lsp::URIForFile &uri, int64_t newVersion,
1634 std::vector<lsp::Diagnostic> &diagnostics) {
1635 version = newVersion;
1636 chunks.clear();
1638 // Split the file into separate PDL documents.
1639 SmallVector<StringRef, 8> subContents;
1640 StringRef(contents).split(subContents, kDefaultSplitMarker);
1641 chunks.emplace_back(std::make_unique<PDLTextFileChunk>(
1642 /*lineOffset=*/0, uri, subContents.front(), extraIncludeDirs,
1643 diagnostics));
1645 uint64_t lineOffset = subContents.front().count('\n');
1646 for (StringRef docContents : llvm::drop_begin(subContents)) {
1647 unsigned currentNumDiags = diagnostics.size();
1648 auto chunk = std::make_unique<PDLTextFileChunk>(
1649 lineOffset, uri, docContents, extraIncludeDirs, diagnostics);
1650 lineOffset += docContents.count('\n');
1652 // Adjust locations used in diagnostics to account for the offset from the
1653 // beginning of the file.
1654 for (lsp::Diagnostic &diag :
1655 llvm::drop_begin(diagnostics, currentNumDiags)) {
1656 chunk->adjustLocForChunkOffset(diag.range);
1658 if (!diag.relatedInformation)
1659 continue;
1660 for (auto &it : *diag.relatedInformation)
1661 if (it.location.uri == uri)
1662 chunk->adjustLocForChunkOffset(it.location.range);
1664 chunks.emplace_back(std::move(chunk));
1666 totalNumLines = lineOffset;
1669 PDLTextFile::ChunkIterator PDLTextFile::getChunkItFor(lsp::Position &pos) {
1670 if (chunks.size() == 1)
1671 return chunks.begin();
1673 // Search for the first chunk with a greater line offset, the previous chunk
1674 // is the one that contains `pos`.
1675 auto it = llvm::upper_bound(
1676 chunks, pos, [](const lsp::Position &pos, const auto &chunk) {
1677 return static_cast<uint64_t>(pos.line) < chunk->lineOffset;
1679 ChunkIterator chunkIt(it == chunks.end() ? (chunks.end() - 1) : --it);
1680 pos.line -= chunkIt->lineOffset;
1681 return chunkIt;
1684 //===----------------------------------------------------------------------===//
1685 // PDLLServer::Impl
1686 //===----------------------------------------------------------------------===//
1688 struct lsp::PDLLServer::Impl {
1689 explicit Impl(const Options &options)
1690 : options(options), compilationDatabase(options.compilationDatabases) {}
1692 /// PDLL LSP options.
1693 const Options &options;
1695 /// The compilation database containing additional information for files
1696 /// passed to the server.
1697 lsp::CompilationDatabase compilationDatabase;
1699 /// The files held by the server, mapped by their URI file name.
1700 llvm::StringMap<std::unique_ptr<PDLTextFile>> files;
1703 //===----------------------------------------------------------------------===//
1704 // PDLLServer
1705 //===----------------------------------------------------------------------===//
1707 lsp::PDLLServer::PDLLServer(const Options &options)
1708 : impl(std::make_unique<Impl>(options)) {}
1709 lsp::PDLLServer::~PDLLServer() = default;
1711 void lsp::PDLLServer::addDocument(const URIForFile &uri, StringRef contents,
1712 int64_t version,
1713 std::vector<Diagnostic> &diagnostics) {
1714 // Build the set of additional include directories.
1715 std::vector<std::string> additionalIncludeDirs = impl->options.extraDirs;
1716 const auto &fileInfo = impl->compilationDatabase.getFileInfo(uri.file());
1717 llvm::append_range(additionalIncludeDirs, fileInfo.includeDirs);
1719 impl->files[uri.file()] = std::make_unique<PDLTextFile>(
1720 uri, contents, version, additionalIncludeDirs, diagnostics);
1723 void lsp::PDLLServer::updateDocument(
1724 const URIForFile &uri, ArrayRef<TextDocumentContentChangeEvent> changes,
1725 int64_t version, std::vector<Diagnostic> &diagnostics) {
1726 // Check that we actually have a document for this uri.
1727 auto it = impl->files.find(uri.file());
1728 if (it == impl->files.end())
1729 return;
1731 // Try to update the document. If we fail, erase the file from the server. A
1732 // failed updated generally means we've fallen out of sync somewhere.
1733 if (failed(it->second->update(uri, version, changes, diagnostics)))
1734 impl->files.erase(it);
1737 std::optional<int64_t> lsp::PDLLServer::removeDocument(const URIForFile &uri) {
1738 auto it = impl->files.find(uri.file());
1739 if (it == impl->files.end())
1740 return std::nullopt;
1742 int64_t version = it->second->getVersion();
1743 impl->files.erase(it);
1744 return version;
1747 void lsp::PDLLServer::getLocationsOf(const URIForFile &uri,
1748 const Position &defPos,
1749 std::vector<Location> &locations) {
1750 auto fileIt = impl->files.find(uri.file());
1751 if (fileIt != impl->files.end())
1752 fileIt->second->getLocationsOf(uri, defPos, locations);
1755 void lsp::PDLLServer::findReferencesOf(const URIForFile &uri,
1756 const Position &pos,
1757 std::vector<Location> &references) {
1758 auto fileIt = impl->files.find(uri.file());
1759 if (fileIt != impl->files.end())
1760 fileIt->second->findReferencesOf(uri, pos, references);
1763 void lsp::PDLLServer::getDocumentLinks(
1764 const URIForFile &uri, std::vector<DocumentLink> &documentLinks) {
1765 auto fileIt = impl->files.find(uri.file());
1766 if (fileIt != impl->files.end())
1767 return fileIt->second->getDocumentLinks(uri, documentLinks);
1770 std::optional<lsp::Hover> lsp::PDLLServer::findHover(const URIForFile &uri,
1771 const Position &hoverPos) {
1772 auto fileIt = impl->files.find(uri.file());
1773 if (fileIt != impl->files.end())
1774 return fileIt->second->findHover(uri, hoverPos);
1775 return std::nullopt;
1778 void lsp::PDLLServer::findDocumentSymbols(
1779 const URIForFile &uri, std::vector<DocumentSymbol> &symbols) {
1780 auto fileIt = impl->files.find(uri.file());
1781 if (fileIt != impl->files.end())
1782 fileIt->second->findDocumentSymbols(symbols);
1785 lsp::CompletionList
1786 lsp::PDLLServer::getCodeCompletion(const URIForFile &uri,
1787 const Position &completePos) {
1788 auto fileIt = impl->files.find(uri.file());
1789 if (fileIt != impl->files.end())
1790 return fileIt->second->getCodeCompletion(uri, completePos);
1791 return CompletionList();
1794 lsp::SignatureHelp lsp::PDLLServer::getSignatureHelp(const URIForFile &uri,
1795 const Position &helpPos) {
1796 auto fileIt = impl->files.find(uri.file());
1797 if (fileIt != impl->files.end())
1798 return fileIt->second->getSignatureHelp(uri, helpPos);
1799 return SignatureHelp();
1802 void lsp::PDLLServer::getInlayHints(const URIForFile &uri, const Range &range,
1803 std::vector<InlayHint> &inlayHints) {
1804 auto fileIt = impl->files.find(uri.file());
1805 if (fileIt == impl->files.end())
1806 return;
1807 fileIt->second->getInlayHints(uri, range, inlayHints);
1809 // Drop any duplicated hints that may have cropped up.
1810 llvm::sort(inlayHints);
1811 inlayHints.erase(llvm::unique(inlayHints), inlayHints.end());
1814 std::optional<lsp::PDLLViewOutputResult>
1815 lsp::PDLLServer::getPDLLViewOutput(const URIForFile &uri,
1816 PDLLViewOutputKind kind) {
1817 auto fileIt = impl->files.find(uri.file());
1818 if (fileIt != impl->files.end())
1819 return fileIt->second->getPDLLViewOutput(kind);
1820 return std::nullopt;