MLIR  16.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 "../lsp-server-support/CompilationDatabase.h"
12 #include "../lsp-server-support/Logging.h"
13 #include "../lsp-server-support/SourceMgrUtils.h"
14 #include "Protocol.h"
15 #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 
34 using namespace mlir;
35 using namespace mlir::pdll;
36 
37 /// Returns a language server uri for the given source location. `mainFileURI`
38 /// corresponds to the uri for the main file of the source manager.
39 static lsp::URIForFile getURIFromLoc(llvm::SourceMgr &mgr, SMRange loc,
40  const lsp::URIForFile &mainFileURI) {
41  int bufferId = mgr.FindBufferContainingLoc(loc.Start);
42  if (bufferId == 0 || bufferId == static_cast<int>(mgr.getMainFileID()))
43  return mainFileURI;
45  mgr.getBufferInfo(bufferId).Buffer->getBufferIdentifier());
46  if (fileForLoc)
47  return *fileForLoc;
48  lsp::Logger::error("Failed to create URI for include file: {0}",
49  llvm::toString(fileForLoc.takeError()));
50  return mainFileURI;
51 }
52 
53 /// Returns true if the given location is in the main file of the source
54 /// manager.
55 static bool isMainFileLoc(llvm::SourceMgr &mgr, SMRange loc) {
56  return mgr.FindBufferContainingLoc(loc.Start) == mgr.getMainFileID();
57 }
58 
59 /// Returns a language server location from the given source range.
60 static lsp::Location getLocationFromLoc(llvm::SourceMgr &mgr, SMRange range,
61  const lsp::URIForFile &uri) {
62  return lsp::Location(getURIFromLoc(mgr, range, uri), lsp::Range(mgr, range));
63 }
64 
65 /// Convert the given MLIR diagnostic to the LSP form.
67 getLspDiagnoticFromDiag(llvm::SourceMgr &sourceMgr, const ast::Diagnostic &diag,
68  const lsp::URIForFile &uri) {
69  lsp::Diagnostic lspDiag;
70  lspDiag.source = "pdll";
71 
72  // FIXME: Right now all of the diagnostics are treated as parser issues, but
73  // some are parser and some are verifier.
74  lspDiag.category = "Parse Error";
75 
76  // Try to grab a file location for this diagnostic.
77  lsp::Location loc = getLocationFromLoc(sourceMgr, diag.getLocation(), uri);
78  lspDiag.range = loc.range;
79 
80  // Skip diagnostics that weren't emitted within the main file.
81  if (loc.uri != uri)
82  return llvm::None;
83 
84  // Convert the severity for the diagnostic.
85  switch (diag.getSeverity()) {
86  case ast::Diagnostic::Severity::DK_Note:
87  llvm_unreachable("expected notes to be handled separately");
88  case ast::Diagnostic::Severity::DK_Warning:
90  break;
91  case ast::Diagnostic::Severity::DK_Error:
93  break;
94  case ast::Diagnostic::Severity::DK_Remark:
96  break;
97  }
98  lspDiag.message = diag.getMessage().str();
99 
100  // Attach any notes to the main diagnostic as related information.
101  std::vector<lsp::DiagnosticRelatedInformation> relatedDiags;
102  for (const ast::Diagnostic &note : diag.getNotes()) {
103  relatedDiags.emplace_back(
104  getLocationFromLoc(sourceMgr, note.getLocation(), uri),
105  note.getMessage().str());
106  }
107  if (!relatedDiags.empty())
108  lspDiag.relatedInformation = std::move(relatedDiags);
109 
110  return lspDiag;
111 }
112 
113 /// Get or extract the documentation for the given decl.
114 static Optional<std::string> getDocumentationFor(llvm::SourceMgr &sourceMgr,
115  const ast::Decl *decl) {
116  // If the decl already had documentation set, use it.
117  if (Optional<StringRef> doc = decl->getDocComment())
118  return doc->str();
119 
120  // If the decl doesn't yet have documentation, try to extract it from the
121  // source file.
122  return lsp::extractSourceDocComment(sourceMgr, decl->getLoc().Start);
123 }
124 
125 //===----------------------------------------------------------------------===//
126 // PDLIndex
127 //===----------------------------------------------------------------------===//
128 
129 namespace {
130 struct PDLIndexSymbol {
131  explicit PDLIndexSymbol(const ast::Decl *definition)
132  : definition(definition) {}
133  explicit PDLIndexSymbol(const ods::Operation *definition)
134  : definition(definition) {}
135 
136  /// Return the location of the definition of this symbol.
137  SMRange getDefLoc() const {
138  if (const ast::Decl *decl = definition.dyn_cast<const ast::Decl *>()) {
139  const ast::Name *declName = decl->getName();
140  return declName ? declName->getLoc() : decl->getLoc();
141  }
142  return definition.get<const ods::Operation *>()->getLoc();
143  }
144 
145  /// The main definition of the symbol.
147  /// The set of references to the symbol.
148  std::vector<SMRange> references;
149 };
150 
151 /// This class provides an index for definitions/uses within a PDL document.
152 /// It provides efficient lookup of a definition given an input source range.
153 class PDLIndex {
154 public:
155  PDLIndex() : intervalMap(allocator) {}
156 
157  /// Initialize the index with the given ast::Module.
158  void initialize(const ast::Module &module, const ods::Context &odsContext);
159 
160  /// Lookup a symbol for the given location. Returns nullptr if no symbol could
161  /// be found. If provided, `overlappedRange` is set to the range that the
162  /// provided `loc` overlapped with.
163  const PDLIndexSymbol *lookup(SMLoc loc,
164  SMRange *overlappedRange = nullptr) const;
165 
166 private:
167  /// The type of interval map used to store source references. SMRange is
168  /// half-open, so we also need to use a half-open interval map.
169  using MapT =
170  llvm::IntervalMap<const char *, const PDLIndexSymbol *,
171  llvm::IntervalMapImpl::NodeSizer<
172  const char *, const PDLIndexSymbol *>::LeafSize,
173  llvm::IntervalMapHalfOpenInfo<const char *>>;
174 
175  /// An allocator for the interval map.
176  MapT::Allocator allocator;
177 
178  /// An interval map containing a corresponding definition mapped to a source
179  /// interval.
180  MapT intervalMap;
181 
182  /// A mapping between definitions and their corresponding symbol.
184 };
185 } // namespace
186 
187 void PDLIndex::initialize(const ast::Module &module,
188  const ods::Context &odsContext) {
189  auto getOrInsertDef = [&](const auto *def) -> PDLIndexSymbol * {
190  auto it = defToSymbol.try_emplace(def, nullptr);
191  if (it.second)
192  it.first->second = std::make_unique<PDLIndexSymbol>(def);
193  return &*it.first->second;
194  };
195  auto insertDeclRef = [&](PDLIndexSymbol *sym, SMRange refLoc,
196  bool isDef = false) {
197  const char *startLoc = refLoc.Start.getPointer();
198  const char *endLoc = refLoc.End.getPointer();
199  if (!intervalMap.overlaps(startLoc, endLoc)) {
200  intervalMap.insert(startLoc, endLoc, sym);
201  if (!isDef)
202  sym->references.push_back(refLoc);
203  }
204  };
205  auto insertODSOpRef = [&](StringRef opName, SMRange refLoc) {
206  const ods::Operation *odsOp = odsContext.lookupOperation(opName);
207  if (!odsOp)
208  return;
209 
210  PDLIndexSymbol *symbol = getOrInsertDef(odsOp);
211  insertDeclRef(symbol, odsOp->getLoc(), /*isDef=*/true);
212  insertDeclRef(symbol, refLoc);
213  };
214 
215  module.walk([&](const ast::Node *node) {
216  // Handle references to PDL decls.
217  if (const auto *decl = dyn_cast<ast::OpNameDecl>(node)) {
218  if (Optional<StringRef> name = decl->getName())
219  insertODSOpRef(*name, decl->getLoc());
220  } else if (const ast::Decl *decl = dyn_cast<ast::Decl>(node)) {
221  const ast::Name *name = decl->getName();
222  if (!name)
223  return;
224  PDLIndexSymbol *declSym = getOrInsertDef(decl);
225  insertDeclRef(declSym, name->getLoc(), /*isDef=*/true);
226 
227  if (const auto *varDecl = dyn_cast<ast::VariableDecl>(decl)) {
228  // Record references to any constraints.
229  for (const auto &it : varDecl->getConstraints())
230  insertDeclRef(getOrInsertDef(it.constraint), it.referenceLoc);
231  }
232  } else if (const auto *expr = dyn_cast<ast::DeclRefExpr>(node)) {
233  insertDeclRef(getOrInsertDef(expr->getDecl()), expr->getLoc());
234  }
235  });
236 }
237 
238 const PDLIndexSymbol *PDLIndex::lookup(SMLoc loc,
239  SMRange *overlappedRange) const {
240  auto it = intervalMap.find(loc.getPointer());
241  if (!it.valid() || loc.getPointer() < it.start())
242  return nullptr;
243 
244  if (overlappedRange) {
245  *overlappedRange = SMRange(SMLoc::getFromPointer(it.start()),
246  SMLoc::getFromPointer(it.stop()));
247  }
248  return it.value();
249 }
250 
251 //===----------------------------------------------------------------------===//
252 // PDLDocument
253 //===----------------------------------------------------------------------===//
254 
255 namespace {
256 /// This class represents all of the information pertaining to a specific PDL
257 /// document.
258 struct PDLDocument {
259  PDLDocument(const lsp::URIForFile &uri, StringRef contents,
260  const std::vector<std::string> &extraDirs,
261  std::vector<lsp::Diagnostic> &diagnostics);
262  PDLDocument(const PDLDocument &) = delete;
263  PDLDocument &operator=(const PDLDocument &) = delete;
264 
265  //===--------------------------------------------------------------------===//
266  // Definitions and References
267  //===--------------------------------------------------------------------===//
268 
269  void getLocationsOf(const lsp::URIForFile &uri, const lsp::Position &defPos,
270  std::vector<lsp::Location> &locations);
271  void findReferencesOf(const lsp::URIForFile &uri, const lsp::Position &pos,
272  std::vector<lsp::Location> &references);
273 
274  //===--------------------------------------------------------------------===//
275  // Document Links
276  //===--------------------------------------------------------------------===//
277 
278  void getDocumentLinks(const lsp::URIForFile &uri,
279  std::vector<lsp::DocumentLink> &links);
280 
281  //===--------------------------------------------------------------------===//
282  // Hover
283  //===--------------------------------------------------------------------===//
284 
285  Optional<lsp::Hover> findHover(const lsp::URIForFile &uri,
286  const lsp::Position &hoverPos);
287  Optional<lsp::Hover> findHover(const ast::Decl *decl,
288  const SMRange &hoverRange);
289  lsp::Hover buildHoverForOpName(const ods::Operation *op,
290  const SMRange &hoverRange);
291  lsp::Hover buildHoverForVariable(const ast::VariableDecl *varDecl,
292  const SMRange &hoverRange);
293  lsp::Hover buildHoverForPattern(const ast::PatternDecl *decl,
294  const SMRange &hoverRange);
295  lsp::Hover buildHoverForCoreConstraint(const ast::CoreConstraintDecl *decl,
296  const SMRange &hoverRange);
297  template <typename T>
298  lsp::Hover buildHoverForUserConstraintOrRewrite(StringRef typeName,
299  const T *decl,
300  const SMRange &hoverRange);
301 
302  //===--------------------------------------------------------------------===//
303  // Document Symbols
304  //===--------------------------------------------------------------------===//
305 
306  void findDocumentSymbols(std::vector<lsp::DocumentSymbol> &symbols);
307 
308  //===--------------------------------------------------------------------===//
309  // Code Completion
310  //===--------------------------------------------------------------------===//
311 
312  lsp::CompletionList getCodeCompletion(const lsp::URIForFile &uri,
313  const lsp::Position &completePos);
314 
315  //===--------------------------------------------------------------------===//
316  // Signature Help
317  //===--------------------------------------------------------------------===//
318 
319  lsp::SignatureHelp getSignatureHelp(const lsp::URIForFile &uri,
320  const lsp::Position &helpPos);
321 
322  //===--------------------------------------------------------------------===//
323  // Inlay Hints
324  //===--------------------------------------------------------------------===//
325 
326  void getInlayHints(const lsp::URIForFile &uri, const lsp::Range &range,
327  std::vector<lsp::InlayHint> &inlayHints);
328  void getInlayHintsFor(const ast::VariableDecl *decl,
329  const lsp::URIForFile &uri,
330  std::vector<lsp::InlayHint> &inlayHints);
331  void getInlayHintsFor(const ast::CallExpr *expr, const lsp::URIForFile &uri,
332  std::vector<lsp::InlayHint> &inlayHints);
333  void getInlayHintsFor(const ast::OperationExpr *expr,
334  const lsp::URIForFile &uri,
335  std::vector<lsp::InlayHint> &inlayHints);
336 
337  /// Add a parameter hint for the given expression using `label`.
338  void addParameterHintFor(std::vector<lsp::InlayHint> &inlayHints,
339  const ast::Expr *expr, StringRef label);
340 
341  //===--------------------------------------------------------------------===//
342  // PDLL ViewOutput
343  //===--------------------------------------------------------------------===//
344 
345  void getPDLLViewOutput(raw_ostream &os, lsp::PDLLViewOutputKind kind);
346 
347  //===--------------------------------------------------------------------===//
348  // Fields
349  //===--------------------------------------------------------------------===//
350 
351  /// The include directories for this file.
352  std::vector<std::string> includeDirs;
353 
354  /// The source manager containing the contents of the input file.
355  llvm::SourceMgr sourceMgr;
356 
357  /// The ODS and AST contexts.
358  ods::Context odsContext;
359  ast::Context astContext;
360 
361  /// The parsed AST module, or failure if the file wasn't valid.
362  FailureOr<ast::Module *> astModule;
363 
364  /// The index of the parsed module.
365  PDLIndex index;
366 
367  /// The set of includes of the parsed module.
368  SmallVector<lsp::SourceMgrInclude> parsedIncludes;
369 };
370 } // namespace
371 
372 PDLDocument::PDLDocument(const lsp::URIForFile &uri, StringRef contents,
373  const std::vector<std::string> &extraDirs,
374  std::vector<lsp::Diagnostic> &diagnostics)
375  : astContext(odsContext) {
376  auto memBuffer = llvm::MemoryBuffer::getMemBufferCopy(contents, uri.file());
377  if (!memBuffer) {
378  lsp::Logger::error("Failed to create memory buffer for file", uri.file());
379  return;
380  }
381 
382  // Build the set of include directories for this file.
383  llvm::SmallString<32> uriDirectory(uri.file());
384  llvm::sys::path::remove_filename(uriDirectory);
385  includeDirs.push_back(uriDirectory.str().str());
386  includeDirs.insert(includeDirs.end(), extraDirs.begin(), extraDirs.end());
387 
388  sourceMgr.setIncludeDirs(includeDirs);
389  sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
390 
391  astContext.getDiagEngine().setHandlerFn([&](const ast::Diagnostic &diag) {
392  if (auto lspDiag = getLspDiagnoticFromDiag(sourceMgr, diag, uri))
393  diagnostics.push_back(std::move(*lspDiag));
394  });
395  astModule = parsePDLLAST(astContext, sourceMgr, /*enableDocumentation=*/true);
396 
397  // Initialize the set of parsed includes.
398  lsp::gatherIncludeFiles(sourceMgr, parsedIncludes);
399 
400  // If we failed to parse the module, there is nothing left to initialize.
401  if (failed(astModule))
402  return;
403 
404  // Prepare the AST index with the parsed module.
405  index.initialize(**astModule, odsContext);
406 }
407 
408 //===----------------------------------------------------------------------===//
409 // PDLDocument: Definitions and References
410 //===----------------------------------------------------------------------===//
411 
412 void PDLDocument::getLocationsOf(const lsp::URIForFile &uri,
413  const lsp::Position &defPos,
414  std::vector<lsp::Location> &locations) {
415  SMLoc posLoc = defPos.getAsSMLoc(sourceMgr);
416  const PDLIndexSymbol *symbol = index.lookup(posLoc);
417  if (!symbol)
418  return;
419 
420  locations.push_back(getLocationFromLoc(sourceMgr, symbol->getDefLoc(), uri));
421 }
422 
423 void PDLDocument::findReferencesOf(const lsp::URIForFile &uri,
424  const lsp::Position &pos,
425  std::vector<lsp::Location> &references) {
426  SMLoc posLoc = pos.getAsSMLoc(sourceMgr);
427  const PDLIndexSymbol *symbol = index.lookup(posLoc);
428  if (!symbol)
429  return;
430 
431  references.push_back(getLocationFromLoc(sourceMgr, symbol->getDefLoc(), uri));
432  for (SMRange refLoc : symbol->references)
433  references.push_back(getLocationFromLoc(sourceMgr, refLoc, uri));
434 }
435 
436 //===--------------------------------------------------------------------===//
437 // PDLDocument: Document Links
438 //===--------------------------------------------------------------------===//
439 
440 void PDLDocument::getDocumentLinks(const lsp::URIForFile &uri,
441  std::vector<lsp::DocumentLink> &links) {
442  for (const lsp::SourceMgrInclude &include : parsedIncludes)
443  links.emplace_back(include.range, include.uri);
444 }
445 
446 //===----------------------------------------------------------------------===//
447 // PDLDocument: Hover
448 //===----------------------------------------------------------------------===//
449 
450 Optional<lsp::Hover> PDLDocument::findHover(const lsp::URIForFile &uri,
451  const lsp::Position &hoverPos) {
452  SMLoc posLoc = hoverPos.getAsSMLoc(sourceMgr);
453 
454  // Check for a reference to an include.
455  for (const lsp::SourceMgrInclude &include : parsedIncludes)
456  if (include.range.contains(hoverPos))
457  return include.buildHover();
458 
459  // Find the symbol at the given location.
460  SMRange hoverRange;
461  const PDLIndexSymbol *symbol = index.lookup(posLoc, &hoverRange);
462  if (!symbol)
463  return llvm::None;
464 
465  // Add hover for operation names.
466  if (const auto *op = symbol->definition.dyn_cast<const ods::Operation *>())
467  return buildHoverForOpName(op, hoverRange);
468  const auto *decl = symbol->definition.get<const ast::Decl *>();
469  return findHover(decl, hoverRange);
470 }
471 
472 Optional<lsp::Hover> PDLDocument::findHover(const ast::Decl *decl,
473  const SMRange &hoverRange) {
474  // Add hover for variables.
475  if (const auto *varDecl = dyn_cast<ast::VariableDecl>(decl))
476  return buildHoverForVariable(varDecl, hoverRange);
477 
478  // Add hover for patterns.
479  if (const auto *patternDecl = dyn_cast<ast::PatternDecl>(decl))
480  return buildHoverForPattern(patternDecl, hoverRange);
481 
482  // Add hover for core constraints.
483  if (const auto *cst = dyn_cast<ast::CoreConstraintDecl>(decl))
484  return buildHoverForCoreConstraint(cst, hoverRange);
485 
486  // Add hover for user constraints.
487  if (const auto *cst = dyn_cast<ast::UserConstraintDecl>(decl))
488  return buildHoverForUserConstraintOrRewrite("Constraint", cst, hoverRange);
489 
490  // Add hover for user rewrites.
491  if (const auto *rewrite = dyn_cast<ast::UserRewriteDecl>(decl))
492  return buildHoverForUserConstraintOrRewrite("Rewrite", rewrite, hoverRange);
493 
494  return llvm::None;
495 }
496 
497 lsp::Hover PDLDocument::buildHoverForOpName(const ods::Operation *op,
498  const SMRange &hoverRange) {
499  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
500  {
501  llvm::raw_string_ostream hoverOS(hover.contents.value);
502  hoverOS << "**OpName**: `" << op->getName() << "`\n***\n"
503  << op->getSummary() << "\n***\n"
504  << op->getDescription();
505  }
506  return hover;
507 }
508 
509 lsp::Hover PDLDocument::buildHoverForVariable(const ast::VariableDecl *varDecl,
510  const SMRange &hoverRange) {
511  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
512  {
513  llvm::raw_string_ostream hoverOS(hover.contents.value);
514  hoverOS << "**Variable**: `" << varDecl->getName().getName() << "`\n***\n"
515  << "Type: `" << varDecl->getType() << "`\n";
516  }
517  return hover;
518 }
519 
520 lsp::Hover PDLDocument::buildHoverForPattern(const ast::PatternDecl *decl,
521  const SMRange &hoverRange) {
522  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
523  {
524  llvm::raw_string_ostream hoverOS(hover.contents.value);
525  hoverOS << "**Pattern**";
526  if (const ast::Name *name = decl->getName())
527  hoverOS << ": `" << name->getName() << "`";
528  hoverOS << "\n***\n";
529  if (Optional<uint16_t> benefit = decl->getBenefit())
530  hoverOS << "Benefit: " << *benefit << "\n";
531  if (decl->hasBoundedRewriteRecursion())
532  hoverOS << "HasBoundedRewriteRecursion\n";
533  hoverOS << "RootOp: `"
534  << decl->getRootRewriteStmt()->getRootOpExpr()->getType() << "`\n";
535 
536  // Format the documentation for the decl.
537  if (Optional<std::string> doc = getDocumentationFor(sourceMgr, decl))
538  hoverOS << "\n" << *doc << "\n";
539  }
540  return hover;
541 }
542 
544 PDLDocument::buildHoverForCoreConstraint(const ast::CoreConstraintDecl *decl,
545  const SMRange &hoverRange) {
546  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
547  {
548  llvm::raw_string_ostream hoverOS(hover.contents.value);
549  hoverOS << "**Constraint**: `";
551  .Case([&](const ast::AttrConstraintDecl *) { hoverOS << "Attr"; })
552  .Case([&](const ast::OpConstraintDecl *opCst) {
553  hoverOS << "Op";
554  if (Optional<StringRef> name = opCst->getName())
555  hoverOS << "<" << name << ">";
556  })
557  .Case([&](const ast::TypeConstraintDecl *) { hoverOS << "Type"; })
558  .Case([&](const ast::TypeRangeConstraintDecl *) {
559  hoverOS << "TypeRange";
560  })
561  .Case([&](const ast::ValueConstraintDecl *) { hoverOS << "Value"; })
562  .Case([&](const ast::ValueRangeConstraintDecl *) {
563  hoverOS << "ValueRange";
564  });
565  hoverOS << "`\n";
566  }
567  return hover;
568 }
569 
570 template <typename T>
571 lsp::Hover PDLDocument::buildHoverForUserConstraintOrRewrite(
572  StringRef typeName, const T *decl, const SMRange &hoverRange) {
573  lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
574  {
575  llvm::raw_string_ostream hoverOS(hover.contents.value);
576  hoverOS << "**" << typeName << "**: `" << decl->getName().getName()
577  << "`\n***\n";
578  ArrayRef<ast::VariableDecl *> inputs = decl->getInputs();
579  if (!inputs.empty()) {
580  hoverOS << "Parameters:\n";
581  for (const ast::VariableDecl *input : inputs)
582  hoverOS << "* " << input->getName().getName() << ": `"
583  << input->getType() << "`\n";
584  hoverOS << "***\n";
585  }
586  ast::Type resultType = decl->getResultType();
587  if (auto resultTupleTy = resultType.dyn_cast<ast::TupleType>()) {
588  if (!resultTupleTy.empty()) {
589  hoverOS << "Results:\n";
590  for (auto it : llvm::zip(resultTupleTy.getElementNames(),
591  resultTupleTy.getElementTypes())) {
592  StringRef name = std::get<0>(it);
593  hoverOS << "* " << (name.empty() ? "" : (name + ": ")) << "`"
594  << std::get<1>(it) << "`\n";
595  }
596  hoverOS << "***\n";
597  }
598  } else {
599  hoverOS << "Results:\n* `" << resultType << "`\n";
600  hoverOS << "***\n";
601  }
602 
603  // Format the documentation for the decl.
604  if (Optional<std::string> doc = getDocumentationFor(sourceMgr, decl))
605  hoverOS << "\n" << *doc << "\n";
606  }
607  return hover;
608 }
609 
610 //===----------------------------------------------------------------------===//
611 // PDLDocument: Document Symbols
612 //===----------------------------------------------------------------------===//
613 
614 void PDLDocument::findDocumentSymbols(
615  std::vector<lsp::DocumentSymbol> &symbols) {
616  if (failed(astModule))
617  return;
618 
619  for (const ast::Decl *decl : (*astModule)->getChildren()) {
620  if (!isMainFileLoc(sourceMgr, decl->getLoc()))
621  continue;
622 
623  if (const auto *patternDecl = dyn_cast<ast::PatternDecl>(decl)) {
624  const ast::Name *name = patternDecl->getName();
625 
626  SMRange nameLoc = name ? name->getLoc() : patternDecl->getLoc();
627  SMRange bodyLoc(nameLoc.Start, patternDecl->getBody()->getLoc().End);
628 
629  symbols.emplace_back(
630  name ? name->getName() : "<pattern>", lsp::SymbolKind::Class,
631  lsp::Range(sourceMgr, bodyLoc), lsp::Range(sourceMgr, nameLoc));
632  } else if (const auto *cDecl = dyn_cast<ast::UserConstraintDecl>(decl)) {
633  // TODO: Add source information for the code block body.
634  SMRange nameLoc = cDecl->getName().getLoc();
635  SMRange bodyLoc = nameLoc;
636 
637  symbols.emplace_back(
638  cDecl->getName().getName(), lsp::SymbolKind::Function,
639  lsp::Range(sourceMgr, bodyLoc), lsp::Range(sourceMgr, nameLoc));
640  } else if (const auto *cDecl = dyn_cast<ast::UserRewriteDecl>(decl)) {
641  // TODO: Add source information for the code block body.
642  SMRange nameLoc = cDecl->getName().getLoc();
643  SMRange bodyLoc = nameLoc;
644 
645  symbols.emplace_back(
646  cDecl->getName().getName(), lsp::SymbolKind::Function,
647  lsp::Range(sourceMgr, bodyLoc), lsp::Range(sourceMgr, nameLoc));
648  }
649  }
650 }
651 
652 //===----------------------------------------------------------------------===//
653 // PDLDocument: Code Completion
654 //===----------------------------------------------------------------------===//
655 
656 namespace {
657 class LSPCodeCompleteContext : public CodeCompleteContext {
658 public:
659  LSPCodeCompleteContext(SMLoc completeLoc, llvm::SourceMgr &sourceMgr,
660  lsp::CompletionList &completionList,
661  ods::Context &odsContext,
662  ArrayRef<std::string> includeDirs)
663  : CodeCompleteContext(completeLoc), sourceMgr(sourceMgr),
664  completionList(completionList), odsContext(odsContext),
665  includeDirs(includeDirs) {}
666 
667  void codeCompleteTupleMemberAccess(ast::TupleType tupleType) final {
668  ArrayRef<ast::Type> elementTypes = tupleType.getElementTypes();
669  ArrayRef<StringRef> elementNames = tupleType.getElementNames();
670  for (unsigned i = 0, e = tupleType.size(); i < e; ++i) {
671  // Push back a completion item that uses the result index.
672  lsp::CompletionItem item;
673  item.label = llvm::formatv("{0} (field #{0})", i).str();
674  item.insertText = Twine(i).str();
675  item.filterText = item.sortText = item.insertText;
677  item.detail = llvm::formatv("{0}: {1}", i, elementTypes[i]);
679  completionList.items.emplace_back(item);
680 
681  // If the element has a name, push back a completion item with that name.
682  if (!elementNames[i].empty()) {
683  item.label =
684  llvm::formatv("{1} (field #{0})", i, elementNames[i]).str();
685  item.filterText = item.label;
686  item.insertText = elementNames[i].str();
687  completionList.items.emplace_back(item);
688  }
689  }
690  }
691 
692  void codeCompleteOperationMemberAccess(ast::OperationType opType) final {
693  const ods::Operation *odsOp = opType.getODSOperation();
694  if (!odsOp)
695  return;
696 
697  ArrayRef<ods::OperandOrResult> results = odsOp->getResults();
698  for (const auto &it : llvm::enumerate(results)) {
699  const ods::OperandOrResult &result = it.value();
700  const ods::TypeConstraint &constraint = result.getConstraint();
701 
702  // Push back a completion item that uses the result index.
703  lsp::CompletionItem item;
704  item.label = llvm::formatv("{0} (field #{0})", it.index()).str();
705  item.insertText = Twine(it.index()).str();
706  item.filterText = item.sortText = item.insertText;
708  switch (result.getVariableLengthKind()) {
710  item.detail = llvm::formatv("{0}: Value", it.index()).str();
711  break;
713  item.detail = llvm::formatv("{0}: Value?", it.index()).str();
714  break;
716  item.detail = llvm::formatv("{0}: ValueRange", it.index()).str();
717  break;
718  }
721  llvm::formatv("{0}\n\n```c++\n{1}\n```\n", constraint.getSummary(),
722  constraint.getCppClass())
723  .str()};
725  completionList.items.emplace_back(item);
726 
727  // If the result has a name, push back a completion item with the result
728  // name.
729  if (!result.getName().empty()) {
730  item.label =
731  llvm::formatv("{1} (field #{0})", it.index(), result.getName())
732  .str();
733  item.filterText = item.label;
734  item.insertText = result.getName().str();
735  completionList.items.emplace_back(item);
736  }
737  }
738  }
739 
740  void codeCompleteOperationAttributeName(StringRef opName) final {
741  const ods::Operation *odsOp = odsContext.lookupOperation(opName);
742  if (!odsOp)
743  return;
744 
745  for (const ods::Attribute &attr : odsOp->getAttributes()) {
746  const ods::AttributeConstraint &constraint = attr.getConstraint();
747 
748  lsp::CompletionItem item;
749  item.label = attr.getName().str();
751  item.detail = attr.isOptional() ? "optional" : "";
754  llvm::formatv("{0}\n\n```c++\n{1}\n```\n", constraint.getSummary(),
755  constraint.getCppClass())
756  .str()};
758  completionList.items.emplace_back(item);
759  }
760  }
761 
762  void codeCompleteConstraintName(ast::Type currentType,
763  bool allowNonCoreConstraints,
764  bool allowInlineTypeConstraints,
765  const ast::DeclScope *scope) final {
766  auto addCoreConstraint = [&](StringRef constraint, StringRef mlirType,
767  StringRef snippetText = "") {
768  lsp::CompletionItem item;
769  item.label = constraint.str();
771  item.detail = (constraint + " constraint").str();
774  ("A single entity core constraint of type `" + mlirType + "`").str()};
775  item.sortText = "0";
776  item.insertText = snippetText.str();
777  item.insertTextFormat = snippetText.empty()
780  completionList.items.emplace_back(item);
781  };
782 
783  // Insert completions for the core constraints. Some core constraints have
784  // additional characteristics, so we may add then even if a type has been
785  // inferred.
786  if (!currentType) {
787  addCoreConstraint("Attr", "mlir::Attribute");
788  addCoreConstraint("Op", "mlir::Operation *");
789  addCoreConstraint("Value", "mlir::Value");
790  addCoreConstraint("ValueRange", "mlir::ValueRange");
791  addCoreConstraint("Type", "mlir::Type");
792  addCoreConstraint("TypeRange", "mlir::TypeRange");
793  }
794  if (allowInlineTypeConstraints) {
795  /// Attr<Type>.
796  if (!currentType || currentType.isa<ast::AttributeType>())
797  addCoreConstraint("Attr<type>", "mlir::Attribute", "Attr<$1>");
798  /// Value<Type>.
799  if (!currentType || currentType.isa<ast::ValueType>())
800  addCoreConstraint("Value<type>", "mlir::Value", "Value<$1>");
801  /// ValueRange<TypeRange>.
802  if (!currentType || currentType.isa<ast::ValueRangeType>())
803  addCoreConstraint("ValueRange<type>", "mlir::ValueRange",
804  "ValueRange<$1>");
805  }
806 
807  // If a scope was provided, check it for potential constraints.
808  while (scope) {
809  for (const ast::Decl *decl : scope->getDecls()) {
810  if (const auto *cst = dyn_cast<ast::UserConstraintDecl>(decl)) {
811  if (!allowNonCoreConstraints)
812  continue;
813 
814  lsp::CompletionItem item;
815  item.label = cst->getName().getName().str();
817  item.sortText = "2_" + item.label;
818 
819  // Skip constraints that are not single-arg. We currently only
820  // complete variable constraints.
821  if (cst->getInputs().size() != 1)
822  continue;
823 
824  // Ensure the input type matched the given type.
825  ast::Type constraintType = cst->getInputs()[0]->getType();
826  if (currentType && !currentType.refineWith(constraintType))
827  continue;
828 
829  // Format the constraint signature.
830  {
831  llvm::raw_string_ostream strOS(item.detail);
832  strOS << "(";
833  llvm::interleaveComma(
834  cst->getInputs(), strOS, [&](const ast::VariableDecl *var) {
835  strOS << var->getName().getName() << ": " << var->getType();
836  });
837  strOS << ") -> " << cst->getResultType();
838  }
839 
840  // Format the documentation for the constraint.
841  if (Optional<std::string> doc = getDocumentationFor(sourceMgr, cst)) {
842  item.documentation =
844  }
845 
846  completionList.items.emplace_back(item);
847  }
848  }
849 
850  scope = scope->getParentScope();
851  }
852  }
853 
854  void codeCompleteDialectName() final {
855  // Code complete known dialects.
856  for (const ods::Dialect &dialect : odsContext.getDialects()) {
857  lsp::CompletionItem item;
858  item.label = dialect.getName().str();
861  completionList.items.emplace_back(item);
862  }
863  }
864 
865  void codeCompleteOperationName(StringRef dialectName) final {
866  const ods::Dialect *dialect = odsContext.lookupDialect(dialectName);
867  if (!dialect)
868  return;
869 
870  for (const auto &it : dialect->getOperations()) {
871  const ods::Operation &op = *it.second;
872 
873  lsp::CompletionItem item;
874  item.label = op.getName().drop_front(dialectName.size() + 1).str();
877  completionList.items.emplace_back(item);
878  }
879  }
880 
881  void codeCompletePatternMetadata() final {
882  auto addSimpleConstraint = [&](StringRef constraint, StringRef desc,
883  StringRef snippetText = "") {
884  lsp::CompletionItem item;
885  item.label = constraint.str();
887  item.detail = "pattern metadata";
888  item.documentation =
890  item.insertText = snippetText.str();
891  item.insertTextFormat = snippetText.empty()
894  completionList.items.emplace_back(item);
895  };
896 
897  addSimpleConstraint("benefit", "The `benefit` of matching the pattern.",
898  "benefit($1)");
899  addSimpleConstraint("recursion",
900  "The pattern properly handles recursive application.");
901  }
902 
903  void codeCompleteIncludeFilename(StringRef curPath) final {
904  // Normalize the path to allow for interacting with the file system
905  // utilities.
906  SmallString<128> nativeRelDir(llvm::sys::path::convert_to_slash(curPath));
907  llvm::sys::path::native(nativeRelDir);
908 
909  // Set of already included completion paths.
910  StringSet<> seenResults;
911 
912  // Functor used to add a single include completion item.
913  auto addIncludeCompletion = [&](StringRef path, bool isDirectory) {
914  lsp::CompletionItem item;
915  item.label = path.str();
916  item.kind = isDirectory ? lsp::CompletionItemKind::Folder
918  if (seenResults.insert(item.label).second)
919  completionList.items.emplace_back(item);
920  };
921 
922  // Process the include directories for this file, adding any potential
923  // nested include files or directories.
924  for (StringRef includeDir : includeDirs) {
925  llvm::SmallString<128> dir = includeDir;
926  if (!nativeRelDir.empty())
927  llvm::sys::path::append(dir, nativeRelDir);
928 
929  std::error_code errorCode;
930  for (auto it = llvm::sys::fs::directory_iterator(dir, errorCode),
931  e = llvm::sys::fs::directory_iterator();
932  !errorCode && it != e; it.increment(errorCode)) {
933  StringRef filename = llvm::sys::path::filename(it->path());
934 
935  // To know whether a symlink should be treated as file or a directory,
936  // we have to stat it. This should be cheap enough as there shouldn't be
937  // many symlinks.
938  llvm::sys::fs::file_type fileType = it->type();
939  if (fileType == llvm::sys::fs::file_type::symlink_file) {
940  if (auto fileStatus = it->status())
941  fileType = fileStatus->type();
942  }
943 
944  switch (fileType) {
945  case llvm::sys::fs::file_type::directory_file:
946  addIncludeCompletion(filename, /*isDirectory=*/true);
947  break;
948  case llvm::sys::fs::file_type::regular_file: {
949  // Only consider concrete files that can actually be included by PDLL.
950  if (filename.endswith(".pdll") || filename.endswith(".td"))
951  addIncludeCompletion(filename, /*isDirectory=*/false);
952  break;
953  }
954  default:
955  break;
956  }
957  }
958  }
959 
960  // Sort the completion results to make sure the output is deterministic in
961  // the face of different iteration schemes for different platforms.
962  llvm::sort(completionList.items, [](const lsp::CompletionItem &lhs,
963  const lsp::CompletionItem &rhs) {
964  return lhs.label < rhs.label;
965  });
966  }
967 
968 private:
969  llvm::SourceMgr &sourceMgr;
970  lsp::CompletionList &completionList;
971  ods::Context &odsContext;
972  ArrayRef<std::string> includeDirs;
973 };
974 } // namespace
975 
977 PDLDocument::getCodeCompletion(const lsp::URIForFile &uri,
978  const lsp::Position &completePos) {
979  SMLoc posLoc = completePos.getAsSMLoc(sourceMgr);
980  if (!posLoc.isValid())
981  return lsp::CompletionList();
982 
983  // To perform code completion, we run another parse of the module with the
984  // code completion context provided.
985  ods::Context tmpODSContext;
986  lsp::CompletionList completionList;
987  LSPCodeCompleteContext lspCompleteContext(posLoc, sourceMgr, completionList,
988  tmpODSContext,
989  sourceMgr.getIncludeDirs());
990 
991  ast::Context tmpContext(tmpODSContext);
992  (void)parsePDLLAST(tmpContext, sourceMgr, /*enableDocumentation=*/true,
993  &lspCompleteContext);
994 
995  return completionList;
996 }
997 
998 //===----------------------------------------------------------------------===//
999 // PDLDocument: Signature Help
1000 //===----------------------------------------------------------------------===//
1001 
1002 namespace {
1003 class LSPSignatureHelpContext : public CodeCompleteContext {
1004 public:
1005  LSPSignatureHelpContext(SMLoc completeLoc, llvm::SourceMgr &sourceMgr,
1006  lsp::SignatureHelp &signatureHelp,
1007  ods::Context &odsContext)
1008  : CodeCompleteContext(completeLoc), sourceMgr(sourceMgr),
1009  signatureHelp(signatureHelp), odsContext(odsContext) {}
1010 
1011  void codeCompleteCallSignature(const ast::CallableDecl *callable,
1012  unsigned currentNumArgs) final {
1013  signatureHelp.activeParameter = currentNumArgs;
1014 
1015  lsp::SignatureInformation signatureInfo;
1016  {
1017  llvm::raw_string_ostream strOS(signatureInfo.label);
1018  strOS << callable->getName()->getName() << "(";
1019  auto formatParamFn = [&](const ast::VariableDecl *var) {
1020  unsigned paramStart = strOS.str().size();
1021  strOS << var->getName().getName() << ": " << var->getType();
1022  unsigned paramEnd = strOS.str().size();
1023  signatureInfo.parameters.emplace_back(lsp::ParameterInformation{
1024  StringRef(strOS.str()).slice(paramStart, paramEnd).str(),
1025  std::make_pair(paramStart, paramEnd), /*paramDoc*/ std::string()});
1026  };
1027  llvm::interleaveComma(callable->getInputs(), strOS, formatParamFn);
1028  strOS << ") -> " << callable->getResultType();
1029  }
1030 
1031  // Format the documentation for the callable.
1032  if (Optional<std::string> doc = getDocumentationFor(sourceMgr, callable))
1033  signatureInfo.documentation = std::move(*doc);
1034 
1035  signatureHelp.signatures.emplace_back(std::move(signatureInfo));
1036  }
1037 
1038  void
1039  codeCompleteOperationOperandsSignature(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() : llvm::None,
1045  currentNumOperands, "operand", "Value");
1046  }
1047 
1048  void codeCompleteOperationResultsSignature(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() : llvm::None,
1054  currentNumResults, "result", "Type");
1055  }
1056 
1057  void codeCompleteOperationOperandOrResultSignature(
1058  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.
1189  .Case<ast::VariableDecl, ast::CallExpr, ast::OperationExpr>(
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 
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.
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 
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  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 Optional<lsp::Hover> PDLTextFile::findHover(const lsp::URIForFile &uri,
1505  lsp::Position hoverPos) {
1506  PDLTextFileChunk &chunk = getChunkFor(hoverPos);
1507  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) + ">",
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 
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 
1737  auto it = impl->files.find(uri.file());
1738  if (it == impl->files.end())
1739  return llvm::None;
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 
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 llvm::None;
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 
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 llvm::None;
1821 }
void findDocumentSymbols(const URIForFile &uri, std::vector< DocumentSymbol > &symbols)
Find all of the document symbols within the given file.
StringRef file() const
Returns the absolute path to the file.
Definition: Protocol.h:113
Include the generated interface declarations.
ArrayRef< OperandOrResult > getOperands() const
Returns the operands of this operation.
Definition: Operation.h:165
Optional< int64_t > removeDocument(const URIForFile &uri)
Remove the document with the given uri.
StringRef getName() const
Returns the name of the operation.
Definition: Operation.h:150
Documents should not be synced at all.
MutableArrayRef< Expr * > getArguments()
Return the arguments of this call.
Definition: Nodes.h:398
static std::string diag(llvm::Value &v)
This class represents a PDLL type that corresponds to an mlir::Value.
Definition: Types.h:283
static void error(const char *fmt, Ts &&...vals)
Definition: Logging.h:42
This class represents the main context of the PDLL AST.
Definition: Context.h:25
This class represents a generic ODS Attribute constraint.
Definition: Constraint.h:63
Type getType() const
Return the type of the decl.
Definition: Nodes.h:1216
auto getDecls() const
Return all of the decls within this scope.
Definition: Nodes.h:73
StringRef getMessage() const
Return the message of this diagnostic.
Definition: Diagnostic.h:37
StringRef getSummary() const
Returns the summary of the operation.
Definition: Operation.h:153
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.
Optional< StringRef > getName() const
Return the name of the operation, or None if there isn&#39;t one.
Definition: Nodes.cpp:388
std::string label
The label of this hint.
Definition: Protocol.h:1084
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
bool paddingRight
Render padding after the hint.
Definition: Protocol.h:1102
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:3118
StringRef getCppClass() const
Return the name of the underlying c++ class of this constraint.
Definition: Constraint.h:66
MutableArrayRef< Expr * > getResultTypes()
Return the result types of this operation.
Definition: Nodes.h:530
U dyn_cast() const
Definition: Types.h:75
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value...
Definition: LogicalResult.h:72
This class represents a PDLL tuple type, i.e.
Definition: Types.h:242
This class represents a generic ODS Type constraint.
Definition: Constraint.h:84
DiagnosticSeverity severity
The diagnostic&#39;s severity.
Definition: Protocol.h:667
The class represents an Operation constraint, and constrains a variable to be an Operation.
Definition: Nodes.h:727
Optional< std::string > category
The diagnostic&#39;s category.
Definition: Protocol.h:684
std::vector< CompletionItem > items
The completion items.
Definition: Protocol.h:874
auto getNotes() const
Return the notes of this diagnostic.
Definition: Diagnostic.h:43
bool isa() const
Provide type casting support.
Definition: Types.h:66
PDLLServer(const Options &options)
URI in "file" scheme for a file.
Definition: Protocol.h:100
Optional< TextEdit > textEdit
An edit which is applied to a document when selecting this completion.
Definition: Protocol.h:847
Expr * getInitExpr() const
Return the initializer expression of this statement, or nullptr if there was no initializer.
Definition: Nodes.h:1210
void getInlayHints(const URIForFile &uri, const Range &range, std::vector< InlayHint > &inlayHints)
Get the inlay hints for the range within the given file.
StringRef getName() const
Return the name of this value.
Definition: Operation.h:77
Range range
The range enclosing this symbol not including leading/trailing whitespace but everything else like co...
Definition: Protocol.h:598
void findReferencesOf(const URIForFile &uri, const Position &pos, std::vector< Location > &references)
Find all references of the object pointed at by the given position.
static std::string toString(bytecode::Section::ID sectionID)
Stringify the given section ID.
CompletionList getCodeCompletion(const URIForFile &uri, const Position &completePos)
Get the code completion list for the position within the given file.
std::vector< ParameterInformation > parameters
The parameters of this signature.
Definition: Protocol.h:955
An inlay hint that for a type annotation.
SMRange getLoc() const
Return the source location of this operation.
Definition: Operation.h:128
This class contains all of the registered ODS operation classes.
Definition: Context.h:32
VariableLengthKind getVariableLengthKind() const
Returns the variable length kind of this value.
Definition: Operation.h:92
std::string documentation
The documentation of this signature. Optional.
Definition: Protocol.h:952
SMRange getLoc() const
Return the location of this node.
Definition: Nodes.h:130
Expr * getRootOpExpr() const
Return the root operation of this rewrite.
Definition: Nodes.h:236
MutableArrayRef< Expr * > getOperands()
Return the operands of this operation.
Definition: Nodes.h:522
static constexpr const bool value
Optional< uint16_t > getBenefit() const
Return the benefit of this pattern if specified, or None.
Definition: Nodes.h:998
static Optional< std::string > getDocumentationFor(llvm::SourceMgr &sourceMgr, const ast::Decl *decl)
Get or extract the documentation for the given decl.
Definition: PDLLServer.cpp:114
This class represents the base Decl node.
Definition: Nodes.h:625
The class represents an Attribute constraint, and constrains a variable to be an Attribute.
Definition: Nodes.h:704
std::string output
The string representation of the output.
Definition: Protocol.h:60
This class represents a single include within a root file.
This class contains a collection of compilation information for files provided to the language server...
LogicalResult applyTo(std::string &contents) const
Try to apply this change to the given contents string.
Definition: Protocol.cpp:497
This class represents a scope for named AST decls.
Definition: Nodes.h:63
StringRef getName() const
Return the raw string name.
Definition: Nodes.h:40
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:56
std::string source
A human-readable string describing the source of this diagnostic, e.g.
Definition: Protocol.h:671
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:84
Type getType() const
Return the type of this expression.
Definition: Nodes.h:347
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
This expression represents a reference to a Decl node.
Definition: Nodes.h:424
This class provides support for representing a failure result, or a valid value of type T...
Definition: LogicalResult.h:78
Type refineWith(Type other) const
Try to refine this type with the one provided.
Definition: Types.cpp:32
StringRef getSummary() const
Return the summary of this constraint.
Definition: Constraint.h:44
auto getDialects() const
Return a range of all of the registered dialects.
Definition: Context.h:57
The class represents a ValueRange constraint, and constrains a variable to be a ValueRange.
Definition: Nodes.h:802
const Options & options
PDLL LSP options.
std::string insertText
A string that should be inserted to a document when selecting this completion.
Definition: Protocol.h:836
This class represents the base of all "core" constraints.
Definition: Nodes.h:689
int activeParameter
The active parameter of the active signature.
Definition: Protocol.h:975
int line
Line position in a document (zero-based).
Definition: Protocol.h:274
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:232
const Name * getName() const
Return the name of the decl, or nullptr if it doesn&#39;t have one.
Definition: Nodes.h:628
Represents the result of viewing the output of a PDLL file.
Definition: Protocol.h:58
Optional< MarkupContent > documentation
A human-readable string that represents a doc-comment.
Definition: Protocol.h:824
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:72
size_t size() const
Return the number of elements within this tuple.
Definition: Types.h:259
This class represents a PDLL type that corresponds to an mlir::Attribute.
Definition: Types.h:130
ArrayRef< OperandOrResult > getResults() const
Returns the results of this operation.
Definition: Operation.h:168
This class provides a simple implementation of a PDLL diagnostic.
Definition: Diagnostic.h:29
Position end
The range&#39;s end position.
Definition: Protocol.h:326
Represents a collection of completion items to be presented in the editor.
Definition: Protocol.h:868
Expr * getCallableExpr() const
Return the callable of this call.
Definition: Nodes.h:395
SMRange getLocation() const
Return the location of this diagnostic.
Definition: Diagnostic.h:40
This Decl represents the definition of a PDLL variable.
Definition: Nodes.h:1192
ArrayRef< VariableDecl * > getInputs() const
Return the inputs of this decl.
Definition: Nodes.h:1151
Optional< Hover > findHover(const URIForFile &uri, const Position &hoverPos)
Find a hover description for the given hover position, or None if one couldn&#39;t be found...
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:55
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:345
MutableArrayRef< ConstraintRef > getConstraints()
Return the constraints of this variable.
Definition: Nodes.h:1201
MarkupContent contents
The hover&#39;s content.
Definition: Protocol.h:526
This expression represents the structural form of an MLIR Operation.
Definition: Nodes.h:499
This class represents a base AST node.
Definition: Nodes.h:107
std::vector< DocumentSymbol > children
Children of this symbol, e.g. properties of a class.
Definition: Protocol.h:605
std::string message
The diagnostic&#39;s message.
Definition: Protocol.h:674
Inlay hint information.
Definition: Protocol.h:1074
The class represents a Value constraint, and constrains a variable to be a Value. ...
Definition: Nodes.h:780
This class is a utility diagnostic handler for use with llvm::SourceMgr.
Definition: Diagnostics.h:548
This decl represents a shared interface for all callable decls.
Definition: Nodes.h:1140
void getLocationsOf(const URIForFile &uri, const Position &defPos, std::vector< Location > &locations)
Return the locations of the object pointed at by the given position.
static void rewrite(DataFlowSolver &solver, MLIRContext *context, MutableArrayRef< Region > initialRegions)
Rewrite the given regions using the computing analysis.
Definition: SCCP.cpp:67
std::string label
The label of this signature. Mandatory.
Definition: Protocol.h:949
std::vector< SignatureInformation > signatures
The resulting signatures.
Definition: Protocol.h:969
An inlay hint that is for a parameter.
Position start
The range&#39;s start position.
Definition: Protocol.h:323
static llvm::ManagedStatic< PassManagerOptions > options
Optional< std::vector< DiagnosticRelatedInformation > > relatedInformation
An array of related diagnostic information, e.g.
Definition: Protocol.h:678
std::string filterText
A string that should be used when filtering a set of completion items.
Definition: Protocol.h:832
URIForFile uri
The text document&#39;s URI.
Definition: Protocol.h:375
This class provides an abstract interface into the parser for hooking in code completion events...
Definition: CodeComplete.h:29
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:39
std::string label
The label of this completion item.
Definition: Protocol.h:813
llvm::StringMap< std::unique_ptr< PDLTextFile > > files
The files held by the server, mapped by their URI file name.
A single parameter of a particular signature.
Definition: Protocol.h:927
std::string detail
A human-readable string with additional information about this item, like type or symbol information...
Definition: Protocol.h:821
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:57
Set of flags used to control the behavior of the various IR print methods (e.g.
This class provides an ODS representation of a specific operation attribute.
Definition: Operation.h:39
The class represents a Type constraint, and constrains a variable to be a Type.
Definition: Nodes.h:752
void gatherIncludeFiles(llvm::SourceMgr &sourceMgr, SmallVectorImpl< SourceMgrInclude > &includes)
Given a source manager, gather all of the processed include files.
void codegenPDLLToCPP(const ast::Module &astModule, ModuleOp module, raw_ostream &os)
Definition: CPPGen.cpp:249
ArrayRef< StringRef > getElementNames() const
Return the element names of this tuple.
Definition: Types.cpp:153
This class represents an ODS dialect, and contains information on the constructs held within the dial...
Definition: Dialect.h:26
This class provides an ODS representation of a specific operation.
Definition: Operation.h:125
Range range
The range of the text document to be manipulated.
Definition: Protocol.h:718
Impl(const Options &options)
This class provides a convenient API for interacting with source names.
Definition: Nodes.h:36
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:60
Range range
The source range where the message applies.
Definition: Protocol.h:663
Optional< std::string > extractSourceDocComment(llvm::SourceMgr &sourceMgr, SMLoc loc)
Extract a documentation comment for the given location within the source manager. ...
Range selectionRange
The range that should be selected and revealed when this symbol is being picked, e.g the name of a function.
Definition: Protocol.h:602
StringRef getDescription() const
Returns the description of the operation.
Definition: Operation.h:156
Optional< PDLLViewOutputResult > getPDLLViewOutput(const URIForFile &uri, PDLLViewOutputKind kind)
Get the output of the given PDLL file, or None if there is no valid output.
This Decl represents a single Pattern.
Definition: Nodes.h:990
DeclScope * getParentScope()
Return the parent scope of this scope, or nullptr if there is no parent.
Definition: Nodes.h:69
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.
Optional< StringRef > getDocComment() const
Return the documentation comment attached to this decl if it has been set.
Definition: Nodes.h:638
bool hasBoundedRewriteRecursion() const
Return if this pattern has bounded rewrite recursion.
Definition: Nodes.h:1001
StringRef getCppClass() const
Return the name of the underlying c++ class of this constraint.
Definition: Constraint.h:87
void getDocumentLinks(const URIForFile &uri, std::vector< DocumentLink > &documentLinks)
Return the document links referenced by the given file.
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:56
PDLLViewOutputKind
The type of output to view from PDLL.
Definition: Protocol.h:32
lsp::CompilationDatabase compilationDatabase
The compilation database containing additional information for files passed to the server...
SignatureHelp getSignatureHelp(const URIForFile &uri, const Position &helpPos)
Get the signature help for the position within the given file.
This class provides an ODS representation of a specific operation operand or result.
Definition: Operation.h:74
Represents the signature of a callable.
Definition: Protocol.h:967
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:607
Type getResultType() const
Return the result type of this decl.
Definition: Nodes.h:1158
InsertTextFormat insertTextFormat
The format of the insert text.
Definition: Protocol.h:840
static 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:67
static bool shouldAddHintFor(const ast::Expr *expr, StringRef name)
Returns true if the given name should be added as a hint for expr.
const OpRewriteStmt * getRootRewriteStmt() const
Return the root rewrite statement of this pattern.
Definition: Nodes.h:1007
const Name & getName() const
Return the name of the decl.
Definition: Nodes.h:1213
Represents programming constructs like variables, classes, interfaces etc.
Definition: Protocol.h:577
const TypeConstraint & getConstraint() const
Return the constraint of this value.
Definition: Operation.h:97
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:297
std::vector< TextEdit > additionalTextEdits
An optional array of additional text edits that are applied when selecting this completion.
Definition: Protocol.h:852
The class represents a TypeRange constraint, and constrains a variable to be a TypeRange.
Definition: Nodes.h:766
Severity getSeverity() const
Return the severity of this diagnostic.
Definition: Diagnostic.h:34
This class represents a top-level AST module.
Definition: Nodes.h:1242
Represents the signature of something callable.
Definition: Protocol.h:947
ArrayRef< Attribute > getAttributes() const
Returns the attributes of this operation.
Definition: Operation.h:162
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
std::string sortText
A string that should be used when comparing this item with other items.
Definition: Protocol.h:828
This class represents a PDLL type that corresponds to an mlir::ValueRange.
Definition: Types.h:211
SMRange getLoc() const
Get the location of this name.
Definition: Nodes.h:43
This class represents a base AST Expression node.
Definition: Nodes.h:344
This class represents a PDLL type that corresponds to an mlir::Operation.
Definition: Types.h:157
void walk(function_ref< void(const Node *)> walkFn) const
Walk all of the nodes including, and nested under, this node in pre-order.
Definition: Nodes.cpp:162
ArrayRef< Type > getElementTypes() const
Return the element types of this tuple.
Definition: Types.cpp:149
CompletionItemKind kind
The kind of this completion item.
Definition: Protocol.h:817
This expression represents a call to a decl, such as a UserConstraintDecl/UserRewriteDecl.
Definition: Nodes.h:388
bool contains(SMRange range, SMLoc loc)
Returns true if the given range contains the given source location.