MLIR  19.0.0git
PDLLServer.cpp
Go to the documentation of this file.
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 //===----------------------------------------------------------------------===//
8 
9 #include "PDLLServer.h"
10 
11 #include "Protocol.h"
12 #include "mlir/IR/BuiltinOps.h"
27 #include "llvm/ADT/IntervalMap.h"
28 #include "llvm/ADT/StringMap.h"
29 #include "llvm/ADT/StringSet.h"
30 #include "llvm/ADT/TypeSwitch.h"
31 #include "llvm/Support/FileSystem.h"
32 #include "llvm/Support/Path.h"
33 #include <optional>
34 
35 using namespace mlir;
36 using namespace mlir::pdll;
37 
38 /// Returns a language server uri for the given source location. `mainFileURI`
39 /// corresponds to the uri for the main file of the source manager.
40 static lsp::URIForFile getURIFromLoc(llvm::SourceMgr &mgr, SMRange loc,
41  const lsp::URIForFile &mainFileURI) {
42  int bufferId = mgr.FindBufferContainingLoc(loc.Start);
43  if (bufferId == 0 || bufferId == static_cast<int>(mgr.getMainFileID()))
44  return mainFileURI;
46  mgr.getBufferInfo(bufferId).Buffer->getBufferIdentifier());
47  if (fileForLoc)
48  return *fileForLoc;
49  lsp::Logger::error("Failed to create URI for include file: {0}",
50  llvm::toString(fileForLoc.takeError()));
51  return mainFileURI;
52 }
53 
54 /// Returns true if the given location is in the main file of the source
55 /// manager.
56 static bool isMainFileLoc(llvm::SourceMgr &mgr, SMRange loc) {
57  return mgr.FindBufferContainingLoc(loc.Start) == mgr.getMainFileID();
58 }
59 
60 /// Returns a language server location from the given source range.
61 static lsp::Location getLocationFromLoc(llvm::SourceMgr &mgr, SMRange range,
62  const lsp::URIForFile &uri) {
63  return lsp::Location(getURIFromLoc(mgr, range, uri), lsp::Range(mgr, range));
64 }
65 
66 /// Convert the given MLIR diagnostic to the LSP form.
67 static std::optional<lsp::Diagnostic>
68 getLspDiagnoticFromDiag(llvm::SourceMgr &sourceMgr, const ast::Diagnostic &diag,
69  const lsp::URIForFile &uri) {
70  lsp::Diagnostic lspDiag;
71  lspDiag.source = "pdll";
72 
73  // FIXME: Right now all of the diagnostics are treated as parser issues, but
74  // some are parser and some are verifier.
75  lspDiag.category = "Parse Error";
76 
77  // Try to grab a file location for this diagnostic.
78  lsp::Location loc = getLocationFromLoc(sourceMgr, diag.getLocation(), uri);
79  lspDiag.range = loc.range;
80 
81  // Skip diagnostics that weren't emitted within the main file.
82  if (loc.uri != uri)
83  return std::nullopt;
84 
85  // Convert the severity for the diagnostic.
86  switch (diag.getSeverity()) {
87  case ast::Diagnostic::Severity::DK_Note:
88  llvm_unreachable("expected notes to be handled separately");
89  case ast::Diagnostic::Severity::DK_Warning:
91  break;
92  case ast::Diagnostic::Severity::DK_Error:
94  break;
95  case ast::Diagnostic::Severity::DK_Remark:
97  break;
98  }
99  lspDiag.message = diag.getMessage().str();
100 
101  // Attach any notes to the main diagnostic as related information.
102  std::vector<lsp::DiagnosticRelatedInformation> relatedDiags;
103  for (const ast::Diagnostic &note : diag.getNotes()) {
104  relatedDiags.emplace_back(
105  getLocationFromLoc(sourceMgr, note.getLocation(), uri),
106  note.getMessage().str());
107  }
108  if (!relatedDiags.empty())
109  lspDiag.relatedInformation = std::move(relatedDiags);
110 
111  return lspDiag;
112 }
113 
114 /// Get or extract the documentation for the given decl.
115 static std::optional<std::string>
116 getDocumentationFor(llvm::SourceMgr &sourceMgr, const ast::Decl *decl) {
117  // If the decl already had documentation set, use it.
118  if (std::optional<StringRef> doc = decl->getDocComment())
119  return doc->str();
120 
121  // If the decl doesn't yet have documentation, try to extract it from the
122  // source file.
123  return lsp::extractSourceDocComment(sourceMgr, decl->getLoc().Start);
124 }
125 
126 //===----------------------------------------------------------------------===//
127 // PDLIndex
128 //===----------------------------------------------------------------------===//
129 
130 namespace {
131 struct PDLIndexSymbol {
132  explicit PDLIndexSymbol(const ast::Decl *definition)
133  : definition(definition) {}
134  explicit PDLIndexSymbol(const ods::Operation *definition)
135  : definition(definition) {}
136 
137  /// Return the location of the definition of this symbol.
138  SMRange getDefLoc() const {
139  if (const ast::Decl *decl = llvm::dyn_cast_if_present<const ast::Decl *>(definition)) {
140  const ast::Name *declName = decl->getName();
141  return declName ? declName->getLoc() : decl->getLoc();
142  }
143  return definition.get<const ods::Operation *>()->getLoc();
144  }
145 
146  /// The main definition of the symbol.
148  /// The set of references to the symbol.
149  std::vector<SMRange> references;
150 };
151 
152 /// This class provides an index for definitions/uses within a PDL document.
153 /// It provides efficient lookup of a definition given an input source range.
154 class PDLIndex {
155 public:
156  PDLIndex() : intervalMap(allocator) {}
157 
158  /// Initialize the index with the given ast::Module.
159  void initialize(const ast::Module &module, const ods::Context &odsContext);
160 
161  /// Lookup a symbol for the given location. Returns nullptr if no symbol could
162  /// be found. If provided, `overlappedRange` is set to the range that the
163  /// provided `loc` overlapped with.
164  const PDLIndexSymbol *lookup(SMLoc loc,
165  SMRange *overlappedRange = nullptr) const;
166 
167 private:
168  /// The type of interval map used to store source references. SMRange is
169  /// half-open, so we also need to use a half-open interval map.
170  using MapT =
171  llvm::IntervalMap<const char *, const PDLIndexSymbol *,
172  llvm::IntervalMapImpl::NodeSizer<
173  const char *, const PDLIndexSymbol *>::LeafSize,
174  llvm::IntervalMapHalfOpenInfo<const char *>>;
175 
176  /// An allocator for the interval map.
177  MapT::Allocator allocator;
178 
179  /// An interval map containing a corresponding definition mapped to a source
180  /// interval.
181  MapT intervalMap;
182 
183  /// A mapping between definitions and their corresponding symbol.
185 };
186 } // namespace
187 
188 void PDLIndex::initialize(const ast::Module &module,
189  const ods::Context &odsContext) {
190  auto getOrInsertDef = [&](const auto *def) -> PDLIndexSymbol * {
191  auto it = defToSymbol.try_emplace(def, nullptr);
192  if (it.second)
193  it.first->second = std::make_unique<PDLIndexSymbol>(def);
194  return &*it.first->second;
195  };
196  auto insertDeclRef = [&](PDLIndexSymbol *sym, SMRange refLoc,
197  bool isDef = false) {
198  const char *startLoc = refLoc.Start.getPointer();
199  const char *endLoc = refLoc.End.getPointer();
200  if (!intervalMap.overlaps(startLoc, endLoc)) {
201  intervalMap.insert(startLoc, endLoc, sym);
202  if (!isDef)
203  sym->references.push_back(refLoc);
204  }
205  };
206  auto insertODSOpRef = [&](StringRef opName, SMRange refLoc) {
207  const ods::Operation *odsOp = odsContext.lookupOperation(opName);
208  if (!odsOp)
209  return;
210 
211  PDLIndexSymbol *symbol = getOrInsertDef(odsOp);
212  insertDeclRef(symbol, odsOp->getLoc(), /*isDef=*/true);
213  insertDeclRef(symbol, refLoc);
214  };
215 
216  module.walk([&](const ast::Node *node) {
217  // Handle references to PDL decls.
218  if (const auto *decl = dyn_cast<ast::OpNameDecl>(node)) {
219  if (std::optional<StringRef> name = decl->getName())
220  insertODSOpRef(*name, decl->getLoc());
221  } else if (const ast::Decl *decl = dyn_cast<ast::Decl>(node)) {
222  const ast::Name *name = decl->getName();
223  if (!name)
224  return;
225  PDLIndexSymbol *declSym = getOrInsertDef(decl);
226  insertDeclRef(declSym, name->getLoc(), /*isDef=*/true);
227 
228  if (const auto *varDecl = dyn_cast<ast::VariableDecl>(decl)) {
229  // Record references to any constraints.
230  for (const auto &it : varDecl->getConstraints())
231  insertDeclRef(getOrInsertDef(it.constraint), it.referenceLoc);
232  }
233  } else if (const auto *expr = dyn_cast<ast::DeclRefExpr>(node)) {
234  insertDeclRef(getOrInsertDef(expr->getDecl()), expr->getLoc());
235  }
236  });
237 }
238 
239 const PDLIndexSymbol *PDLIndex::lookup(SMLoc loc,
240  SMRange *overlappedRange) const {
241  auto it = intervalMap.find(loc.getPointer());
242  if (!it.valid() || loc.getPointer() < it.start())
243  return nullptr;
244 
245  if (overlappedRange) {
246  *overlappedRange = SMRange(SMLoc::getFromPointer(it.start()),
247  SMLoc::getFromPointer(it.stop()));
248  }
249  return it.value();
250 }
251 
252 //===----------------------------------------------------------------------===//
253 // PDLDocument
254 //===----------------------------------------------------------------------===//
255 
256 namespace {
257 /// This class represents all of the information pertaining to a specific PDL
258 /// document.
259 struct PDLDocument {
260  PDLDocument(const lsp::URIForFile &uri, StringRef contents,
261  const std::vector<std::string> &extraDirs,
262  std::vector<lsp::Diagnostic> &diagnostics);
263  PDLDocument(const PDLDocument &) = delete;
264  PDLDocument &operator=(const PDLDocument &) = delete;
265 
266  //===--------------------------------------------------------------------===//
267  // Definitions and References
268  //===--------------------------------------------------------------------===//
269 
270  void getLocationsOf(const lsp::URIForFile &uri, const lsp::Position &defPos,
271  std::vector<lsp::Location> &locations);
272  void findReferencesOf(const lsp::URIForFile &uri, const lsp::Position &pos,
273  std::vector<lsp::Location> &references);
274 
275  //===--------------------------------------------------------------------===//
276  // Document Links
277  //===--------------------------------------------------------------------===//
278 
279  void getDocumentLinks(const lsp::URIForFile &uri,
280  std::vector<lsp::DocumentLink> &links);
281 
282  //===--------------------------------------------------------------------===//
283  // Hover
284  //===--------------------------------------------------------------------===//
285 
286  std::optional<lsp::Hover> findHover(const lsp::URIForFile &uri,
287  const lsp::Position &hoverPos);
288  std::optional<lsp::Hover> findHover(const ast::Decl *decl,
289  const SMRange &hoverRange);
290  lsp::Hover buildHoverForOpName(const ods::Operation *op,
291  const SMRange &hoverRange);
292  lsp::Hover buildHoverForVariable(const ast::VariableDecl *varDecl,
293  const SMRange &hoverRange);
294  lsp::Hover buildHoverForPattern(const ast::PatternDecl *decl,
295  const SMRange &hoverRange);
296  lsp::Hover buildHoverForCoreConstraint(const ast::CoreConstraintDecl *decl,
297  const SMRange &hoverRange);
298  template <typename T>
299  lsp::Hover buildHoverForUserConstraintOrRewrite(StringRef typeName,
300  const T *decl,
301  const SMRange &hoverRange);
302 
303  //===--------------------------------------------------------------------===//
304  // Document Symbols
305  //===--------------------------------------------------------------------===//
306 
307  void findDocumentSymbols(std::vector<lsp::DocumentSymbol> &symbols);
308 
309  //===--------------------------------------------------------------------===//
310  // Code Completion
311  //===--------------------------------------------------------------------===//
312 
313  lsp::CompletionList getCodeCompletion(const lsp::URIForFile &uri,
314  const lsp::Position &completePos);
315 
316  //===--------------------------------------------------------------------===//
317  // Signature Help
318  //===--------------------------------------------------------------------===//
319 
320  lsp::SignatureHelp getSignatureHelp(const lsp::URIForFile &uri,
321  const lsp::Position &helpPos);
322 
323  //===--------------------------------------------------------------------===//
324  // Inlay Hints
325  //===--------------------------------------------------------------------===//
326 
327  void getInlayHints(const lsp::URIForFile &uri, const lsp::Range &range,
328  std::vector<lsp::InlayHint> &inlayHints);
329  void getInlayHintsFor(const ast::VariableDecl *decl,
330  const lsp::URIForFile &uri,
331  std::vector<lsp::InlayHint> &inlayHints);
332  void getInlayHintsFor(const ast::CallExpr *expr, const lsp::URIForFile &uri,
333  std::vector<lsp::InlayHint> &inlayHints);
334  void getInlayHintsFor(const ast::OperationExpr *expr,
335  const lsp::URIForFile &uri,
336  std::vector<lsp::InlayHint> &inlayHints);
337 
338  /// Add a parameter hint for the given expression using `label`.
339  void addParameterHintFor(std::vector<lsp::InlayHint> &inlayHints,
340  const ast::Expr *expr, StringRef label);
341 
342  //===--------------------------------------------------------------------===//
343  // PDLL ViewOutput
344  //===--------------------------------------------------------------------===//
345 
346  void getPDLLViewOutput(raw_ostream &os, lsp::PDLLViewOutputKind kind);
347 
348  //===--------------------------------------------------------------------===//
349  // Fields
350  //===--------------------------------------------------------------------===//
351 
352  /// The include directories for this file.
353  std::vector<std::string> includeDirs;
354 
355  /// The source manager containing the contents of the input file.
356  llvm::SourceMgr sourceMgr;
357 
358  /// The ODS and AST contexts.
359  ods::Context odsContext;
360  ast::Context astContext;
361 
362  /// The parsed AST module, or failure if the file wasn't valid.
363  FailureOr<ast::Module *> astModule;
364 
365  /// The index of the parsed module.
366  PDLIndex index;
367 
368  /// The set of includes of the parsed module.
369  SmallVector<lsp::SourceMgrInclude> parsedIncludes;
370 };
371 } // namespace
372 
373 PDLDocument::PDLDocument(const lsp::URIForFile &uri, StringRef contents,
374  const std::vector<std::string> &extraDirs,
375  std::vector<lsp::Diagnostic> &diagnostics)
376  : astContext(odsContext) {
377  auto memBuffer = llvm::MemoryBuffer::getMemBufferCopy(contents, uri.file());
378  if (!memBuffer) {
379  lsp::Logger::error("Failed to create memory buffer for file", uri.file());
380  return;
381  }
382 
383  // Build the set of include directories for this file.
384  llvm::SmallString<32> uriDirectory(uri.file());
385  llvm::sys::path::remove_filename(uriDirectory);
386  includeDirs.push_back(uriDirectory.str().str());
387  includeDirs.insert(includeDirs.end(), extraDirs.begin(), extraDirs.end());
388 
389  sourceMgr.setIncludeDirs(includeDirs);
390  sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
391 
392  astContext.getDiagEngine().setHandlerFn([&](const ast::Diagnostic &diag) {
393  if (auto lspDiag = getLspDiagnoticFromDiag(sourceMgr, diag, uri))
394  diagnostics.push_back(std::move(*lspDiag));
395  });
396  astModule = parsePDLLAST(astContext, sourceMgr, /*enableDocumentation=*/true);
397 
398  // Initialize the set of parsed includes.
399  lsp::gatherIncludeFiles(sourceMgr, parsedIncludes);
400 
401  // If we failed to parse the module, there is nothing left to initialize.
402  if (failed(astModule))
403  return;
404 
405  // Prepare the AST index with the parsed module.
406  index.initialize(**astModule, odsContext);
407 }
408 
409 //===----------------------------------------------------------------------===//
410 // PDLDocument: Definitions and References
411 //===----------------------------------------------------------------------===//
412 
413 void PDLDocument::getLocationsOf(const lsp::URIForFile &uri,
414  const lsp::Position &defPos,
415  std::vector<lsp::Location> &locations) {
416  SMLoc posLoc = defPos.getAsSMLoc(sourceMgr);
417  const PDLIndexSymbol *symbol = index.lookup(posLoc);
418  if (!symbol)
419  return;
420 
421  locations.push_back(getLocationFromLoc(sourceMgr, symbol->getDefLoc(), uri));
422 }
423 
424 void PDLDocument::findReferencesOf(const lsp::URIForFile &uri,
425  const lsp::Position &pos,
426  std::vector<lsp::Location> &references) {
427  SMLoc posLoc = pos.getAsSMLoc(sourceMgr);
428  const PDLIndexSymbol *symbol = index.lookup(posLoc);
429  if (!symbol)
430  return;
431 
432  references.push_back(getLocationFromLoc(sourceMgr, symbol->getDefLoc(), uri));
433  for (SMRange refLoc : symbol->references)
434  references.push_back(getLocationFromLoc(sourceMgr, refLoc, uri));
435 }
436 
437 //===--------------------------------------------------------------------===//
438 // PDLDocument: Document Links
439 //===--------------------------------------------------------------------===//
440 
441 void PDLDocument::getDocumentLinks(const lsp::URIForFile &uri,
442  std::vector<lsp::DocumentLink> &links) {
443  for (const lsp::SourceMgrInclude &include : parsedIncludes)
444  links.emplace_back(include.range, include.uri);
445 }
446 
447 //===----------------------------------------------------------------------===//
448 // PDLDocument: Hover
449 //===----------------------------------------------------------------------===//
450 
451 std::optional<lsp::Hover>
452 PDLDocument::findHover(const lsp::URIForFile &uri,
453  const lsp::Position &hoverPos) {
454  SMLoc posLoc = hoverPos.getAsSMLoc(sourceMgr);
455 
456  // Check for a reference to an include.
457  for (const lsp::SourceMgrInclude &include : parsedIncludes)
458  if (include.range.contains(hoverPos))
459  return include.buildHover();
460 
461  // Find the symbol at the given location.
462  SMRange hoverRange;
463  const PDLIndexSymbol *symbol = index.lookup(posLoc, &hoverRange);
464  if (!symbol)
465  return std::nullopt;
466 
467  // Add hover for operation names.
468  if (const auto *op = llvm::dyn_cast_if_present<const ods::Operation *>(symbol->definition))
469  return buildHoverForOpName(op, hoverRange);
470  const auto *decl = symbol->definition.get<const ast::Decl *>();
471  return findHover(decl, hoverRange);
472 }
473 
474 std::optional<lsp::Hover> PDLDocument::findHover(const ast::Decl *decl,
475  const SMRange &hoverRange) {
476  // Add hover for variables.
477  if (const auto *varDecl = dyn_cast<ast::VariableDecl>(decl))
478  return buildHoverForVariable(varDecl, hoverRange);
479 
480  // Add hover for patterns.
481  if (const auto *patternDecl = dyn_cast<ast::PatternDecl>(decl))
482  return buildHoverForPattern(patternDecl, hoverRange);
483 
484  // Add hover for core constraints.
485  if (const auto *cst = dyn_cast<ast::CoreConstraintDecl>(decl))
486  return buildHoverForCoreConstraint(cst, hoverRange);
487 
488  // Add hover for user constraints.
489  if (const auto *cst = dyn_cast<ast::UserConstraintDecl>(decl))
490  return buildHoverForUserConstraintOrRewrite("Constraint", cst, hoverRange);
491 
492  // Add hover for user rewrites.
493  if (const auto *rewrite = dyn_cast<ast::UserRewriteDecl>(decl))
494  return buildHoverForUserConstraintOrRewrite("Rewrite", rewrite, hoverRange);
495 
496  return std::nullopt;
497 }
498 
499 lsp::Hover PDLDocument::buildHoverForOpName(const ods::Operation *op,
500  const SMRange &hoverRange) {
501  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
502  {
503  llvm::raw_string_ostream hoverOS(hover.contents.value);
504  hoverOS << "**OpName**: `" << op->getName() << "`\n***\n"
505  << op->getSummary() << "\n***\n"
506  << op->getDescription();
507  }
508  return hover;
509 }
510 
511 lsp::Hover PDLDocument::buildHoverForVariable(const ast::VariableDecl *varDecl,
512  const SMRange &hoverRange) {
513  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
514  {
515  llvm::raw_string_ostream hoverOS(hover.contents.value);
516  hoverOS << "**Variable**: `" << varDecl->getName().getName() << "`\n***\n"
517  << "Type: `" << varDecl->getType() << "`\n";
518  }
519  return hover;
520 }
521 
522 lsp::Hover PDLDocument::buildHoverForPattern(const ast::PatternDecl *decl,
523  const SMRange &hoverRange) {
524  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
525  {
526  llvm::raw_string_ostream hoverOS(hover.contents.value);
527  hoverOS << "**Pattern**";
528  if (const ast::Name *name = decl->getName())
529  hoverOS << ": `" << name->getName() << "`";
530  hoverOS << "\n***\n";
531  if (std::optional<uint16_t> benefit = decl->getBenefit())
532  hoverOS << "Benefit: " << *benefit << "\n";
533  if (decl->hasBoundedRewriteRecursion())
534  hoverOS << "HasBoundedRewriteRecursion\n";
535  hoverOS << "RootOp: `"
536  << decl->getRootRewriteStmt()->getRootOpExpr()->getType() << "`\n";
537 
538  // Format the documentation for the decl.
539  if (std::optional<std::string> doc = getDocumentationFor(sourceMgr, decl))
540  hoverOS << "\n" << *doc << "\n";
541  }
542  return hover;
543 }
544 
546 PDLDocument::buildHoverForCoreConstraint(const ast::CoreConstraintDecl *decl,
547  const SMRange &hoverRange) {
548  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
549  {
550  llvm::raw_string_ostream hoverOS(hover.contents.value);
551  hoverOS << "**Constraint**: `";
553  .Case([&](const ast::AttrConstraintDecl *) { hoverOS << "Attr"; })
554  .Case([&](const ast::OpConstraintDecl *opCst) {
555  hoverOS << "Op";
556  if (std::optional<StringRef> name = opCst->getName())
557  hoverOS << "<" << *name << ">";
558  })
559  .Case([&](const ast::TypeConstraintDecl *) { hoverOS << "Type"; })
560  .Case([&](const ast::TypeRangeConstraintDecl *) {
561  hoverOS << "TypeRange";
562  })
563  .Case([&](const ast::ValueConstraintDecl *) { hoverOS << "Value"; })
564  .Case([&](const ast::ValueRangeConstraintDecl *) {
565  hoverOS << "ValueRange";
566  });
567  hoverOS << "`\n";
568  }
569  return hover;
570 }
571 
572 template <typename T>
573 lsp::Hover PDLDocument::buildHoverForUserConstraintOrRewrite(
574  StringRef typeName, const T *decl, const SMRange &hoverRange) {
575  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
576  {
577  llvm::raw_string_ostream hoverOS(hover.contents.value);
578  hoverOS << "**" << typeName << "**: `" << decl->getName().getName()
579  << "`\n***\n";
580  ArrayRef<ast::VariableDecl *> inputs = decl->getInputs();
581  if (!inputs.empty()) {
582  hoverOS << "Parameters:\n";
583  for (const ast::VariableDecl *input : inputs)
584  hoverOS << "* " << input->getName().getName() << ": `"
585  << input->getType() << "`\n";
586  hoverOS << "***\n";
587  }
588  ast::Type resultType = decl->getResultType();
589  if (auto resultTupleTy = resultType.dyn_cast<ast::TupleType>()) {
590  if (!resultTupleTy.empty()) {
591  hoverOS << "Results:\n";
592  for (auto it : llvm::zip(resultTupleTy.getElementNames(),
593  resultTupleTy.getElementTypes())) {
594  StringRef name = std::get<0>(it);
595  hoverOS << "* " << (name.empty() ? "" : (name + ": ")) << "`"
596  << std::get<1>(it) << "`\n";
597  }
598  hoverOS << "***\n";
599  }
600  } else {
601  hoverOS << "Results:\n* `" << resultType << "`\n";
602  hoverOS << "***\n";
603  }
604 
605  // Format the documentation for the decl.
606  if (std::optional<std::string> doc = getDocumentationFor(sourceMgr, decl))
607  hoverOS << "\n" << *doc << "\n";
608  }
609  return hover;
610 }
611 
612 //===----------------------------------------------------------------------===//
613 // PDLDocument: Document Symbols
614 //===----------------------------------------------------------------------===//
615 
616 void PDLDocument::findDocumentSymbols(
617  std::vector<lsp::DocumentSymbol> &symbols) {
618  if (failed(astModule))
619  return;
620 
621  for (const ast::Decl *decl : (*astModule)->getChildren()) {
622  if (!isMainFileLoc(sourceMgr, decl->getLoc()))
623  continue;
624 
625  if (const auto *patternDecl = dyn_cast<ast::PatternDecl>(decl)) {
626  const ast::Name *name = patternDecl->getName();
627 
628  SMRange nameLoc = name ? name->getLoc() : patternDecl->getLoc();
629  SMRange bodyLoc(nameLoc.Start, patternDecl->getBody()->getLoc().End);
630 
631  symbols.emplace_back(
632  name ? name->getName() : "<pattern>", lsp::SymbolKind::Class,
633  lsp::Range(sourceMgr, bodyLoc), lsp::Range(sourceMgr, nameLoc));
634  } else if (const auto *cDecl = dyn_cast<ast::UserConstraintDecl>(decl)) {
635  // TODO: Add source information for the code block body.
636  SMRange nameLoc = cDecl->getName().getLoc();
637  SMRange bodyLoc = nameLoc;
638 
639  symbols.emplace_back(
640  cDecl->getName().getName(), lsp::SymbolKind::Function,
641  lsp::Range(sourceMgr, bodyLoc), lsp::Range(sourceMgr, nameLoc));
642  } else if (const auto *cDecl = dyn_cast<ast::UserRewriteDecl>(decl)) {
643  // TODO: Add source information for the code block body.
644  SMRange nameLoc = cDecl->getName().getLoc();
645  SMRange bodyLoc = nameLoc;
646 
647  symbols.emplace_back(
648  cDecl->getName().getName(), lsp::SymbolKind::Function,
649  lsp::Range(sourceMgr, bodyLoc), lsp::Range(sourceMgr, nameLoc));
650  }
651  }
652 }
653 
654 //===----------------------------------------------------------------------===//
655 // PDLDocument: Code Completion
656 //===----------------------------------------------------------------------===//
657 
658 namespace {
659 class LSPCodeCompleteContext : public CodeCompleteContext {
660 public:
661  LSPCodeCompleteContext(SMLoc completeLoc, llvm::SourceMgr &sourceMgr,
662  lsp::CompletionList &completionList,
663  ods::Context &odsContext,
664  ArrayRef<std::string> includeDirs)
665  : CodeCompleteContext(completeLoc), sourceMgr(sourceMgr),
666  completionList(completionList), odsContext(odsContext),
667  includeDirs(includeDirs) {}
668 
669  void codeCompleteTupleMemberAccess(ast::TupleType tupleType) final {
670  ArrayRef<ast::Type> elementTypes = tupleType.getElementTypes();
671  ArrayRef<StringRef> elementNames = tupleType.getElementNames();
672  for (unsigned i = 0, e = tupleType.size(); i < e; ++i) {
673  // Push back a completion item that uses the result index.
674  lsp::CompletionItem item;
675  item.label = llvm::formatv("{0} (field #{0})", i).str();
676  item.insertText = Twine(i).str();
677  item.filterText = item.sortText = item.insertText;
679  item.detail = llvm::formatv("{0}: {1}", i, elementTypes[i]);
681  completionList.items.emplace_back(item);
682 
683  // If the element has a name, push back a completion item with that name.
684  if (!elementNames[i].empty()) {
685  item.label =
686  llvm::formatv("{1} (field #{0})", i, elementNames[i]).str();
687  item.filterText = item.label;
688  item.insertText = elementNames[i].str();
689  completionList.items.emplace_back(item);
690  }
691  }
692  }
693 
695  const ods::Operation *odsOp = opType.getODSOperation();
696  if (!odsOp)
697  return;
698 
699  ArrayRef<ods::OperandOrResult> results = odsOp->getResults();
700  for (const auto &it : llvm::enumerate(results)) {
701  const ods::OperandOrResult &result = it.value();
702  const ods::TypeConstraint &constraint = result.getConstraint();
703 
704  // Push back a completion item that uses the result index.
705  lsp::CompletionItem item;
706  item.label = llvm::formatv("{0} (field #{0})", it.index()).str();
707  item.insertText = Twine(it.index()).str();
708  item.filterText = item.sortText = item.insertText;
710  switch (result.getVariableLengthKind()) {
712  item.detail = llvm::formatv("{0}: Value", it.index()).str();
713  break;
715  item.detail = llvm::formatv("{0}: Value?", it.index()).str();
716  break;
718  item.detail = llvm::formatv("{0}: ValueRange", it.index()).str();
719  break;
720  }
723  llvm::formatv("{0}\n\n```c++\n{1}\n```\n", constraint.getSummary(),
724  constraint.getCppClass())
725  .str()};
727  completionList.items.emplace_back(item);
728 
729  // If the result has a name, push back a completion item with the result
730  // name.
731  if (!result.getName().empty()) {
732  item.label =
733  llvm::formatv("{1} (field #{0})", it.index(), result.getName())
734  .str();
735  item.filterText = item.label;
736  item.insertText = result.getName().str();
737  completionList.items.emplace_back(item);
738  }
739  }
740  }
741 
742  void codeCompleteOperationAttributeName(StringRef opName) final {
743  const ods::Operation *odsOp = odsContext.lookupOperation(opName);
744  if (!odsOp)
745  return;
746 
747  for (const ods::Attribute &attr : odsOp->getAttributes()) {
748  const ods::AttributeConstraint &constraint = attr.getConstraint();
749 
750  lsp::CompletionItem item;
751  item.label = attr.getName().str();
753  item.detail = attr.isOptional() ? "optional" : "";
756  llvm::formatv("{0}\n\n```c++\n{1}\n```\n", constraint.getSummary(),
757  constraint.getCppClass())
758  .str()};
760  completionList.items.emplace_back(item);
761  }
762  }
763 
764  void codeCompleteConstraintName(ast::Type currentType,
765  bool allowInlineTypeConstraints,
766  const ast::DeclScope *scope) final {
767  auto addCoreConstraint = [&](StringRef constraint, StringRef mlirType,
768  StringRef snippetText = "") {
769  lsp::CompletionItem item;
770  item.label = constraint.str();
772  item.detail = (constraint + " constraint").str();
775  ("A single entity core constraint of type `" + mlirType + "`").str()};
776  item.sortText = "0";
777  item.insertText = snippetText.str();
778  item.insertTextFormat = snippetText.empty()
781  completionList.items.emplace_back(item);
782  };
783 
784  // Insert completions for the core constraints. Some core constraints have
785  // additional characteristics, so we may add then even if a type has been
786  // inferred.
787  if (!currentType) {
788  addCoreConstraint("Attr", "mlir::Attribute");
789  addCoreConstraint("Op", "mlir::Operation *");
790  addCoreConstraint("Value", "mlir::Value");
791  addCoreConstraint("ValueRange", "mlir::ValueRange");
792  addCoreConstraint("Type", "mlir::Type");
793  addCoreConstraint("TypeRange", "mlir::TypeRange");
794  }
795  if (allowInlineTypeConstraints) {
796  /// Attr<Type>.
797  if (!currentType || currentType.isa<ast::AttributeType>())
798  addCoreConstraint("Attr<type>", "mlir::Attribute", "Attr<$1>");
799  /// Value<Type>.
800  if (!currentType || currentType.isa<ast::ValueType>())
801  addCoreConstraint("Value<type>", "mlir::Value", "Value<$1>");
802  /// ValueRange<TypeRange>.
803  if (!currentType || currentType.isa<ast::ValueRangeType>())
804  addCoreConstraint("ValueRange<type>", "mlir::ValueRange",
805  "ValueRange<$1>");
806  }
807 
808  // If a scope was provided, check it for potential constraints.
809  while (scope) {
810  for (const ast::Decl *decl : scope->getDecls()) {
811  if (const auto *cst = dyn_cast<ast::UserConstraintDecl>(decl)) {
812  lsp::CompletionItem item;
813  item.label = cst->getName().getName().str();
815  item.sortText = "2_" + item.label;
816 
817  // Skip constraints that are not single-arg. We currently only
818  // complete variable constraints.
819  if (cst->getInputs().size() != 1)
820  continue;
821 
822  // Ensure the input type matched the given type.
823  ast::Type constraintType = cst->getInputs()[0]->getType();
824  if (currentType && !currentType.refineWith(constraintType))
825  continue;
826 
827  // Format the constraint signature.
828  {
829  llvm::raw_string_ostream strOS(item.detail);
830  strOS << "(";
831  llvm::interleaveComma(
832  cst->getInputs(), strOS, [&](const ast::VariableDecl *var) {
833  strOS << var->getName().getName() << ": " << var->getType();
834  });
835  strOS << ") -> " << cst->getResultType();
836  }
837 
838  // Format the documentation for the constraint.
839  if (std::optional<std::string> doc =
840  getDocumentationFor(sourceMgr, cst)) {
841  item.documentation =
843  }
844 
845  completionList.items.emplace_back(item);
846  }
847  }
848 
849  scope = scope->getParentScope();
850  }
851  }
852 
853  void codeCompleteDialectName() final {
854  // Code complete known dialects.
855  for (const ods::Dialect &dialect : odsContext.getDialects()) {
856  lsp::CompletionItem item;
857  item.label = dialect.getName().str();
860  completionList.items.emplace_back(item);
861  }
862  }
863 
864  void codeCompleteOperationName(StringRef dialectName) final {
865  const ods::Dialect *dialect = odsContext.lookupDialect(dialectName);
866  if (!dialect)
867  return;
868 
869  for (const auto &it : dialect->getOperations()) {
870  const ods::Operation &op = *it.second;
871 
872  lsp::CompletionItem item;
873  item.label = op.getName().drop_front(dialectName.size() + 1).str();
876  completionList.items.emplace_back(item);
877  }
878  }
879 
880  void codeCompletePatternMetadata() final {
881  auto addSimpleConstraint = [&](StringRef constraint, StringRef desc,
882  StringRef snippetText = "") {
883  lsp::CompletionItem item;
884  item.label = constraint.str();
886  item.detail = "pattern metadata";
887  item.documentation =
889  item.insertText = snippetText.str();
890  item.insertTextFormat = snippetText.empty()
893  completionList.items.emplace_back(item);
894  };
895 
896  addSimpleConstraint("benefit", "The `benefit` of matching the pattern.",
897  "benefit($1)");
898  addSimpleConstraint("recursion",
899  "The pattern properly handles recursive application.");
900  }
901 
902  void codeCompleteIncludeFilename(StringRef curPath) final {
903  // Normalize the path to allow for interacting with the file system
904  // utilities.
905  SmallString<128> nativeRelDir(llvm::sys::path::convert_to_slash(curPath));
906  llvm::sys::path::native(nativeRelDir);
907 
908  // Set of already included completion paths.
909  StringSet<> seenResults;
910 
911  // Functor used to add a single include completion item.
912  auto addIncludeCompletion = [&](StringRef path, bool isDirectory) {
913  lsp::CompletionItem item;
914  item.label = path.str();
915  item.kind = isDirectory ? lsp::CompletionItemKind::Folder
917  if (seenResults.insert(item.label).second)
918  completionList.items.emplace_back(item);
919  };
920 
921  // Process the include directories for this file, adding any potential
922  // nested include files or directories.
923  for (StringRef includeDir : includeDirs) {
924  llvm::SmallString<128> dir = includeDir;
925  if (!nativeRelDir.empty())
926  llvm::sys::path::append(dir, nativeRelDir);
927 
928  std::error_code errorCode;
929  for (auto it = llvm::sys::fs::directory_iterator(dir, errorCode),
930  e = llvm::sys::fs::directory_iterator();
931  !errorCode && it != e; it.increment(errorCode)) {
932  StringRef filename = llvm::sys::path::filename(it->path());
933 
934  // To know whether a symlink should be treated as file or a directory,
935  // we have to stat it. This should be cheap enough as there shouldn't be
936  // many symlinks.
937  llvm::sys::fs::file_type fileType = it->type();
938  if (fileType == llvm::sys::fs::file_type::symlink_file) {
939  if (auto fileStatus = it->status())
940  fileType = fileStatus->type();
941  }
942 
943  switch (fileType) {
944  case llvm::sys::fs::file_type::directory_file:
945  addIncludeCompletion(filename, /*isDirectory=*/true);
946  break;
947  case llvm::sys::fs::file_type::regular_file: {
948  // Only consider concrete files that can actually be included by PDLL.
949  if (filename.ends_with(".pdll") || filename.ends_with(".td"))
950  addIncludeCompletion(filename, /*isDirectory=*/false);
951  break;
952  }
953  default:
954  break;
955  }
956  }
957  }
958 
959  // Sort the completion results to make sure the output is deterministic in
960  // the face of different iteration schemes for different platforms.
961  llvm::sort(completionList.items, [](const lsp::CompletionItem &lhs,
962  const lsp::CompletionItem &rhs) {
963  return lhs.label < rhs.label;
964  });
965  }
966 
967 private:
968  llvm::SourceMgr &sourceMgr;
969  lsp::CompletionList &completionList;
970  ods::Context &odsContext;
971  ArrayRef<std::string> includeDirs;
972 };
973 } // namespace
974 
976 PDLDocument::getCodeCompletion(const lsp::URIForFile &uri,
977  const lsp::Position &completePos) {
978  SMLoc posLoc = completePos.getAsSMLoc(sourceMgr);
979  if (!posLoc.isValid())
980  return lsp::CompletionList();
981 
982  // To perform code completion, we run another parse of the module with the
983  // code completion context provided.
984  ods::Context tmpODSContext;
985  lsp::CompletionList completionList;
986  LSPCodeCompleteContext lspCompleteContext(posLoc, sourceMgr, completionList,
987  tmpODSContext,
988  sourceMgr.getIncludeDirs());
989 
990  ast::Context tmpContext(tmpODSContext);
991  (void)parsePDLLAST(tmpContext, sourceMgr, /*enableDocumentation=*/true,
992  &lspCompleteContext);
993 
994  return completionList;
995 }
996 
997 //===----------------------------------------------------------------------===//
998 // PDLDocument: Signature Help
999 //===----------------------------------------------------------------------===//
1000 
1001 namespace {
1002 class LSPSignatureHelpContext : public CodeCompleteContext {
1003 public:
1004  LSPSignatureHelpContext(SMLoc completeLoc, llvm::SourceMgr &sourceMgr,
1005  lsp::SignatureHelp &signatureHelp,
1006  ods::Context &odsContext)
1007  : CodeCompleteContext(completeLoc), sourceMgr(sourceMgr),
1008  signatureHelp(signatureHelp), odsContext(odsContext) {}
1009 
1010  void codeCompleteCallSignature(const ast::CallableDecl *callable,
1011  unsigned currentNumArgs) final {
1012  signatureHelp.activeParameter = currentNumArgs;
1013 
1014  lsp::SignatureInformation signatureInfo;
1015  {
1016  llvm::raw_string_ostream strOS(signatureInfo.label);
1017  strOS << callable->getName()->getName() << "(";
1018  auto formatParamFn = [&](const ast::VariableDecl *var) {
1019  unsigned paramStart = strOS.str().size();
1020  strOS << var->getName().getName() << ": " << var->getType();
1021  unsigned paramEnd = strOS.str().size();
1022  signatureInfo.parameters.emplace_back(lsp::ParameterInformation{
1023  StringRef(strOS.str()).slice(paramStart, paramEnd).str(),
1024  std::make_pair(paramStart, paramEnd), /*paramDoc*/ std::string()});
1025  };
1026  llvm::interleaveComma(callable->getInputs(), strOS, formatParamFn);
1027  strOS << ") -> " << callable->getResultType();
1028  }
1029 
1030  // Format the documentation for the callable.
1031  if (std::optional<std::string> doc =
1032  getDocumentationFor(sourceMgr, callable))
1033  signatureInfo.documentation = std::move(*doc);
1034 
1035  signatureHelp.signatures.emplace_back(std::move(signatureInfo));
1036  }
1037 
1038  void
1039  codeCompleteOperationOperandsSignature(std::optional<StringRef> opName,
1040  unsigned currentNumOperands) final {
1041  const ods::Operation *odsOp =
1042  opName ? odsContext.lookupOperation(*opName) : nullptr;
1043  codeCompleteOperationOperandOrResultSignature(
1044  opName, odsOp, odsOp ? odsOp->getOperands() : std::nullopt,
1045  currentNumOperands, "operand", "Value");
1046  }
1047 
1048  void codeCompleteOperationResultsSignature(std::optional<StringRef> opName,
1049  unsigned currentNumResults) final {
1050  const ods::Operation *odsOp =
1051  opName ? odsContext.lookupOperation(*opName) : nullptr;
1052  codeCompleteOperationOperandOrResultSignature(
1053  opName, odsOp, odsOp ? odsOp->getResults() : std::nullopt,
1054  currentNumResults, "result", "Type");
1055  }
1056 
1057  void codeCompleteOperationOperandOrResultSignature(
1058  std::optional<StringRef> opName, const ods::Operation *odsOp,
1059  ArrayRef<ods::OperandOrResult> values, unsigned currentValue,
1060  StringRef label, StringRef dataType) {
1061  signatureHelp.activeParameter = currentValue;
1062 
1063  // If we have ODS information for the operation, add in the ODS signature
1064  // for the operation. We also verify that the current number of values is
1065  // not more than what is defined in ODS, as this will result in an error
1066  // anyways.
1067  if (odsOp && currentValue < values.size()) {
1068  lsp::SignatureInformation signatureInfo;
1069 
1070  // Build the signature label.
1071  {
1072  llvm::raw_string_ostream strOS(signatureInfo.label);
1073  strOS << "(";
1074  auto formatFn = [&](const ods::OperandOrResult &value) {
1075  unsigned paramStart = strOS.str().size();
1076 
1077  strOS << value.getName() << ": ";
1078 
1079  StringRef constraintDoc = value.getConstraint().getSummary();
1080  std::string paramDoc;
1081  switch (value.getVariableLengthKind()) {
1083  strOS << dataType;
1084  paramDoc = constraintDoc.str();
1085  break;
1087  strOS << dataType << "?";
1088  paramDoc = ("optional: " + constraintDoc).str();
1089  break;
1091  strOS << dataType << "Range";
1092  paramDoc = ("variadic: " + constraintDoc).str();
1093  break;
1094  }
1095 
1096  unsigned paramEnd = strOS.str().size();
1097  signatureInfo.parameters.emplace_back(lsp::ParameterInformation{
1098  StringRef(strOS.str()).slice(paramStart, paramEnd).str(),
1099  std::make_pair(paramStart, paramEnd), paramDoc});
1100  };
1101  llvm::interleaveComma(values, strOS, formatFn);
1102  strOS << ")";
1103  }
1104  signatureInfo.documentation =
1105  llvm::formatv("`op<{0}>` ODS {1} specification", *opName, label)
1106  .str();
1107  signatureHelp.signatures.emplace_back(std::move(signatureInfo));
1108  }
1109 
1110  // If there aren't any arguments yet, we also add the generic signature.
1111  if (currentValue == 0 && (!odsOp || !values.empty())) {
1112  lsp::SignatureInformation signatureInfo;
1113  signatureInfo.label =
1114  llvm::formatv("(<{0}s>: {1}Range)", label, dataType).str();
1115  signatureInfo.documentation =
1116  ("Generic operation " + label + " specification").str();
1117  signatureInfo.parameters.emplace_back(lsp::ParameterInformation{
1118  StringRef(signatureInfo.label).drop_front().drop_back().str(),
1119  std::pair<unsigned, unsigned>(1, signatureInfo.label.size() - 1),
1120  ("All of the " + label + "s of the operation.").str()});
1121  signatureHelp.signatures.emplace_back(std::move(signatureInfo));
1122  }
1123  }
1124 
1125 private:
1126  llvm::SourceMgr &sourceMgr;
1127  lsp::SignatureHelp &signatureHelp;
1128  ods::Context &odsContext;
1129 };
1130 } // namespace
1131 
1132 lsp::SignatureHelp PDLDocument::getSignatureHelp(const lsp::URIForFile &uri,
1133  const lsp::Position &helpPos) {
1134  SMLoc posLoc = helpPos.getAsSMLoc(sourceMgr);
1135  if (!posLoc.isValid())
1136  return lsp::SignatureHelp();
1137 
1138  // To perform code completion, we run another parse of the module with the
1139  // code completion context provided.
1140  ods::Context tmpODSContext;
1141  lsp::SignatureHelp signatureHelp;
1142  LSPSignatureHelpContext completeContext(posLoc, sourceMgr, signatureHelp,
1143  tmpODSContext);
1144 
1145  ast::Context tmpContext(tmpODSContext);
1146  (void)parsePDLLAST(tmpContext, sourceMgr, /*enableDocumentation=*/true,
1147  &completeContext);
1148 
1149  return signatureHelp;
1150 }
1151 
1152 //===----------------------------------------------------------------------===//
1153 // PDLDocument: Inlay Hints
1154 //===----------------------------------------------------------------------===//
1155 
1156 /// Returns true if the given name should be added as a hint for `expr`.
1157 static bool shouldAddHintFor(const ast::Expr *expr, StringRef name) {
1158  if (name.empty())
1159  return false;
1160 
1161  // If the argument is a reference of the same name, don't add it as a hint.
1162  if (auto *ref = dyn_cast<ast::DeclRefExpr>(expr)) {
1163  const ast::Name *declName = ref->getDecl()->getName();
1164  if (declName && declName->getName() == name)
1165  return false;
1166  }
1167 
1168  return true;
1169 }
1170 
1171 void PDLDocument::getInlayHints(const lsp::URIForFile &uri,
1172  const lsp::Range &range,
1173  std::vector<lsp::InlayHint> &inlayHints) {
1174  if (failed(astModule))
1175  return;
1176  SMRange rangeLoc = range.getAsSMRange(sourceMgr);
1177  if (!rangeLoc.isValid())
1178  return;
1179  (*astModule)->walk([&](const ast::Node *node) {
1180  SMRange loc = node->getLoc();
1181 
1182  // Check that the location of this node is within the input range.
1183  if (!lsp::contains(rangeLoc, loc.Start) &&
1184  !lsp::contains(rangeLoc, loc.End))
1185  return;
1186 
1187  // Handle hints for various types of nodes.
1190  [&](const auto *node) {
1191  this->getInlayHintsFor(node, uri, inlayHints);
1192  });
1193  });
1194 }
1195 
1196 void PDLDocument::getInlayHintsFor(const ast::VariableDecl *decl,
1197  const lsp::URIForFile &uri,
1198  std::vector<lsp::InlayHint> &inlayHints) {
1199  // Check to see if the variable has a constraint list, if it does we don't
1200  // provide initializer hints.
1201  if (!decl->getConstraints().empty())
1202  return;
1203 
1204  // Check to see if the variable has an initializer.
1205  if (const ast::Expr *expr = decl->getInitExpr()) {
1206  // Don't add hints for operation expression initialized variables given that
1207  // the type of the variable is easily inferred by the expression operation
1208  // name.
1209  if (isa<ast::OperationExpr>(expr))
1210  return;
1211  }
1212 
1213  lsp::InlayHint hint(lsp::InlayHintKind::Type,
1214  lsp::Position(sourceMgr, decl->getLoc().End));
1215  {
1216  llvm::raw_string_ostream labelOS(hint.label);
1217  labelOS << ": " << decl->getType();
1218  }
1219 
1220  inlayHints.emplace_back(std::move(hint));
1221 }
1222 
1223 void PDLDocument::getInlayHintsFor(const ast::CallExpr *expr,
1224  const lsp::URIForFile &uri,
1225  std::vector<lsp::InlayHint> &inlayHints) {
1226  // Try to extract the callable of this call.
1227  const auto *callableRef = dyn_cast<ast::DeclRefExpr>(expr->getCallableExpr());
1228  const auto *callable =
1229  callableRef ? dyn_cast<ast::CallableDecl>(callableRef->getDecl())
1230  : nullptr;
1231  if (!callable)
1232  return;
1233 
1234  // Add hints for the arguments to the call.
1235  for (const auto &it : llvm::zip(expr->getArguments(), callable->getInputs()))
1236  addParameterHintFor(inlayHints, std::get<0>(it),
1237  std::get<1>(it)->getName().getName());
1238 }
1239 
1240 void PDLDocument::getInlayHintsFor(const ast::OperationExpr *expr,
1241  const lsp::URIForFile &uri,
1242  std::vector<lsp::InlayHint> &inlayHints) {
1243  // Check for ODS information.
1244  ast::OperationType opType = expr->getType().dyn_cast<ast::OperationType>();
1245  const auto *odsOp = opType ? opType.getODSOperation() : nullptr;
1246 
1247  auto addOpHint = [&](const ast::Expr *valueExpr, StringRef label) {
1248  // If the value expression used the same location as the operation, don't
1249  // add a hint. This expression was materialized during parsing.
1250  if (expr->getLoc().Start == valueExpr->getLoc().Start)
1251  return;
1252  addParameterHintFor(inlayHints, valueExpr, label);
1253  };
1254 
1255  // Functor used to process hints for the operands and results of the
1256  // operation. They effectively have the same format, and thus can be processed
1257  // using the same logic.
1258  auto addOperandOrResultHints = [&](ArrayRef<ast::Expr *> values,
1260  StringRef allValuesName) {
1261  if (values.empty())
1262  return;
1263 
1264  // The values should either map to a single range, or be equivalent to the
1265  // ODS values.
1266  if (values.size() != odsValues.size()) {
1267  // Handle the case of a single element that covers the full range.
1268  if (values.size() == 1)
1269  return addOpHint(values.front(), allValuesName);
1270  return;
1271  }
1272 
1273  for (const auto &it : llvm::zip(values, odsValues))
1274  addOpHint(std::get<0>(it), std::get<1>(it).getName());
1275  };
1276 
1277  // Add hints for the operands and results of the operation.
1278  addOperandOrResultHints(expr->getOperands(),
1279  odsOp ? odsOp->getOperands()
1281  "operands");
1282  addOperandOrResultHints(expr->getResultTypes(),
1283  odsOp ? odsOp->getResults()
1285  "results");
1286 }
1287 
1288 void PDLDocument::addParameterHintFor(std::vector<lsp::InlayHint> &inlayHints,
1289  const ast::Expr *expr, StringRef label) {
1290  if (!shouldAddHintFor(expr, label))
1291  return;
1292 
1293  lsp::InlayHint hint(lsp::InlayHintKind::Parameter,
1294  lsp::Position(sourceMgr, expr->getLoc().Start));
1295  hint.label = (label + ":").str();
1296  hint.paddingRight = true;
1297  inlayHints.emplace_back(std::move(hint));
1298 }
1299 
1300 //===----------------------------------------------------------------------===//
1301 // PDLL ViewOutput
1302 //===----------------------------------------------------------------------===//
1303 
1304 void PDLDocument::getPDLLViewOutput(raw_ostream &os,
1305  lsp::PDLLViewOutputKind kind) {
1306  if (failed(astModule))
1307  return;
1308  if (kind == lsp::PDLLViewOutputKind::AST) {
1309  (*astModule)->print(os);
1310  return;
1311  }
1312 
1313  // Generate the MLIR for the ast module. We also capture diagnostics here to
1314  // show to the user, which may be useful if PDLL isn't capturing constraints
1315  // expected by PDL.
1316  MLIRContext mlirContext;
1317  SourceMgrDiagnosticHandler diagHandler(sourceMgr, &mlirContext, os);
1318  OwningOpRef<ModuleOp> pdlModule =
1319  codegenPDLLToMLIR(&mlirContext, astContext, sourceMgr, **astModule);
1320  if (!pdlModule)
1321  return;
1322  if (kind == lsp::PDLLViewOutputKind::MLIR) {
1323  pdlModule->print(os, OpPrintingFlags().enableDebugInfo());
1324  return;
1325  }
1326 
1327  // Otherwise, generate the output for C++.
1328  assert(kind == lsp::PDLLViewOutputKind::CPP &&
1329  "unexpected PDLLViewOutputKind");
1330  codegenPDLLToCPP(**astModule, *pdlModule, os);
1331 }
1332 
1333 //===----------------------------------------------------------------------===//
1334 // PDLTextFileChunk
1335 //===----------------------------------------------------------------------===//
1336 
1337 namespace {
1338 /// This class represents a single chunk of an PDL text file.
1339 struct PDLTextFileChunk {
1340  PDLTextFileChunk(uint64_t lineOffset, const lsp::URIForFile &uri,
1341  StringRef contents,
1342  const std::vector<std::string> &extraDirs,
1343  std::vector<lsp::Diagnostic> &diagnostics)
1344  : lineOffset(lineOffset),
1345  document(uri, contents, extraDirs, diagnostics) {}
1346 
1347  /// Adjust the line number of the given range to anchor at the beginning of
1348  /// the file, instead of the beginning of this chunk.
1349  void adjustLocForChunkOffset(lsp::Range &range) {
1350  adjustLocForChunkOffset(range.start);
1351  adjustLocForChunkOffset(range.end);
1352  }
1353  /// Adjust the line number of the given position to anchor at the beginning of
1354  /// the file, instead of the beginning of this chunk.
1355  void adjustLocForChunkOffset(lsp::Position &pos) { pos.line += lineOffset; }
1356 
1357  /// The line offset of this chunk from the beginning of the file.
1358  uint64_t lineOffset;
1359  /// The document referred to by this chunk.
1360  PDLDocument document;
1361 };
1362 } // namespace
1363 
1364 //===----------------------------------------------------------------------===//
1365 // PDLTextFile
1366 //===----------------------------------------------------------------------===//
1367 
1368 namespace {
1369 /// This class represents a text file containing one or more PDL documents.
1370 class PDLTextFile {
1371 public:
1372  PDLTextFile(const lsp::URIForFile &uri, StringRef fileContents,
1373  int64_t version, const std::vector<std::string> &extraDirs,
1374  std::vector<lsp::Diagnostic> &diagnostics);
1375 
1376  /// Return the current version of this text file.
1377  int64_t getVersion() const { return version; }
1378 
1379  /// Update the file to the new version using the provided set of content
1380  /// changes. Returns failure if the update was unsuccessful.
1381  LogicalResult update(const lsp::URIForFile &uri, int64_t newVersion,
1383  std::vector<lsp::Diagnostic> &diagnostics);
1384 
1385  //===--------------------------------------------------------------------===//
1386  // LSP Queries
1387  //===--------------------------------------------------------------------===//
1388 
1389  void getLocationsOf(const lsp::URIForFile &uri, lsp::Position defPos,
1390  std::vector<lsp::Location> &locations);
1391  void findReferencesOf(const lsp::URIForFile &uri, lsp::Position pos,
1392  std::vector<lsp::Location> &references);
1393  void getDocumentLinks(const lsp::URIForFile &uri,
1394  std::vector<lsp::DocumentLink> &links);
1395  std::optional<lsp::Hover> findHover(const lsp::URIForFile &uri,
1396  lsp::Position hoverPos);
1397  void findDocumentSymbols(std::vector<lsp::DocumentSymbol> &symbols);
1398  lsp::CompletionList getCodeCompletion(const lsp::URIForFile &uri,
1399  lsp::Position completePos);
1400  lsp::SignatureHelp getSignatureHelp(const lsp::URIForFile &uri,
1401  lsp::Position helpPos);
1402  void getInlayHints(const lsp::URIForFile &uri, lsp::Range range,
1403  std::vector<lsp::InlayHint> &inlayHints);
1404  lsp::PDLLViewOutputResult getPDLLViewOutput(lsp::PDLLViewOutputKind kind);
1405 
1406 private:
1407  using ChunkIterator = llvm::pointee_iterator<
1408  std::vector<std::unique_ptr<PDLTextFileChunk>>::iterator>;
1409 
1410  /// Initialize the text file from the given file contents.
1411  void initialize(const lsp::URIForFile &uri, int64_t newVersion,
1412  std::vector<lsp::Diagnostic> &diagnostics);
1413 
1414  /// Find the PDL document that contains the given position, and update the
1415  /// position to be anchored at the start of the found chunk instead of the
1416  /// beginning of the file.
1417  ChunkIterator getChunkItFor(lsp::Position &pos);
1418  PDLTextFileChunk &getChunkFor(lsp::Position &pos) {
1419  return *getChunkItFor(pos);
1420  }
1421 
1422  /// The full string contents of the file.
1423  std::string contents;
1424 
1425  /// The version of this file.
1426  int64_t version = 0;
1427 
1428  /// The number of lines in the file.
1429  int64_t totalNumLines = 0;
1430 
1431  /// The chunks of this file. The order of these chunks is the order in which
1432  /// they appear in the text file.
1433  std::vector<std::unique_ptr<PDLTextFileChunk>> chunks;
1434 
1435  /// The extra set of include directories for this file.
1436  std::vector<std::string> extraIncludeDirs;
1437 };
1438 } // namespace
1439 
1440 PDLTextFile::PDLTextFile(const lsp::URIForFile &uri, StringRef fileContents,
1441  int64_t version,
1442  const std::vector<std::string> &extraDirs,
1443  std::vector<lsp::Diagnostic> &diagnostics)
1444  : contents(fileContents.str()), extraIncludeDirs(extraDirs) {
1445  initialize(uri, version, diagnostics);
1446 }
1447 
1449 PDLTextFile::update(const lsp::URIForFile &uri, int64_t newVersion,
1451  std::vector<lsp::Diagnostic> &diagnostics) {
1452  if (failed(lsp::TextDocumentContentChangeEvent::applyTo(changes, contents))) {
1453  lsp::Logger::error("Failed to update contents of {0}", uri.file());
1454  return failure();
1455  }
1456 
1457  // If the file contents were properly changed, reinitialize the text file.
1458  initialize(uri, newVersion, diagnostics);
1459  return success();
1460 }
1461 
1462 void PDLTextFile::getLocationsOf(const lsp::URIForFile &uri,
1463  lsp::Position defPos,
1464  std::vector<lsp::Location> &locations) {
1465  PDLTextFileChunk &chunk = getChunkFor(defPos);
1466  chunk.document.getLocationsOf(uri, defPos, locations);
1467 
1468  // Adjust any locations within this file for the offset of this chunk.
1469  if (chunk.lineOffset == 0)
1470  return;
1471  for (lsp::Location &loc : locations)
1472  if (loc.uri == uri)
1473  chunk.adjustLocForChunkOffset(loc.range);
1474 }
1475 
1476 void PDLTextFile::findReferencesOf(const lsp::URIForFile &uri,
1477  lsp::Position pos,
1478  std::vector<lsp::Location> &references) {
1479  PDLTextFileChunk &chunk = getChunkFor(pos);
1480  chunk.document.findReferencesOf(uri, pos, references);
1481 
1482  // Adjust any locations within this file for the offset of this chunk.
1483  if (chunk.lineOffset == 0)
1484  return;
1485  for (lsp::Location &loc : references)
1486  if (loc.uri == uri)
1487  chunk.adjustLocForChunkOffset(loc.range);
1488 }
1489 
1490 void PDLTextFile::getDocumentLinks(const lsp::URIForFile &uri,
1491  std::vector<lsp::DocumentLink> &links) {
1492  chunks.front()->document.getDocumentLinks(uri, links);
1493  for (const auto &it : llvm::drop_begin(chunks)) {
1494  size_t currentNumLinks = links.size();
1495  it->document.getDocumentLinks(uri, links);
1496 
1497  // Adjust any links within this file to account for the offset of this
1498  // chunk.
1499  for (auto &link : llvm::drop_begin(links, currentNumLinks))
1500  it->adjustLocForChunkOffset(link.range);
1501  }
1502 }
1503 
1504 std::optional<lsp::Hover> PDLTextFile::findHover(const lsp::URIForFile &uri,
1505  lsp::Position hoverPos) {
1506  PDLTextFileChunk &chunk = getChunkFor(hoverPos);
1507  std::optional<lsp::Hover> hoverInfo = chunk.document.findHover(uri, hoverPos);
1508 
1509  // Adjust any locations within this file for the offset of this chunk.
1510  if (chunk.lineOffset != 0 && hoverInfo && hoverInfo->range)
1511  chunk.adjustLocForChunkOffset(*hoverInfo->range);
1512  return hoverInfo;
1513 }
1514 
1515 void PDLTextFile::findDocumentSymbols(
1516  std::vector<lsp::DocumentSymbol> &symbols) {
1517  if (chunks.size() == 1)
1518  return chunks.front()->document.findDocumentSymbols(symbols);
1519 
1520  // If there are multiple chunks in this file, we create top-level symbols for
1521  // each chunk.
1522  for (unsigned i = 0, e = chunks.size(); i < e; ++i) {
1523  PDLTextFileChunk &chunk = *chunks[i];
1524  lsp::Position startPos(chunk.lineOffset);
1525  lsp::Position endPos((i == e - 1) ? totalNumLines - 1
1526  : chunks[i + 1]->lineOffset);
1527  lsp::DocumentSymbol symbol("<file-split-" + Twine(i) + ">",
1528  lsp::SymbolKind::Namespace,
1529  /*range=*/lsp::Range(startPos, endPos),
1530  /*selectionRange=*/lsp::Range(startPos));
1531  chunk.document.findDocumentSymbols(symbol.children);
1532 
1533  // Fixup the locations of document symbols within this chunk.
1534  if (i != 0) {
1536  for (lsp::DocumentSymbol &childSymbol : symbol.children)
1537  symbolsToFix.push_back(&childSymbol);
1538 
1539  while (!symbolsToFix.empty()) {
1540  lsp::DocumentSymbol *symbol = symbolsToFix.pop_back_val();
1541  chunk.adjustLocForChunkOffset(symbol->range);
1542  chunk.adjustLocForChunkOffset(symbol->selectionRange);
1543 
1544  for (lsp::DocumentSymbol &childSymbol : symbol->children)
1545  symbolsToFix.push_back(&childSymbol);
1546  }
1547  }
1548 
1549  // Push the symbol for this chunk.
1550  symbols.emplace_back(std::move(symbol));
1551  }
1552 }
1553 
1554 lsp::CompletionList PDLTextFile::getCodeCompletion(const lsp::URIForFile &uri,
1555  lsp::Position completePos) {
1556  PDLTextFileChunk &chunk = getChunkFor(completePos);
1557  lsp::CompletionList completionList =
1558  chunk.document.getCodeCompletion(uri, completePos);
1559 
1560  // Adjust any completion locations.
1561  for (lsp::CompletionItem &item : completionList.items) {
1562  if (item.textEdit)
1563  chunk.adjustLocForChunkOffset(item.textEdit->range);
1564  for (lsp::TextEdit &edit : item.additionalTextEdits)
1565  chunk.adjustLocForChunkOffset(edit.range);
1566  }
1567  return completionList;
1568 }
1569 
1570 lsp::SignatureHelp PDLTextFile::getSignatureHelp(const lsp::URIForFile &uri,
1571  lsp::Position helpPos) {
1572  return getChunkFor(helpPos).document.getSignatureHelp(uri, helpPos);
1573 }
1574 
1575 void PDLTextFile::getInlayHints(const lsp::URIForFile &uri, lsp::Range range,
1576  std::vector<lsp::InlayHint> &inlayHints) {
1577  auto startIt = getChunkItFor(range.start);
1578  auto endIt = getChunkItFor(range.end);
1579 
1580  // Functor used to get the chunks for a given file, and fixup any locations
1581  auto getHintsForChunk = [&](ChunkIterator chunkIt, lsp::Range range) {
1582  size_t currentNumHints = inlayHints.size();
1583  chunkIt->document.getInlayHints(uri, range, inlayHints);
1584 
1585  // If this isn't the first chunk, update any positions to account for line
1586  // number differences.
1587  if (&*chunkIt != &*chunks.front()) {
1588  for (auto &hint : llvm::drop_begin(inlayHints, currentNumHints))
1589  chunkIt->adjustLocForChunkOffset(hint.position);
1590  }
1591  };
1592  // Returns the number of lines held by a given chunk.
1593  auto getNumLines = [](ChunkIterator chunkIt) {
1594  return (chunkIt + 1)->lineOffset - chunkIt->lineOffset;
1595  };
1596 
1597  // Check if the range is fully within a single chunk.
1598  if (startIt == endIt)
1599  return getHintsForChunk(startIt, range);
1600 
1601  // Otherwise, the range is split between multiple chunks. The first chunk
1602  // has the correct range start, but covers the total document.
1603  getHintsForChunk(startIt, lsp::Range(range.start, getNumLines(startIt)));
1604 
1605  // Every chunk in between uses the full document.
1606  for (++startIt; startIt != endIt; ++startIt)
1607  getHintsForChunk(startIt, lsp::Range(0, getNumLines(startIt)));
1608 
1609  // The range for the last chunk starts at the beginning of the document, up
1610  // through the end of the input range.
1611  getHintsForChunk(startIt, lsp::Range(0, range.end));
1612 }
1613 
1615 PDLTextFile::getPDLLViewOutput(lsp::PDLLViewOutputKind kind) {
1617  {
1618  llvm::raw_string_ostream outputOS(result.output);
1619  llvm::interleave(
1620  llvm::make_pointee_range(chunks),
1621  [&](PDLTextFileChunk &chunk) {
1622  chunk.document.getPDLLViewOutput(outputOS, kind);
1623  },
1624  [&] { outputOS << "\n// -----\n\n"; });
1625  }
1626  return result;
1627 }
1628 
1629 void PDLTextFile::initialize(const lsp::URIForFile &uri, int64_t newVersion,
1630  std::vector<lsp::Diagnostic> &diagnostics) {
1631  version = newVersion;
1632  chunks.clear();
1633 
1634  // Split the file into separate PDL documents.
1635  // TODO: Find a way to share the split file marker with other tools. We don't
1636  // want to use `splitAndProcessBuffer` here, but we do want to make sure this
1637  // marker doesn't go out of sync.
1638  SmallVector<StringRef, 8> subContents;
1639  StringRef(contents).split(subContents, "// -----");
1640  chunks.emplace_back(std::make_unique<PDLTextFileChunk>(
1641  /*lineOffset=*/0, uri, subContents.front(), extraIncludeDirs,
1642  diagnostics));
1643 
1644  uint64_t lineOffset = subContents.front().count('\n');
1645  for (StringRef docContents : llvm::drop_begin(subContents)) {
1646  unsigned currentNumDiags = diagnostics.size();
1647  auto chunk = std::make_unique<PDLTextFileChunk>(
1648  lineOffset, uri, docContents, extraIncludeDirs, diagnostics);
1649  lineOffset += docContents.count('\n');
1650 
1651  // Adjust locations used in diagnostics to account for the offset from the
1652  // beginning of the file.
1653  for (lsp::Diagnostic &diag :
1654  llvm::drop_begin(diagnostics, currentNumDiags)) {
1655  chunk->adjustLocForChunkOffset(diag.range);
1656 
1657  if (!diag.relatedInformation)
1658  continue;
1659  for (auto &it : *diag.relatedInformation)
1660  if (it.location.uri == uri)
1661  chunk->adjustLocForChunkOffset(it.location.range);
1662  }
1663  chunks.emplace_back(std::move(chunk));
1664  }
1665  totalNumLines = lineOffset;
1666 }
1667 
1668 PDLTextFile::ChunkIterator PDLTextFile::getChunkItFor(lsp::Position &pos) {
1669  if (chunks.size() == 1)
1670  return chunks.begin();
1671 
1672  // Search for the first chunk with a greater line offset, the previous chunk
1673  // is the one that contains `pos`.
1674  auto it = llvm::upper_bound(
1675  chunks, pos, [](const lsp::Position &pos, const auto &chunk) {
1676  return static_cast<uint64_t>(pos.line) < chunk->lineOffset;
1677  });
1678  ChunkIterator chunkIt(it == chunks.end() ? (chunks.end() - 1) : --it);
1679  pos.line -= chunkIt->lineOffset;
1680  return chunkIt;
1681 }
1682 
1683 //===----------------------------------------------------------------------===//
1684 // PDLLServer::Impl
1685 //===----------------------------------------------------------------------===//
1686 
1688  explicit Impl(const Options &options)
1689  : options(options), compilationDatabase(options.compilationDatabases) {}
1690 
1691  /// PDLL LSP options.
1693 
1694  /// The compilation database containing additional information for files
1695  /// passed to the server.
1697 
1698  /// The files held by the server, mapped by their URI file name.
1699  llvm::StringMap<std::unique_ptr<PDLTextFile>> files;
1700 };
1701 
1702 //===----------------------------------------------------------------------===//
1703 // PDLLServer
1704 //===----------------------------------------------------------------------===//
1705 
1706 lsp::PDLLServer::PDLLServer(const Options &options)
1707  : impl(std::make_unique<Impl>(options)) {}
1708 lsp::PDLLServer::~PDLLServer() = default;
1709 
1710 void lsp::PDLLServer::addDocument(const URIForFile &uri, StringRef contents,
1711  int64_t version,
1712  std::vector<Diagnostic> &diagnostics) {
1713  // Build the set of additional include directories.
1714  std::vector<std::string> additionalIncludeDirs = impl->options.extraDirs;
1715  const auto &fileInfo = impl->compilationDatabase.getFileInfo(uri.file());
1716  llvm::append_range(additionalIncludeDirs, fileInfo.includeDirs);
1717 
1718  impl->files[uri.file()] = std::make_unique<PDLTextFile>(
1719  uri, contents, version, additionalIncludeDirs, diagnostics);
1720 }
1721 
1724  int64_t version, std::vector<Diagnostic> &diagnostics) {
1725  // Check that we actually have a document for this uri.
1726  auto it = impl->files.find(uri.file());
1727  if (it == impl->files.end())
1728  return;
1729 
1730  // Try to update the document. If we fail, erase the file from the server. A
1731  // failed updated generally means we've fallen out of sync somewhere.
1732  if (failed(it->second->update(uri, version, changes, diagnostics)))
1733  impl->files.erase(it);
1734 }
1735 
1736 std::optional<int64_t> lsp::PDLLServer::removeDocument(const URIForFile &uri) {
1737  auto it = impl->files.find(uri.file());
1738  if (it == impl->files.end())
1739  return std::nullopt;
1740 
1741  int64_t version = it->second->getVersion();
1742  impl->files.erase(it);
1743  return version;
1744 }
1745 
1747  const Position &defPos,
1748  std::vector<Location> &locations) {
1749  auto fileIt = impl->files.find(uri.file());
1750  if (fileIt != impl->files.end())
1751  fileIt->second->getLocationsOf(uri, defPos, locations);
1752 }
1753 
1755  const Position &pos,
1756  std::vector<Location> &references) {
1757  auto fileIt = impl->files.find(uri.file());
1758  if (fileIt != impl->files.end())
1759  fileIt->second->findReferencesOf(uri, pos, references);
1760 }
1761 
1763  const URIForFile &uri, std::vector<DocumentLink> &documentLinks) {
1764  auto fileIt = impl->files.find(uri.file());
1765  if (fileIt != impl->files.end())
1766  return fileIt->second->getDocumentLinks(uri, documentLinks);
1767 }
1768 
1769 std::optional<lsp::Hover> lsp::PDLLServer::findHover(const URIForFile &uri,
1770  const Position &hoverPos) {
1771  auto fileIt = impl->files.find(uri.file());
1772  if (fileIt != impl->files.end())
1773  return fileIt->second->findHover(uri, hoverPos);
1774  return std::nullopt;
1775 }
1776 
1778  const URIForFile &uri, std::vector<DocumentSymbol> &symbols) {
1779  auto fileIt = impl->files.find(uri.file());
1780  if (fileIt != impl->files.end())
1781  fileIt->second->findDocumentSymbols(symbols);
1782 }
1783 
1786  const Position &completePos) {
1787  auto fileIt = impl->files.find(uri.file());
1788  if (fileIt != impl->files.end())
1789  return fileIt->second->getCodeCompletion(uri, completePos);
1790  return CompletionList();
1791 }
1792 
1794  const Position &helpPos) {
1795  auto fileIt = impl->files.find(uri.file());
1796  if (fileIt != impl->files.end())
1797  return fileIt->second->getSignatureHelp(uri, helpPos);
1798  return SignatureHelp();
1799 }
1800 
1801 void lsp::PDLLServer::getInlayHints(const URIForFile &uri, const Range &range,
1802  std::vector<InlayHint> &inlayHints) {
1803  auto fileIt = impl->files.find(uri.file());
1804  if (fileIt == impl->files.end())
1805  return;
1806  fileIt->second->getInlayHints(uri, range, inlayHints);
1807 
1808  // Drop any duplicated hints that may have cropped up.
1809  llvm::sort(inlayHints);
1810  inlayHints.erase(std::unique(inlayHints.begin(), inlayHints.end()),
1811  inlayHints.end());
1812 }
1813 
1814 std::optional<lsp::PDLLViewOutputResult>
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;
1821 }
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.
Definition: MLIRServer.cpp:110
static std::string diag(const llvm::Value &value)
static std::optional< std::string > getDocumentationFor(llvm::SourceMgr &sourceMgr, const ast::Decl *decl)
Get or extract the documentation for the given decl.
Definition: PDLLServer.cpp:116
static lsp::Location getLocationFromLoc(llvm::SourceMgr &mgr, SMRange range, const lsp::URIForFile &uri)
Returns a language server location from the given source range.
Definition: PDLLServer.cpp:61
static lsp::URIForFile getURIFromLoc(llvm::SourceMgr &mgr, SMRange loc, const lsp::URIForFile &mainFileURI)
Returns a language server uri for the given source location.
Definition: PDLLServer.cpp:40
static bool shouldAddHintFor(const ast::Expr *expr, StringRef name)
Returns true if the given name should be added as a hint for expr.
static std::optional< lsp::Diagnostic > getLspDiagnoticFromDiag(llvm::SourceMgr &sourceMgr, const ast::Diagnostic &diag, const lsp::URIForFile &uri)
Convert the given MLIR diagnostic to the LSP form.
Definition: PDLLServer.cpp:68
static bool isMainFileLoc(llvm::SourceMgr &mgr, SMRange loc)
Returns true if the given location is in the main file of the source manager.
Definition: PDLLServer.cpp:56
static llvm::ManagedStatic< PassManagerOptions > options
static void rewrite(DataFlowSolver &solver, MLIRContext *context, MutableArrayRef< Region > initialRegions)
Rewrite the given regions using the computing analysis.
Definition: SCCP.cpp:67
This class provides support for representing a failure result, or a valid value of type T.
Definition: LogicalResult.h:78
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:60
Set of flags used to control the behavior of the various IR print methods (e.g.
OperationName getName()
The name of an operation is the key identifier for it.
Definition: Operation.h:119
This class is a utility diagnostic handler for use with llvm::SourceMgr.
Definition: Diagnostics.h:553
This class contains a collection of compilation information for files provided to the language server...
static void error(const char *fmt, Ts &&...vals)
Definition: Logging.h:42
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 getDocumentLinks(const URIForFile &uri, std::vector< DocumentLink > &documentLinks)
Return the document links referenced by the given file.
void getInlayHints(const URIForFile &uri, const Range &range, std::vector< InlayHint > &inlayHints)
Get the inlay hints for the range within 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 findReferencesOf(const URIForFile &uri, const Position &pos, std::vector< Location > &references)
Find all references of the object pointed at by the given position.
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 findDocumentSymbols(const URIForFile &uri, std::vector< DocumentSymbol > &symbols)
Find all of the document symbols within the given file.
std::optional< int64_t > removeDocument(const URIForFile &uri)
Remove the document with the given uri.
CompletionList getCodeCompletion(const URIForFile &uri, const Position &completePos)
Get the code completion list for the position within the given file.
SignatureHelp getSignatureHelp(const URIForFile &uri, const Position &helpPos)
Get the signature help for the position within the given file.
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< PDLLViewOutputResult > getPDLLViewOutput(const URIForFile &uri, PDLLViewOutputKind kind)
Get the output of the given PDLL file, or std::nullopt if there is no valid output.
URI in "file" scheme for a file.
Definition: Protocol.h:100
static llvm::Expected< URIForFile > fromFile(StringRef absoluteFilepath, StringRef scheme="file")
Try to build a URIForFile from the given absolute file path and optional scheme.
Definition: Protocol.cpp:233
StringRef file() const
Returns the absolute path to the file.
Definition: Protocol.h:113
This class provides an abstract interface into the parser for hooking in code completion events.
Definition: CodeComplete.h:30
virtual void codeCompleteConstraintName(ast::Type currentType, bool allowInlineTypeConstraints, const ast::DeclScope *scope)
Signal code completion for a constraint name with an optional decl scope.
virtual void codeCompleteOperationAttributeName(StringRef opName)
Signal code completion for a member access into the given operation type.
Definition: CodeComplete.h:48
virtual void codeCompleteOperationOperandsSignature(std::optional< StringRef > opName, unsigned currentNumOperands)
Signal code completion for the signature of an operation's operands.
Definition: CodeComplete.h:80
virtual void codeCompleteOperationName(StringRef dialectName)
Signal code completion for an operation name in the given dialect.
Definition: CodeComplete.h:62
virtual void codeCompleteOperationResultsSignature(std::optional< StringRef > opName, unsigned currentNumResults)
Signal code completion for the signature of an operation's results.
Definition: CodeComplete.h:85
virtual void codeCompleteDialectName()
Signal code completion for a dialect name.
Definition: CodeComplete.h:59
virtual void codeCompleteOperationMemberAccess(ast::OperationType opType)
Signal code completion for a member access into the given operation type.
virtual void codeCompleteTupleMemberAccess(ast::TupleType tupleType)
Signal code completion for a member access into the given tuple type.
virtual void codeCompletePatternMetadata()
Signal code completion for Pattern metadata.
Definition: CodeComplete.h:65
virtual void codeCompleteCallSignature(const ast::CallableDecl *callable, unsigned currentNumArgs)
Signal code completion for the signature of a callable.
Definition: CodeComplete.h:75
virtual void codeCompleteIncludeFilename(StringRef curPath)
Signal code completion for an include filename.
Definition: CodeComplete.h:68
The class represents an Attribute constraint, and constrains a variable to be an Attribute.
Definition: Nodes.h:749
This class represents a PDLL type that corresponds to an mlir::Attribute.
Definition: Types.h:131
This expression represents a call to a decl, such as a UserConstraintDecl/UserRewriteDecl.
Definition: Nodes.h:390
Expr * getCallableExpr() const
Return the callable of this call.
Definition: Nodes.h:397
MutableArrayRef< Expr * > getArguments()
Return the arguments of this call.
Definition: Nodes.h:400
This decl represents a shared interface for all callable decls.
Definition: Nodes.h:1188
This class represents the main context of the PDLL AST.
Definition: Context.h:25
This class represents the base of all "core" constraints.
Definition: Nodes.h:733
This class represents a scope for named AST decls.
Definition: Nodes.h:64
This class represents the base Decl node.
Definition: Nodes.h:669
std::optional< StringRef > getDocComment() const
Return the documentation comment attached to this decl if it has been set.
Definition: Nodes.h:682
This class provides a simple implementation of a PDLL diagnostic.
Definition: Diagnostic.h:30
This class represents a base AST Expression node.
Definition: Nodes.h:345
Type getType() const
Return the type of this expression.
Definition: Nodes.h:348
This class represents a top-level AST module.
Definition: Nodes.h:1291
This class represents a base AST node.
Definition: Nodes.h:108
SMRange getLoc() const
Return the location of this node.
Definition: Nodes.h:131
The class represents an Operation constraint, and constrains a variable to be an Operation.
Definition: Nodes.h:772
std::optional< StringRef > getName() const
Return the name of the operation, or std::nullopt if there isn't one.
Definition: Nodes.cpp:409
Expr * getRootOpExpr() const
Return the root operation of this rewrite.
Definition: Nodes.h:237
This expression represents the structural form of an MLIR Operation.
Definition: Nodes.h:512
MutableArrayRef< Expr * > getResultTypes()
Return the result types of this operation.
Definition: Nodes.h:540
MutableArrayRef< Expr * > getOperands()
Return the operands of this operation.
Definition: Nodes.h:532
This class represents a PDLL type that corresponds to an mlir::Operation.
Definition: Types.h:158
const ods::Operation * getODSOperation() const
Return the ODS operation that this type refers to, or nullptr if the ODS operation is unknown.
Definition: Types.cpp:87
This Decl represents a single Pattern.
Definition: Nodes.h:1037
const OpRewriteStmt * getRootRewriteStmt() const
Return the root rewrite statement of this pattern.
Definition: Nodes.h:1054
std::optional< uint16_t > getBenefit() const
Return the benefit of this pattern if specified, or std::nullopt.
Definition: Nodes.h:1045
bool hasBoundedRewriteRecursion() const
Return if this pattern has bounded rewrite recursion.
Definition: Nodes.h:1048
This class represents a PDLL tuple type, i.e.
Definition: Types.h:244
The class represents a Type constraint, and constrains a variable to be a Type.
Definition: Nodes.h:797
The class represents a TypeRange constraint, and constrains a variable to be a TypeRange.
Definition: Nodes.h:811
U dyn_cast() const
Definition: Types.h:76
The class represents a Value constraint, and constrains a variable to be a Value.
Definition: Nodes.h:825
The class represents a ValueRange constraint, and constrains a variable to be a ValueRange.
Definition: Nodes.h:847
This class represents a PDLL type that corresponds to an mlir::ValueRange.
Definition: Types.h:213
This class represents a PDLL type that corresponds to an mlir::Value.
Definition: Types.h:285
This Decl represents the definition of a PDLL variable.
Definition: Nodes.h:1242
const Name & getName() const
Return the name of the decl.
Definition: Nodes.h:1261
Expr * getInitExpr() const
Return the initializer expression of this statement, or nullptr if there was no initializer.
Definition: Nodes.h:1258
MutableArrayRef< ConstraintRef > getConstraints()
Return the constraints of this variable.
Definition: Nodes.h:1249
Type getType() const
Return the type of the decl.
Definition: Nodes.h:1264
This class represents a generic ODS Attribute constraint.
Definition: Constraint.h:63
StringRef getCppClass() const
Return the name of the underlying c++ class of this constraint.
Definition: Constraint.h:66
This class provides an ODS representation of a specific operation attribute.
Definition: Operation.h:39
StringRef getSummary() const
Return the summary of this constraint.
Definition: Constraint.h:44
This class contains all of the registered ODS operation classes.
Definition: Context.h:32
auto getDialects() const
Return a range of all of the registered dialects.
Definition: Context.h:57
const Dialect * lookupDialect(StringRef name) const
Lookup a dialect registered with the given name, or null if no dialect with that name was inserted.
Definition: Context.cpp:58
const Operation * lookupOperation(StringRef name) const
Lookup an operation registered with the given name, or null if no operation with that name is registe...
Definition: Context.cpp:73
This class represents an ODS dialect, and contains information on the constructs held within the dial...
Definition: Dialect.h:26
const llvm::StringMap< std::unique_ptr< Operation > > & getOperations() const
Return a map of all of the operations registered to this dialect.
Definition: Dialect.h:46
This class provides an ODS representation of a specific operation operand or result.
Definition: Operation.h:74
const TypeConstraint & getConstraint() const
Return the constraint of this value.
Definition: Operation.h:97
VariableLengthKind getVariableLengthKind() const
Returns the variable length kind of this value.
Definition: Operation.h:92
StringRef getName() const
Return the name of this value.
Definition: Operation.h:77
This class provides an ODS representation of a specific operation.
Definition: Operation.h:125
ArrayRef< Attribute > getAttributes() const
Returns the attributes of this operation.
Definition: Operation.h:162
SMRange getLoc() const
Return the source location of this operation.
Definition: Operation.h:128
ArrayRef< OperandOrResult > getOperands() const
Returns the operands of this operation.
Definition: Operation.h:165
ArrayRef< OperandOrResult > getResults() const
Returns the results of this operation.
Definition: Operation.h:168
This class represents a generic ODS Type constraint.
Definition: Constraint.h:84
StringRef getCppClass() const
Return the name of the underlying c++ class of this constraint.
Definition: Constraint.h:87
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:285
PDLLViewOutputKind
The type of output to view from PDLL.
Definition: Protocol.h:32
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.
@ PlainText
The primary text to be inserted is treated as a plain string.
@ Snippet
The primary text to be inserted is treated as a snippet.
void codegenPDLLToCPP(const ast::Module &astModule, ModuleOp module, raw_ostream &os)
Definition: CPPGen.cpp:253
FailureOr< ast::Module * > parsePDLLAST(ast::Context &ctx, llvm::SourceMgr &sourceMgr, bool enableDocumentation=false, CodeCompleteContext *codeCompleteContext=nullptr)
Parse an AST module from the main file of the given source manager.
Definition: Parser.cpp:3194
OwningOpRef< ModuleOp > codegenPDLLToMLIR(MLIRContext *mlirContext, const ast::Context &context, const llvm::SourceMgr &sourceMgr, const ast::Module &module)
Given a PDLL module, generate an MLIR PDL pattern module within the given MLIR context.
Definition: MLIRGen.cpp:624
Include the generated interface declarations.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:56
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value.
Definition: LogicalResult.h:72
Impl(const Options &options)
lsp::CompilationDatabase compilationDatabase
The compilation database containing additional information for files passed to the server.
llvm::StringMap< std::unique_ptr< PDLTextFile > > files
The files held by the server, mapped by their URI file name.
const Options & options
PDLL LSP options.
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26
std::string detail
A human-readable string with additional information about this item, like type or symbol information.
Definition: Protocol.h:840
std::string filterText
A string that should be used when filtering a set of completion items.
Definition: Protocol.h:851
std::optional< TextEdit > textEdit
An edit which is applied to a document when selecting this completion.
Definition: Protocol.h:866
std::optional< MarkupContent > documentation
A human-readable string that represents a doc-comment.
Definition: Protocol.h:843
std::string insertText
A string that should be inserted to a document when selecting this completion.
Definition: Protocol.h:855
std::string label
The label of this completion item.
Definition: Protocol.h:832
CompletionItemKind kind
The kind of this completion item.
Definition: Protocol.h:836
std::vector< TextEdit > additionalTextEdits
An optional array of additional text edits that are applied when selecting this completion.
Definition: Protocol.h:871
InsertTextFormat insertTextFormat
The format of the insert text.
Definition: Protocol.h:859
std::string sortText
A string that should be used when comparing this item with other items.
Definition: Protocol.h:847
Represents a collection of completion items to be presented in the editor.
Definition: Protocol.h:887
std::vector< CompletionItem > items
The completion items.
Definition: Protocol.h:893
std::string source
A human-readable string describing the source of this diagnostic, e.g.
Definition: Protocol.h:690
DiagnosticSeverity severity
The diagnostic's severity.
Definition: Protocol.h:686
Range range
The source range where the message applies.
Definition: Protocol.h:682
std::optional< std::vector< DiagnosticRelatedInformation > > relatedInformation
An array of related diagnostic information, e.g.
Definition: Protocol.h:697
std::string message
The diagnostic's message.
Definition: Protocol.h:693
std::optional< std::string > category
The diagnostic's category.
Definition: Protocol.h:703
Represents programming constructs like variables, classes, interfaces etc.
Definition: Protocol.h:596
Range range
The range enclosing this symbol not including leading/trailing whitespace but everything else like co...
Definition: Protocol.h:617
Range selectionRange
The range that should be selected and revealed when this symbol is being picked, e....
Definition: Protocol.h:621
std::vector< DocumentSymbol > children
Children of this symbol, e.g. properties of a class.
Definition: Protocol.h:624
Inlay hint information.
Definition: Protocol.h:1093
URIForFile uri
The text document's URI.
Definition: Protocol.h:394
Represents the result of viewing the output of a PDLL file.
Definition: Protocol.h:58
std::string output
The string representation of the output.
Definition: Protocol.h:60
A single parameter of a particular signature.
Definition: Protocol.h:946
int line
Line position in a document (zero-based).
Definition: Protocol.h:293
SMLoc getAsSMLoc(llvm::SourceMgr &mgr) const
Convert this position into a source location in the main file of the given source manager.
Definition: Protocol.h:316
Position end
The range's end position.
Definition: Protocol.h:345
Position start
The range's start position.
Definition: Protocol.h:342
SMRange getAsSMRange(llvm::SourceMgr &mgr) const
Convert this range into a source range in the main file of the given source manager.
Definition: Protocol.h:364
Represents the signature of a callable.
Definition: Protocol.h:986
Represents the signature of something callable.
Definition: Protocol.h:966
std::string label
The label of this signature. Mandatory.
Definition: Protocol.h:968
std::string documentation
The documentation of this signature. Optional.
Definition: Protocol.h:971
std::vector< ParameterInformation > parameters
The parameters of this signature.
Definition: Protocol.h:974
This class represents a single include within a root file.
Range range
The range of the text document to be manipulated.
Definition: Protocol.h:737
This class provides a convenient API for interacting with source names.
Definition: Nodes.h:37
StringRef getName() const
Return the raw string name.
Definition: Nodes.h:41
SMRange getLoc() const
Get the location of this name.
Definition: Nodes.h:44