MLIR  20.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"
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>
35 
36 using namespace mlir;
37 using namespace mlir::pdll;
38 
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;
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;
53 }
54 
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();
59 }
60 
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));
65 }
66 
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";
73 
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";
77 
78  // Try to grab a file location for this diagnostic.
79  lsp::Location loc = getLocationFromLoc(sourceMgr, diag.getLocation(), uri);
80  lspDiag.range = loc.range;
81 
82  // Skip diagnostics that weren't emitted within the main file.
83  if (loc.uri != uri)
84  return std::nullopt;
85 
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:
92  break;
93  case ast::Diagnostic::Severity::DK_Error:
95  break;
96  case ast::Diagnostic::Severity::DK_Remark:
98  break;
99  }
100  lspDiag.message = diag.getMessage().str();
101 
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());
108  }
109  if (!relatedDiags.empty())
110  lspDiag.relatedInformation = std::move(relatedDiags);
111 
112  return lspDiag;
113 }
114 
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();
121 
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);
125 }
126 
127 //===----------------------------------------------------------------------===//
128 // PDLIndex
129 //===----------------------------------------------------------------------===//
130 
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) {}
137 
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();
144  }
145  return definition.get<const ods::Operation *>()->getLoc();
146  }
147 
148  /// The main definition of the symbol.
150  /// The set of references to the symbol.
151  std::vector<SMRange> references;
152 };
153 
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) {}
159 
160  /// Initialize the index with the given ast::Module.
161  void initialize(const ast::Module &module, const ods::Context &odsContext);
162 
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;
168 
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 *>>;
177 
178  /// An allocator for the interval map.
179  MapT::Allocator allocator;
180 
181  /// An interval map containing a corresponding definition mapped to a source
182  /// interval.
183  MapT intervalMap;
184 
185  /// A mapping between definitions and their corresponding symbol.
187 };
188 } // namespace
189 
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;
197  };
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);
206  }
207  };
208  auto insertODSOpRef = [&](StringRef opName, SMRange refLoc) {
209  const ods::Operation *odsOp = odsContext.lookupOperation(opName);
210  if (!odsOp)
211  return;
212 
213  PDLIndexSymbol *symbol = getOrInsertDef(odsOp);
214  insertDeclRef(symbol, odsOp->getLoc(), /*isDef=*/true);
215  insertDeclRef(symbol, refLoc);
216  };
217 
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);
229 
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);
234  }
235  } else if (const auto *expr = dyn_cast<ast::DeclRefExpr>(node)) {
236  insertDeclRef(getOrInsertDef(expr->getDecl()), expr->getLoc());
237  }
238  });
239 }
240 
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;
246 
247  if (overlappedRange) {
248  *overlappedRange = SMRange(SMLoc::getFromPointer(it.start()),
249  SMLoc::getFromPointer(it.stop()));
250  }
251  return it.value();
252 }
253 
254 //===----------------------------------------------------------------------===//
255 // PDLDocument
256 //===----------------------------------------------------------------------===//
257 
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;
267 
268  //===--------------------------------------------------------------------===//
269  // Definitions and References
270  //===--------------------------------------------------------------------===//
271 
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);
276 
277  //===--------------------------------------------------------------------===//
278  // Document Links
279  //===--------------------------------------------------------------------===//
280 
281  void getDocumentLinks(const lsp::URIForFile &uri,
282  std::vector<lsp::DocumentLink> &links);
283 
284  //===--------------------------------------------------------------------===//
285  // Hover
286  //===--------------------------------------------------------------------===//
287 
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);
304 
305  //===--------------------------------------------------------------------===//
306  // Document Symbols
307  //===--------------------------------------------------------------------===//
308 
309  void findDocumentSymbols(std::vector<lsp::DocumentSymbol> &symbols);
310 
311  //===--------------------------------------------------------------------===//
312  // Code Completion
313  //===--------------------------------------------------------------------===//
314 
315  lsp::CompletionList getCodeCompletion(const lsp::URIForFile &uri,
316  const lsp::Position &completePos);
317 
318  //===--------------------------------------------------------------------===//
319  // Signature Help
320  //===--------------------------------------------------------------------===//
321 
322  lsp::SignatureHelp getSignatureHelp(const lsp::URIForFile &uri,
323  const lsp::Position &helpPos);
324 
325  //===--------------------------------------------------------------------===//
326  // Inlay Hints
327  //===--------------------------------------------------------------------===//
328 
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);
339 
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);
343 
344  //===--------------------------------------------------------------------===//
345  // PDLL ViewOutput
346  //===--------------------------------------------------------------------===//
347 
348  void getPDLLViewOutput(raw_ostream &os, lsp::PDLLViewOutputKind kind);
349 
350  //===--------------------------------------------------------------------===//
351  // Fields
352  //===--------------------------------------------------------------------===//
353 
354  /// The include directories for this file.
355  std::vector<std::string> includeDirs;
356 
357  /// The source manager containing the contents of the input file.
358  llvm::SourceMgr sourceMgr;
359 
360  /// The ODS and AST contexts.
361  ods::Context odsContext;
362  ast::Context astContext;
363 
364  /// The parsed AST module, or failure if the file wasn't valid.
365  FailureOr<ast::Module *> astModule;
366 
367  /// The index of the parsed module.
368  PDLIndex index;
369 
370  /// The set of includes of the parsed module.
371  SmallVector<lsp::SourceMgrInclude> parsedIncludes;
372 };
373 } // namespace
374 
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;
383  }
384 
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());
390 
391  sourceMgr.setIncludeDirs(includeDirs);
392  sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
393 
394  astContext.getDiagEngine().setHandlerFn([&](const ast::Diagnostic &diag) {
395  if (auto lspDiag = getLspDiagnoticFromDiag(sourceMgr, diag, uri))
396  diagnostics.push_back(std::move(*lspDiag));
397  });
398  astModule = parsePDLLAST(astContext, sourceMgr, /*enableDocumentation=*/true);
399 
400  // Initialize the set of parsed includes.
401  lsp::gatherIncludeFiles(sourceMgr, parsedIncludes);
402 
403  // If we failed to parse the module, there is nothing left to initialize.
404  if (failed(astModule))
405  return;
406 
407  // Prepare the AST index with the parsed module.
408  index.initialize(**astModule, odsContext);
409 }
410 
411 //===----------------------------------------------------------------------===//
412 // PDLDocument: Definitions and References
413 //===----------------------------------------------------------------------===//
414 
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;
422 
423  locations.push_back(getLocationFromLoc(sourceMgr, symbol->getDefLoc(), uri));
424 }
425 
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;
433 
434  references.push_back(getLocationFromLoc(sourceMgr, symbol->getDefLoc(), uri));
435  for (SMRange refLoc : symbol->references)
436  references.push_back(getLocationFromLoc(sourceMgr, refLoc, uri));
437 }
438 
439 //===--------------------------------------------------------------------===//
440 // PDLDocument: Document Links
441 //===--------------------------------------------------------------------===//
442 
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);
447 }
448 
449 //===----------------------------------------------------------------------===//
450 // PDLDocument: Hover
451 //===----------------------------------------------------------------------===//
452 
453 std::optional<lsp::Hover>
454 PDLDocument::findHover(const lsp::URIForFile &uri,
455  const lsp::Position &hoverPos) {
456  SMLoc posLoc = hoverPos.getAsSMLoc(sourceMgr);
457 
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();
462 
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;
468 
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);
475 }
476 
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);
482 
483  // Add hover for patterns.
484  if (const auto *patternDecl = dyn_cast<ast::PatternDecl>(decl))
485  return buildHoverForPattern(patternDecl, hoverRange);
486 
487  // Add hover for core constraints.
488  if (const auto *cst = dyn_cast<ast::CoreConstraintDecl>(decl))
489  return buildHoverForCoreConstraint(cst, hoverRange);
490 
491  // Add hover for user constraints.
492  if (const auto *cst = dyn_cast<ast::UserConstraintDecl>(decl))
493  return buildHoverForUserConstraintOrRewrite("Constraint", cst, hoverRange);
494 
495  // Add hover for user rewrites.
496  if (const auto *rewrite = dyn_cast<ast::UserRewriteDecl>(decl))
497  return buildHoverForUserConstraintOrRewrite("Rewrite", rewrite, hoverRange);
498 
499  return std::nullopt;
500 }
501 
502 lsp::Hover PDLDocument::buildHoverForOpName(const ods::Operation *op,
503  const SMRange &hoverRange) {
504  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
505  {
506  llvm::raw_string_ostream hoverOS(hover.contents.value);
507  hoverOS << "**OpName**: `" << op->getName() << "`\n***\n"
508  << op->getSummary() << "\n***\n"
509  << op->getDescription();
510  }
511  return hover;
512 }
513 
514 lsp::Hover PDLDocument::buildHoverForVariable(const ast::VariableDecl *varDecl,
515  const SMRange &hoverRange) {
516  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
517  {
518  llvm::raw_string_ostream hoverOS(hover.contents.value);
519  hoverOS << "**Variable**: `" << varDecl->getName().getName() << "`\n***\n"
520  << "Type: `" << varDecl->getType() << "`\n";
521  }
522  return hover;
523 }
524 
525 lsp::Hover PDLDocument::buildHoverForPattern(const ast::PatternDecl *decl,
526  const SMRange &hoverRange) {
527  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
528  {
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";
540 
541  // Format the documentation for the decl.
542  if (std::optional<std::string> doc = getDocumentationFor(sourceMgr, decl))
543  hoverOS << "\n" << *doc << "\n";
544  }
545  return hover;
546 }
547 
549 PDLDocument::buildHoverForCoreConstraint(const ast::CoreConstraintDecl *decl,
550  const SMRange &hoverRange) {
551  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
552  {
553  llvm::raw_string_ostream hoverOS(hover.contents.value);
554  hoverOS << "**Constraint**: `";
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 << ">";
561  })
562  .Case([&](const ast::TypeConstraintDecl *) { hoverOS << "Type"; })
563  .Case([&](const ast::TypeRangeConstraintDecl *) {
564  hoverOS << "TypeRange";
565  })
566  .Case([&](const ast::ValueConstraintDecl *) { hoverOS << "Value"; })
567  .Case([&](const ast::ValueRangeConstraintDecl *) {
568  hoverOS << "ValueRange";
569  });
570  hoverOS << "`\n";
571  }
572  return hover;
573 }
574 
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));
579  {
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";
590  }
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";
600  }
601  hoverOS << "***\n";
602  }
603  } else {
604  hoverOS << "Results:\n* `" << resultType << "`\n";
605  hoverOS << "***\n";
606  }
607 
608  // Format the documentation for the decl.
609  if (std::optional<std::string> doc = getDocumentationFor(sourceMgr, decl))
610  hoverOS << "\n" << *doc << "\n";
611  }
612  return hover;
613 }
614 
615 //===----------------------------------------------------------------------===//
616 // PDLDocument: Document Symbols
617 //===----------------------------------------------------------------------===//
618 
619 void PDLDocument::findDocumentSymbols(
620  std::vector<lsp::DocumentSymbol> &symbols) {
621  if (failed(astModule))
622  return;
623 
624  for (const ast::Decl *decl : (*astModule)->getChildren()) {
625  if (!isMainFileLoc(sourceMgr, decl->getLoc()))
626  continue;
627 
628  if (const auto *patternDecl = dyn_cast<ast::PatternDecl>(decl)) {
629  const ast::Name *name = patternDecl->getName();
630 
631  SMRange nameLoc = name ? name->getLoc() : patternDecl->getLoc();
632  SMRange bodyLoc(nameLoc.Start, patternDecl->getBody()->getLoc().End);
633 
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;
641 
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;
649 
650  symbols.emplace_back(
651  cDecl->getName().getName(), lsp::SymbolKind::Function,
652  lsp::Range(sourceMgr, bodyLoc), lsp::Range(sourceMgr, nameLoc));
653  }
654  }
655 }
656 
657 //===----------------------------------------------------------------------===//
658 // PDLDocument: Code Completion
659 //===----------------------------------------------------------------------===//
660 
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) {}
671 
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;
682  item.detail = llvm::formatv("{0}: {1}", i, elementTypes[i]);
684  completionList.items.emplace_back(item);
685 
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);
693  }
694  }
695  }
696 
698  const ods::Operation *odsOp = opType.getODSOperation();
699  if (!odsOp)
700  return;
701 
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();
706 
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;
713  switch (result.getVariableLengthKind()) {
715  item.detail = llvm::formatv("{0}: Value", it.index()).str();
716  break;
718  item.detail = llvm::formatv("{0}: Value?", it.index()).str();
719  break;
721  item.detail = llvm::formatv("{0}: ValueRange", it.index()).str();
722  break;
723  }
726  llvm::formatv("{0}\n\n```c++\n{1}\n```\n", constraint.getSummary(),
727  constraint.getCppClass())
728  .str()};
730  completionList.items.emplace_back(item);
731 
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);
741  }
742  }
743  }
744 
745  void codeCompleteOperationAttributeName(StringRef opName) final {
746  const ods::Operation *odsOp = odsContext.lookupOperation(opName);
747  if (!odsOp)
748  return;
749 
750  for (const ods::Attribute &attr : odsOp->getAttributes()) {
751  const ods::AttributeConstraint &constraint = attr.getConstraint();
752 
753  lsp::CompletionItem item;
754  item.label = attr.getName().str();
756  item.detail = attr.isOptional() ? "optional" : "";
759  llvm::formatv("{0}\n\n```c++\n{1}\n```\n", constraint.getSummary(),
760  constraint.getCppClass())
761  .str()};
763  completionList.items.emplace_back(item);
764  }
765  }
766 
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();
775  item.detail = (constraint + " constraint").str();
778  ("A single entity core constraint of type `" + mlirType + "`").str()};
779  item.sortText = "0";
780  item.insertText = snippetText.str();
781  item.insertTextFormat = snippetText.empty()
784  completionList.items.emplace_back(item);
785  };
786 
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");
797  }
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>");
809  }
810 
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();
818  item.sortText = "2_" + item.label;
819 
820  // Skip constraints that are not single-arg. We currently only
821  // complete variable constraints.
822  if (cst->getInputs().size() != 1)
823  continue;
824 
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;
829 
830  // Format the constraint signature.
831  {
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();
837  });
838  strOS << ") -> " << cst->getResultType();
839  }
840 
841  // Format the documentation for the constraint.
842  if (std::optional<std::string> doc =
843  getDocumentationFor(sourceMgr, cst)) {
844  item.documentation =
846  }
847 
848  completionList.items.emplace_back(item);
849  }
850  }
851 
852  scope = scope->getParentScope();
853  }
854  }
855 
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();
863  completionList.items.emplace_back(item);
864  }
865  }
866 
867  void codeCompleteOperationName(StringRef dialectName) final {
868  const ods::Dialect *dialect = odsContext.lookupDialect(dialectName);
869  if (!dialect)
870  return;
871 
872  for (const auto &it : dialect->getOperations()) {
873  const ods::Operation &op = *it.second;
874 
875  lsp::CompletionItem item;
876  item.label = op.getName().drop_front(dialectName.size() + 1).str();
879  completionList.items.emplace_back(item);
880  }
881  }
882 
883  void codeCompletePatternMetadata() final {
884  auto addSimpleConstraint = [&](StringRef constraint, StringRef desc,
885  StringRef snippetText = "") {
886  lsp::CompletionItem item;
887  item.label = constraint.str();
889  item.detail = "pattern metadata";
890  item.documentation =
892  item.insertText = snippetText.str();
893  item.insertTextFormat = snippetText.empty()
896  completionList.items.emplace_back(item);
897  };
898 
899  addSimpleConstraint("benefit", "The `benefit` of matching the pattern.",
900  "benefit($1)");
901  addSimpleConstraint("recursion",
902  "The pattern properly handles recursive application.");
903  }
904 
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);
910 
911  // Set of already included completion paths.
912  StringSet<> seenResults;
913 
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
920  if (seenResults.insert(item.label).second)
921  completionList.items.emplace_back(item);
922  };
923 
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);
930 
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());
936 
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();
944  }
945 
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;
955  }
956  default:
957  break;
958  }
959  }
960  }
961 
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;
967  });
968  }
969 
970 private:
971  llvm::SourceMgr &sourceMgr;
972  lsp::CompletionList &completionList;
973  ods::Context &odsContext;
974  ArrayRef<std::string> includeDirs;
975 };
976 } // namespace
977 
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();
984 
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());
992 
993  ast::Context tmpContext(tmpODSContext);
994  (void)parsePDLLAST(tmpContext, sourceMgr, /*enableDocumentation=*/true,
995  &lspCompleteContext);
996 
997  return completionList;
998 }
999 
1000 //===----------------------------------------------------------------------===//
1001 // PDLDocument: Signature Help
1002 //===----------------------------------------------------------------------===//
1003 
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) {}
1012 
1013  void codeCompleteCallSignature(const ast::CallableDecl *callable,
1014  unsigned currentNumArgs) final {
1015  signatureHelp.activeParameter = currentNumArgs;
1016 
1017  lsp::SignatureInformation signatureInfo;
1018  {
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()});
1028  };
1029  llvm::interleaveComma(callable->getInputs(), strOS, formatParamFn);
1030  strOS << ") -> " << callable->getResultType();
1031  }
1032 
1033  // Format the documentation for the callable.
1034  if (std::optional<std::string> doc =
1035  getDocumentationFor(sourceMgr, callable))
1036  signatureInfo.documentation = std::move(*doc);
1037 
1038  signatureHelp.signatures.emplace_back(std::move(signatureInfo));
1039  }
1040 
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");
1049  }
1050 
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");
1058  }
1059 
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;
1065 
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;
1072 
1073  // Build the signature label.
1074  {
1075  llvm::raw_string_ostream strOS(signatureInfo.label);
1076  strOS << "(";
1077  auto formatFn = [&](const ods::OperandOrResult &value) {
1078  unsigned paramStart = strOS.str().size();
1079 
1080  strOS << value.getName() << ": ";
1081 
1082  StringRef constraintDoc = value.getConstraint().getSummary();
1083  std::string paramDoc;
1084  switch (value.getVariableLengthKind()) {
1086  strOS << dataType;
1087  paramDoc = constraintDoc.str();
1088  break;
1090  strOS << dataType << "?";
1091  paramDoc = ("optional: " + constraintDoc).str();
1092  break;
1094  strOS << dataType << "Range";
1095  paramDoc = ("variadic: " + constraintDoc).str();
1096  break;
1097  }
1098 
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});
1103  };
1104  llvm::interleaveComma(values, strOS, formatFn);
1105  strOS << ")";
1106  }
1107  signatureInfo.documentation =
1108  llvm::formatv("`op<{0}>` ODS {1} specification", *opName, label)
1109  .str();
1110  signatureHelp.signatures.emplace_back(std::move(signatureInfo));
1111  }
1112 
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));
1125  }
1126  }
1127 
1128 private:
1129  llvm::SourceMgr &sourceMgr;
1130  lsp::SignatureHelp &signatureHelp;
1131  ods::Context &odsContext;
1132 };
1133 } // namespace
1134 
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();
1140 
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);
1147 
1148  ast::Context tmpContext(tmpODSContext);
1149  (void)parsePDLLAST(tmpContext, sourceMgr, /*enableDocumentation=*/true,
1150  &completeContext);
1151 
1152  return signatureHelp;
1153 }
1154 
1155 //===----------------------------------------------------------------------===//
1156 // PDLDocument: Inlay Hints
1157 //===----------------------------------------------------------------------===//
1158 
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;
1163 
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;
1169  }
1170 
1171  return true;
1172 }
1173 
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();
1184 
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;
1189 
1190  // Handle hints for various types of nodes.
1193  [&](const auto *node) {
1194  this->getInlayHintsFor(node, uri, inlayHints);
1195  });
1196  });
1197 }
1198 
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;
1206 
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;
1214  }
1215 
1216  lsp::InlayHint hint(lsp::InlayHintKind::Type,
1217  lsp::Position(sourceMgr, decl->getLoc().End));
1218  {
1219  llvm::raw_string_ostream labelOS(hint.label);
1220  labelOS << ": " << decl->getType();
1221  }
1222 
1223  inlayHints.emplace_back(std::move(hint));
1224 }
1225 
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;
1236 
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());
1241 }
1242 
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;
1249 
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);
1256  };
1257 
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,
1263  StringRef allValuesName) {
1264  if (values.empty())
1265  return;
1266 
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;
1274  }
1275 
1276  for (const auto &it : llvm::zip(values, odsValues))
1277  addOpHint(std::get<0>(it), std::get<1>(it).getName());
1278  };
1279 
1280  // Add hints for the operands and results of the operation.
1281  addOperandOrResultHints(expr->getOperands(),
1282  odsOp ? odsOp->getOperands()
1284  "operands");
1285  addOperandOrResultHints(expr->getResultTypes(),
1286  odsOp ? odsOp->getResults()
1288  "results");
1289 }
1290 
1291 void PDLDocument::addParameterHintFor(std::vector<lsp::InlayHint> &inlayHints,
1292  const ast::Expr *expr, StringRef label) {
1293  if (!shouldAddHintFor(expr, label))
1294  return;
1295 
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));
1301 }
1302 
1303 //===----------------------------------------------------------------------===//
1304 // PDLL ViewOutput
1305 //===----------------------------------------------------------------------===//
1306 
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;
1314  }
1315 
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;
1328  }
1329 
1330  // Otherwise, generate the output for C++.
1331  assert(kind == lsp::PDLLViewOutputKind::CPP &&
1332  "unexpected PDLLViewOutputKind");
1333  codegenPDLLToCPP(**astModule, *pdlModule, os);
1334 }
1335 
1336 //===----------------------------------------------------------------------===//
1337 // PDLTextFileChunk
1338 //===----------------------------------------------------------------------===//
1339 
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) {}
1349 
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);
1355  }
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; }
1359 
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;
1364 };
1365 } // namespace
1366 
1367 //===----------------------------------------------------------------------===//
1368 // PDLTextFile
1369 //===----------------------------------------------------------------------===//
1370 
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);
1378 
1379  /// Return the current version of this text file.
1380  int64_t getVersion() const { return version; }
1381 
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,
1386  std::vector<lsp::Diagnostic> &diagnostics);
1387 
1388  //===--------------------------------------------------------------------===//
1389  // LSP Queries
1390  //===--------------------------------------------------------------------===//
1391 
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);
1408 
1409 private:
1410  using ChunkIterator = llvm::pointee_iterator<
1411  std::vector<std::unique_ptr<PDLTextFileChunk>>::iterator>;
1412 
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);
1416 
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);
1423  }
1424 
1425  /// The full string contents of the file.
1426  std::string contents;
1427 
1428  /// The version of this file.
1429  int64_t version = 0;
1430 
1431  /// The number of lines in the file.
1432  int64_t totalNumLines = 0;
1433 
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;
1437 
1438  /// The extra set of include directories for this file.
1439  std::vector<std::string> extraIncludeDirs;
1440 };
1441 } // namespace
1442 
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);
1449 }
1450 
1451 LogicalResult
1452 PDLTextFile::update(const lsp::URIForFile &uri, int64_t newVersion,
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();
1458  }
1459 
1460  // If the file contents were properly changed, reinitialize the text file.
1461  initialize(uri, newVersion, diagnostics);
1462  return success();
1463 }
1464 
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);
1470 
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);
1477 }
1478 
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);
1484 
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);
1491 }
1492 
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);
1499 
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);
1504  }
1505 }
1506 
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);
1511 
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;
1516 }
1517 
1518 void PDLTextFile::findDocumentSymbols(
1519  std::vector<lsp::DocumentSymbol> &symbols) {
1520  if (chunks.size() == 1)
1521  return chunks.front()->document.findDocumentSymbols(symbols);
1522 
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);
1535 
1536  // Fixup the locations of document symbols within this chunk.
1537  if (i != 0) {
1539  for (lsp::DocumentSymbol &childSymbol : symbol.children)
1540  symbolsToFix.push_back(&childSymbol);
1541 
1542  while (!symbolsToFix.empty()) {
1543  lsp::DocumentSymbol *symbol = symbolsToFix.pop_back_val();
1544  chunk.adjustLocForChunkOffset(symbol->range);
1545  chunk.adjustLocForChunkOffset(symbol->selectionRange);
1546 
1547  for (lsp::DocumentSymbol &childSymbol : symbol->children)
1548  symbolsToFix.push_back(&childSymbol);
1549  }
1550  }
1551 
1552  // Push the symbol for this chunk.
1553  symbols.emplace_back(std::move(symbol));
1554  }
1555 }
1556 
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);
1562 
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);
1569  }
1570  return completionList;
1571 }
1572 
1573 lsp::SignatureHelp PDLTextFile::getSignatureHelp(const lsp::URIForFile &uri,
1574  lsp::Position helpPos) {
1575  return getChunkFor(helpPos).document.getSignatureHelp(uri, helpPos);
1576 }
1577 
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);
1582 
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);
1587 
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);
1593  }
1594  };
1595  // Returns the number of lines held by a given chunk.
1596  auto getNumLines = [](ChunkIterator chunkIt) {
1597  return (chunkIt + 1)->lineOffset - chunkIt->lineOffset;
1598  };
1599 
1600  // Check if the range is fully within a single chunk.
1601  if (startIt == endIt)
1602  return getHintsForChunk(startIt, range);
1603 
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)));
1607 
1608  // Every chunk in between uses the full document.
1609  for (++startIt; startIt != endIt; ++startIt)
1610  getHintsForChunk(startIt, lsp::Range(0, getNumLines(startIt)));
1611 
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));
1615 }
1616 
1618 PDLTextFile::getPDLLViewOutput(lsp::PDLLViewOutputKind kind) {
1620  {
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);
1626  },
1627  [&] { outputOS << "\n"
1628  << kDefaultSplitMarker << "\n\n"; });
1629  }
1630  return result;
1631 }
1632 
1633 void PDLTextFile::initialize(const lsp::URIForFile &uri, int64_t newVersion,
1634  std::vector<lsp::Diagnostic> &diagnostics) {
1635  version = newVersion;
1636  chunks.clear();
1637 
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));
1644 
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');
1651 
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);
1657 
1658  if (!diag.relatedInformation)
1659  continue;
1660  for (auto &it : *diag.relatedInformation)
1661  if (it.location.uri == uri)
1662  chunk->adjustLocForChunkOffset(it.location.range);
1663  }
1664  chunks.emplace_back(std::move(chunk));
1665  }
1666  totalNumLines = lineOffset;
1667 }
1668 
1669 PDLTextFile::ChunkIterator PDLTextFile::getChunkItFor(lsp::Position &pos) {
1670  if (chunks.size() == 1)
1671  return chunks.begin();
1672 
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;
1678  });
1679  ChunkIterator chunkIt(it == chunks.end() ? (chunks.end() - 1) : --it);
1680  pos.line -= chunkIt->lineOffset;
1681  return chunkIt;
1682 }
1683 
1684 //===----------------------------------------------------------------------===//
1685 // PDLLServer::Impl
1686 //===----------------------------------------------------------------------===//
1687 
1689  explicit Impl(const Options &options)
1690  : options(options), compilationDatabase(options.compilationDatabases) {}
1691 
1692  /// PDLL LSP options.
1694 
1695  /// The compilation database containing additional information for files
1696  /// passed to the server.
1698 
1699  /// The files held by the server, mapped by their URI file name.
1700  llvm::StringMap<std::unique_ptr<PDLTextFile>> files;
1701 };
1702 
1703 //===----------------------------------------------------------------------===//
1704 // PDLLServer
1705 //===----------------------------------------------------------------------===//
1706 
1707 lsp::PDLLServer::PDLLServer(const Options &options)
1708  : impl(std::make_unique<Impl>(options)) {}
1709 lsp::PDLLServer::~PDLLServer() = default;
1710 
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);
1718 
1719  impl->files[uri.file()] = std::make_unique<PDLTextFile>(
1720  uri, contents, version, additionalIncludeDirs, diagnostics);
1721 }
1722 
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;
1730 
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);
1735 }
1736 
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;
1741 
1742  int64_t version = it->second->getVersion();
1743  impl->files.erase(it);
1744  return version;
1745 }
1746 
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);
1753 }
1754 
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);
1761 }
1762 
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);
1768 }
1769 
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;
1776 }
1777 
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);
1783 }
1784 
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();
1792 }
1793 
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();
1800 }
1801 
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);
1808 
1809  // Drop any duplicated hints that may have cropped up.
1810  llvm::sort(inlayHints);
1811  inlayHints.erase(llvm::unique(inlayHints), 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:111
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:117
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:62
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:41
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:69
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:57
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
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.
This class is a utility diagnostic handler for use with llvm::SourceMgr.
Definition: Diagnostics.h:559
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:97
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:232
StringRef file() const
Returns the absolute path to the file.
Definition: Protocol.h:110
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 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:29
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:163
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:249
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
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 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
StringRef getDescription() const
Returns the description of the operation.
Definition: Operation.h:156
StringRef getSummary() const
Returns the summary of the operation.
Definition: Operation.h:153
ArrayRef< Attribute > getAttributes() const
Returns the attributes of this operation.
Definition: Operation.h:162
StringRef getName() const
Returns the name of the operation.
Definition: Operation.h:150
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:344
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.
const char *const kDefaultSplitMarker
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.
std::string detail
A human-readable string with additional information about this item, like type or symbol information.
Definition: Protocol.h:850
std::string filterText
A string that should be used when filtering a set of completion items.
Definition: Protocol.h:861
std::optional< TextEdit > textEdit
An edit which is applied to a document when selecting this completion.
Definition: Protocol.h:876
std::optional< MarkupContent > documentation
A human-readable string that represents a doc-comment.
Definition: Protocol.h:853
std::string insertText
A string that should be inserted to a document when selecting this completion.
Definition: Protocol.h:865
std::string label
The label of this completion item.
Definition: Protocol.h:842
CompletionItemKind kind
The kind of this completion item.
Definition: Protocol.h:846
std::vector< TextEdit > additionalTextEdits
An optional array of additional text edits that are applied when selecting this completion.
Definition: Protocol.h:881
InsertTextFormat insertTextFormat
The format of the insert text.
Definition: Protocol.h:869
std::string sortText
A string that should be used when comparing this item with other items.
Definition: Protocol.h:857
Represents a collection of completion items to be presented in the editor.
Definition: Protocol.h:897
std::vector< CompletionItem > items
The completion items.
Definition: Protocol.h:903
std::string source
A human-readable string describing the source of this diagnostic, e.g.
Definition: Protocol.h:697
DiagnosticSeverity severity
The diagnostic's severity.
Definition: Protocol.h:693
Range range
The source range where the message applies.
Definition: Protocol.h:689
std::optional< std::vector< DiagnosticRelatedInformation > > relatedInformation
An array of related diagnostic information, e.g.
Definition: Protocol.h:704
std::string message
The diagnostic's message.
Definition: Protocol.h:700
std::optional< std::string > category
The diagnostic's category.
Definition: Protocol.h:713
Represents programming constructs like variables, classes, interfaces etc.
Definition: Protocol.h:593
Range range
The range enclosing this symbol not including leading/trailing whitespace but everything else like co...
Definition: Protocol.h:614
Range selectionRange
The range that should be selected and revealed when this symbol is being picked, e....
Definition: Protocol.h:618
std::vector< DocumentSymbol > children
Children of this symbol, e.g. properties of a class.
Definition: Protocol.h:621
Inlay hint information.
Definition: Protocol.h:1103
URIForFile uri
The text document's URI.
Definition: Protocol.h:391
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:956
int line
Line position in a document (zero-based).
Definition: Protocol.h:290
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:313
Position end
The range's end position.
Definition: Protocol.h:342
Position start
The range's start position.
Definition: Protocol.h:339
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:361
Represents the signature of a callable.
Definition: Protocol.h:996
Represents the signature of something callable.
Definition: Protocol.h:976
std::string label
The label of this signature. Mandatory.
Definition: Protocol.h:978
std::string documentation
The documentation of this signature. Optional.
Definition: Protocol.h:981
std::vector< ParameterInformation > parameters
The parameters of this signature.
Definition: Protocol.h:984
This class represents a single include within a root file.
Range range
The range of the text document to be manipulated.
Definition: Protocol.h:747
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