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"
16 #include "mlir/Tools/PDLL/AST/Context.h"
17 #include "mlir/Tools/PDLL/AST/Nodes.h"
18 #include "mlir/Tools/PDLL/AST/Types.h"
19 #include "mlir/Tools/PDLL/CodeGen/CPPGen.h"
20 #include "mlir/Tools/PDLL/CodeGen/MLIRGen.h"
21 #include "mlir/Tools/PDLL/ODS/Constraint.h"
22 #include "mlir/Tools/PDLL/ODS/Context.h"
23 #include "mlir/Tools/PDLL/ODS/Dialect.h"
24 #include "mlir/Tools/PDLL/ODS/Operation.h"
25 #include "mlir/Tools/PDLL/Parser/CodeComplete.h"
26 #include "mlir/Tools/PDLL/Parser/Parser.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;
44   llvm::Expected<lsp::URIForFile> fileForLoc = lsp::URIForFile::fromFile(
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.
66 static Optional<lsp::Diagnostic>
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:
89     lspDiag.severity = lsp::DiagnosticSeverity::Warning;
90     break;
91   case ast::Diagnostic::Severity::DK_Error:
92     lspDiag.severity = lsp::DiagnosticSeverity::Error;
93     break;
94   case ast::Diagnostic::Severity::DK_Remark:
95     lspDiag.severity = lsp::DiagnosticSeverity::Information;
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. This is a heuristic, and isn't intended to cover every case,
122   // but should cover the most common. We essentially look for a comment
123   // preceding the decl, and if we find one, use that as the documentation.
124   SMLoc startLoc = decl->getLoc().Start;
125   if (!startLoc.isValid())
126     return llvm::None;
127   int bufferId = sourceMgr.FindBufferContainingLoc(startLoc);
128   if (bufferId == 0)
129     return llvm::None;
130   const char *bufferStart =
131       sourceMgr.getMemoryBuffer(bufferId)->getBufferStart();
132   StringRef buffer(bufferStart, startLoc.getPointer() - bufferStart);
133 
134   // Pop the last line from the buffer string.
135   auto popLastLine = [&]() -> Optional<StringRef> {
136     size_t newlineOffset = buffer.find_last_of("\n");
137     if (newlineOffset == StringRef::npos)
138       return llvm::None;
139     StringRef lastLine = buffer.drop_front(newlineOffset).trim();
140     buffer = buffer.take_front(newlineOffset);
141     return lastLine;
142   };
143 
144   // Try to pop the current line, which contains the decl.
145   if (!popLastLine())
146     return llvm::None;
147 
148   // Try to parse a comment string from the source file.
149   SmallVector<StringRef> commentLines;
150   while (Optional<StringRef> line = popLastLine()) {
151     // Check for a comment at the beginning of the line.
152     if (!line->startswith("//"))
153       break;
154 
155     // Extract the document string from the comment.
156     commentLines.push_back(line->drop_while([](char c) { return c == '/'; }));
157   }
158 
159   if (commentLines.empty())
160     return llvm::None;
161   return llvm::join(llvm::reverse(commentLines), "\n");
162 }
163 
164 //===----------------------------------------------------------------------===//
165 // PDLIndex
166 //===----------------------------------------------------------------------===//
167 
168 namespace {
169 struct PDLIndexSymbol {
170   explicit PDLIndexSymbol(const ast::Decl *definition)
171       : definition(definition) {}
172   explicit PDLIndexSymbol(const ods::Operation *definition)
173       : definition(definition) {}
174 
175   /// Return the location of the definition of this symbol.
176   SMRange getDefLoc() const {
177     if (const ast::Decl *decl = definition.dyn_cast<const ast::Decl *>()) {
178       const ast::Name *declName = decl->getName();
179       return declName ? declName->getLoc() : decl->getLoc();
180     }
181     return definition.get<const ods::Operation *>()->getLoc();
182   }
183 
184   /// The main definition of the symbol.
185   PointerUnion<const ast::Decl *, const ods::Operation *> definition;
186   /// The set of references to the symbol.
187   std::vector<SMRange> references;
188 };
189 
190 /// This class provides an index for definitions/uses within a PDL document.
191 /// It provides efficient lookup of a definition given an input source range.
192 class PDLIndex {
193 public:
194   PDLIndex() : intervalMap(allocator) {}
195 
196   /// Initialize the index with the given ast::Module.
197   void initialize(const ast::Module &module, const ods::Context &odsContext);
198 
199   /// Lookup a symbol for the given location. Returns nullptr if no symbol could
200   /// be found. If provided, `overlappedRange` is set to the range that the
201   /// provided `loc` overlapped with.
202   const PDLIndexSymbol *lookup(SMLoc loc,
203                                SMRange *overlappedRange = nullptr) const;
204 
205 private:
206   /// The type of interval map used to store source references. SMRange is
207   /// half-open, so we also need to use a half-open interval map.
208   using MapT =
209       llvm::IntervalMap<const char *, const PDLIndexSymbol *,
210                         llvm::IntervalMapImpl::NodeSizer<
211                             const char *, const PDLIndexSymbol *>::LeafSize,
212                         llvm::IntervalMapHalfOpenInfo<const char *>>;
213 
214   /// An allocator for the interval map.
215   MapT::Allocator allocator;
216 
217   /// An interval map containing a corresponding definition mapped to a source
218   /// interval.
219   MapT intervalMap;
220 
221   /// A mapping between definitions and their corresponding symbol.
222   DenseMap<const void *, std::unique_ptr<PDLIndexSymbol>> defToSymbol;
223 };
224 } // namespace
225 
226 void PDLIndex::initialize(const ast::Module &module,
227                           const ods::Context &odsContext) {
228   auto getOrInsertDef = [&](const auto *def) -> PDLIndexSymbol * {
229     auto it = defToSymbol.try_emplace(def, nullptr);
230     if (it.second)
231       it.first->second = std::make_unique<PDLIndexSymbol>(def);
232     return &*it.first->second;
233   };
234   auto insertDeclRef = [&](PDLIndexSymbol *sym, SMRange refLoc,
235                            bool isDef = false) {
236     const char *startLoc = refLoc.Start.getPointer();
237     const char *endLoc = refLoc.End.getPointer();
238     if (!intervalMap.overlaps(startLoc, endLoc)) {
239       intervalMap.insert(startLoc, endLoc, sym);
240       if (!isDef)
241         sym->references.push_back(refLoc);
242     }
243   };
244   auto insertODSOpRef = [&](StringRef opName, SMRange refLoc) {
245     const ods::Operation *odsOp = odsContext.lookupOperation(opName);
246     if (!odsOp)
247       return;
248 
249     PDLIndexSymbol *symbol = getOrInsertDef(odsOp);
250     insertDeclRef(symbol, odsOp->getLoc(), /*isDef=*/true);
251     insertDeclRef(symbol, refLoc);
252   };
253 
254   module.walk([&](const ast::Node *node) {
255     // Handle references to PDL decls.
256     if (const auto *decl = dyn_cast<ast::OpNameDecl>(node)) {
257       if (Optional<StringRef> name = decl->getName())
258         insertODSOpRef(*name, decl->getLoc());
259     } else if (const ast::Decl *decl = dyn_cast<ast::Decl>(node)) {
260       const ast::Name *name = decl->getName();
261       if (!name)
262         return;
263       PDLIndexSymbol *declSym = getOrInsertDef(decl);
264       insertDeclRef(declSym, name->getLoc(), /*isDef=*/true);
265 
266       if (const auto *varDecl = dyn_cast<ast::VariableDecl>(decl)) {
267         // Record references to any constraints.
268         for (const auto &it : varDecl->getConstraints())
269           insertDeclRef(getOrInsertDef(it.constraint), it.referenceLoc);
270       }
271     } else if (const auto *expr = dyn_cast<ast::DeclRefExpr>(node)) {
272       insertDeclRef(getOrInsertDef(expr->getDecl()), expr->getLoc());
273     }
274   });
275 }
276 
277 const PDLIndexSymbol *PDLIndex::lookup(SMLoc loc,
278                                        SMRange *overlappedRange) const {
279   auto it = intervalMap.find(loc.getPointer());
280   if (!it.valid() || loc.getPointer() < it.start())
281     return nullptr;
282 
283   if (overlappedRange) {
284     *overlappedRange = SMRange(SMLoc::getFromPointer(it.start()),
285                                SMLoc::getFromPointer(it.stop()));
286   }
287   return it.value();
288 }
289 
290 //===----------------------------------------------------------------------===//
291 // PDLDocument
292 //===----------------------------------------------------------------------===//
293 
294 namespace {
295 /// This class represents all of the information pertaining to a specific PDL
296 /// document.
297 struct PDLDocument {
298   PDLDocument(const lsp::URIForFile &uri, StringRef contents,
299               const std::vector<std::string> &extraDirs,
300               std::vector<lsp::Diagnostic> &diagnostics);
301   PDLDocument(const PDLDocument &) = delete;
302   PDLDocument &operator=(const PDLDocument &) = delete;
303 
304   //===--------------------------------------------------------------------===//
305   // Definitions and References
306   //===--------------------------------------------------------------------===//
307 
308   void getLocationsOf(const lsp::URIForFile &uri, const lsp::Position &defPos,
309                       std::vector<lsp::Location> &locations);
310   void findReferencesOf(const lsp::URIForFile &uri, const lsp::Position &pos,
311                         std::vector<lsp::Location> &references);
312 
313   //===--------------------------------------------------------------------===//
314   // Document Links
315   //===--------------------------------------------------------------------===//
316 
317   void getDocumentLinks(const lsp::URIForFile &uri,
318                         std::vector<lsp::DocumentLink> &links);
319 
320   //===--------------------------------------------------------------------===//
321   // Hover
322   //===--------------------------------------------------------------------===//
323 
324   Optional<lsp::Hover> findHover(const lsp::URIForFile &uri,
325                                  const lsp::Position &hoverPos);
326   Optional<lsp::Hover> findHover(const ast::Decl *decl,
327                                  const SMRange &hoverRange);
328   lsp::Hover buildHoverForOpName(const ods::Operation *op,
329                                  const SMRange &hoverRange);
330   lsp::Hover buildHoverForVariable(const ast::VariableDecl *varDecl,
331                                    const SMRange &hoverRange);
332   lsp::Hover buildHoverForPattern(const ast::PatternDecl *decl,
333                                   const SMRange &hoverRange);
334   lsp::Hover buildHoverForCoreConstraint(const ast::CoreConstraintDecl *decl,
335                                          const SMRange &hoverRange);
336   template <typename T>
337   lsp::Hover buildHoverForUserConstraintOrRewrite(StringRef typeName,
338                                                   const T *decl,
339                                                   const SMRange &hoverRange);
340 
341   //===--------------------------------------------------------------------===//
342   // Document Symbols
343   //===--------------------------------------------------------------------===//
344 
345   void findDocumentSymbols(std::vector<lsp::DocumentSymbol> &symbols);
346 
347   //===--------------------------------------------------------------------===//
348   // Code Completion
349   //===--------------------------------------------------------------------===//
350 
351   lsp::CompletionList getCodeCompletion(const lsp::URIForFile &uri,
352                                         const lsp::Position &completePos);
353 
354   //===--------------------------------------------------------------------===//
355   // Signature Help
356   //===--------------------------------------------------------------------===//
357 
358   lsp::SignatureHelp getSignatureHelp(const lsp::URIForFile &uri,
359                                       const lsp::Position &helpPos);
360 
361   //===--------------------------------------------------------------------===//
362   // PDLL ViewOutput
363   //===--------------------------------------------------------------------===//
364 
365   void getPDLLViewOutput(raw_ostream &os, lsp::PDLLViewOutputKind kind);
366 
367   //===--------------------------------------------------------------------===//
368   // Fields
369   //===--------------------------------------------------------------------===//
370 
371   /// The include directories for this file.
372   std::vector<std::string> includeDirs;
373 
374   /// The source manager containing the contents of the input file.
375   llvm::SourceMgr sourceMgr;
376 
377   /// The ODS and AST contexts.
378   ods::Context odsContext;
379   ast::Context astContext;
380 
381   /// The parsed AST module, or failure if the file wasn't valid.
382   FailureOr<ast::Module *> astModule;
383 
384   /// The index of the parsed module.
385   PDLIndex index;
386 
387   /// The set of includes of the parsed module.
388   SmallVector<lsp::SourceMgrInclude> parsedIncludes;
389 };
390 } // namespace
391 
392 PDLDocument::PDLDocument(const lsp::URIForFile &uri, StringRef contents,
393                          const std::vector<std::string> &extraDirs,
394                          std::vector<lsp::Diagnostic> &diagnostics)
395     : astContext(odsContext) {
396   auto memBuffer = llvm::MemoryBuffer::getMemBufferCopy(contents, uri.file());
397   if (!memBuffer) {
398     lsp::Logger::error("Failed to create memory buffer for file", uri.file());
399     return;
400   }
401 
402   // Build the set of include directories for this file.
403   llvm::SmallString<32> uriDirectory(uri.file());
404   llvm::sys::path::remove_filename(uriDirectory);
405   includeDirs.push_back(uriDirectory.str().str());
406   includeDirs.insert(includeDirs.end(), extraDirs.begin(), extraDirs.end());
407 
408   sourceMgr.setIncludeDirs(includeDirs);
409   sourceMgr.AddNewSourceBuffer(std::move(memBuffer), SMLoc());
410 
411   astContext.getDiagEngine().setHandlerFn([&](const ast::Diagnostic &diag) {
412     if (auto lspDiag = getLspDiagnoticFromDiag(sourceMgr, diag, uri))
413       diagnostics.push_back(std::move(*lspDiag));
414   });
415   astModule = parsePDLLAST(astContext, sourceMgr, /*enableDocumentation=*/true);
416 
417   // Initialize the set of parsed includes.
418   lsp::gatherIncludeFiles(sourceMgr, parsedIncludes);
419 
420   // If we failed to parse the module, there is nothing left to initialize.
421   if (failed(astModule))
422     return;
423 
424   // Prepare the AST index with the parsed module.
425   index.initialize(**astModule, odsContext);
426 }
427 
428 //===----------------------------------------------------------------------===//
429 // PDLDocument: Definitions and References
430 //===----------------------------------------------------------------------===//
431 
432 void PDLDocument::getLocationsOf(const lsp::URIForFile &uri,
433                                  const lsp::Position &defPos,
434                                  std::vector<lsp::Location> &locations) {
435   SMLoc posLoc = defPos.getAsSMLoc(sourceMgr);
436   const PDLIndexSymbol *symbol = index.lookup(posLoc);
437   if (!symbol)
438     return;
439 
440   locations.push_back(getLocationFromLoc(sourceMgr, symbol->getDefLoc(), uri));
441 }
442 
443 void PDLDocument::findReferencesOf(const lsp::URIForFile &uri,
444                                    const lsp::Position &pos,
445                                    std::vector<lsp::Location> &references) {
446   SMLoc posLoc = pos.getAsSMLoc(sourceMgr);
447   const PDLIndexSymbol *symbol = index.lookup(posLoc);
448   if (!symbol)
449     return;
450 
451   references.push_back(getLocationFromLoc(sourceMgr, symbol->getDefLoc(), uri));
452   for (SMRange refLoc : symbol->references)
453     references.push_back(getLocationFromLoc(sourceMgr, refLoc, uri));
454 }
455 
456 //===--------------------------------------------------------------------===//
457 // PDLDocument: Document Links
458 //===--------------------------------------------------------------------===//
459 
460 void PDLDocument::getDocumentLinks(const lsp::URIForFile &uri,
461                                    std::vector<lsp::DocumentLink> &links) {
462   for (const lsp::SourceMgrInclude &include : parsedIncludes)
463     links.emplace_back(include.range, include.uri);
464 }
465 
466 //===----------------------------------------------------------------------===//
467 // PDLDocument: Hover
468 //===----------------------------------------------------------------------===//
469 
470 Optional<lsp::Hover> PDLDocument::findHover(const lsp::URIForFile &uri,
471                                             const lsp::Position &hoverPos) {
472   SMLoc posLoc = hoverPos.getAsSMLoc(sourceMgr);
473 
474   // Check for a reference to an include.
475   for (const lsp::SourceMgrInclude &include : parsedIncludes)
476     if (include.range.contains(hoverPos))
477       return include.buildHover();
478 
479   // Find the symbol at the given location.
480   SMRange hoverRange;
481   const PDLIndexSymbol *symbol = index.lookup(posLoc, &hoverRange);
482   if (!symbol)
483     return llvm::None;
484 
485   // Add hover for operation names.
486   if (const auto *op = symbol->definition.dyn_cast<const ods::Operation *>())
487     return buildHoverForOpName(op, hoverRange);
488   const auto *decl = symbol->definition.get<const ast::Decl *>();
489   return findHover(decl, hoverRange);
490 }
491 
492 Optional<lsp::Hover> PDLDocument::findHover(const ast::Decl *decl,
493                                             const SMRange &hoverRange) {
494   // Add hover for variables.
495   if (const auto *varDecl = dyn_cast<ast::VariableDecl>(decl))
496     return buildHoverForVariable(varDecl, hoverRange);
497 
498   // Add hover for patterns.
499   if (const auto *patternDecl = dyn_cast<ast::PatternDecl>(decl))
500     return buildHoverForPattern(patternDecl, hoverRange);
501 
502   // Add hover for core constraints.
503   if (const auto *cst = dyn_cast<ast::CoreConstraintDecl>(decl))
504     return buildHoverForCoreConstraint(cst, hoverRange);
505 
506   // Add hover for user constraints.
507   if (const auto *cst = dyn_cast<ast::UserConstraintDecl>(decl))
508     return buildHoverForUserConstraintOrRewrite("Constraint", cst, hoverRange);
509 
510   // Add hover for user rewrites.
511   if (const auto *rewrite = dyn_cast<ast::UserRewriteDecl>(decl))
512     return buildHoverForUserConstraintOrRewrite("Rewrite", rewrite, hoverRange);
513 
514   return llvm::None;
515 }
516 
517 lsp::Hover PDLDocument::buildHoverForOpName(const ods::Operation *op,
518                                             const SMRange &hoverRange) {
519   lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
520   {
521     llvm::raw_string_ostream hoverOS(hover.contents.value);
522     hoverOS << "**OpName**: `" << op->getName() << "`\n***\n"
523             << op->getSummary() << "\n***\n"
524             << op->getDescription();
525   }
526   return hover;
527 }
528 
529 lsp::Hover PDLDocument::buildHoverForVariable(const ast::VariableDecl *varDecl,
530                                               const SMRange &hoverRange) {
531   lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
532   {
533     llvm::raw_string_ostream hoverOS(hover.contents.value);
534     hoverOS << "**Variable**: `" << varDecl->getName().getName() << "`\n***\n"
535             << "Type: `" << varDecl->getType() << "`\n";
536   }
537   return hover;
538 }
539 
540 lsp::Hover PDLDocument::buildHoverForPattern(const ast::PatternDecl *decl,
541                                              const SMRange &hoverRange) {
542   lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
543   {
544     llvm::raw_string_ostream hoverOS(hover.contents.value);
545     hoverOS << "**Pattern**";
546     if (const ast::Name *name = decl->getName())
547       hoverOS << ": `" << name->getName() << "`";
548     hoverOS << "\n***\n";
549     if (Optional<uint16_t> benefit = decl->getBenefit())
550       hoverOS << "Benefit: " << *benefit << "\n";
551     if (decl->hasBoundedRewriteRecursion())
552       hoverOS << "HasBoundedRewriteRecursion\n";
553     hoverOS << "RootOp: `"
554             << decl->getRootRewriteStmt()->getRootOpExpr()->getType() << "`\n";
555 
556     // Format the documentation for the decl.
557     if (Optional<std::string> doc = getDocumentationFor(sourceMgr, decl))
558       hoverOS << "\n" << *doc << "\n";
559   }
560   return hover;
561 }
562 
563 lsp::Hover
564 PDLDocument::buildHoverForCoreConstraint(const ast::CoreConstraintDecl *decl,
565                                          const SMRange &hoverRange) {
566   lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
567   {
568     llvm::raw_string_ostream hoverOS(hover.contents.value);
569     hoverOS << "**Constraint**: `";
570     TypeSwitch<const ast::Decl *>(decl)
571         .Case([&](const ast::AttrConstraintDecl *) { hoverOS << "Attr"; })
572         .Case([&](const ast::OpConstraintDecl *opCst) {
573           hoverOS << "Op";
574           if (Optional<StringRef> name = opCst->getName())
575             hoverOS << "<" << name << ">";
576         })
577         .Case([&](const ast::TypeConstraintDecl *) { hoverOS << "Type"; })
578         .Case([&](const ast::TypeRangeConstraintDecl *) {
579           hoverOS << "TypeRange";
580         })
581         .Case([&](const ast::ValueConstraintDecl *) { hoverOS << "Value"; })
582         .Case([&](const ast::ValueRangeConstraintDecl *) {
583           hoverOS << "ValueRange";
584         });
585     hoverOS << "`\n";
586   }
587   return hover;
588 }
589 
590 template <typename T>
591 lsp::Hover PDLDocument::buildHoverForUserConstraintOrRewrite(
592     StringRef typeName, const T *decl, const SMRange &hoverRange) {
593   lsp::Hover hover(lsp::Range(sourceMgr, hoverRange));
594   {
595     llvm::raw_string_ostream hoverOS(hover.contents.value);
596     hoverOS << "**" << typeName << "**: `" << decl->getName().getName()
597             << "`\n***\n";
598     ArrayRef<ast::VariableDecl *> inputs = decl->getInputs();
599     if (!inputs.empty()) {
600       hoverOS << "Parameters:\n";
601       for (const ast::VariableDecl *input : inputs)
602         hoverOS << "* " << input->getName().getName() << ": `"
603                 << input->getType() << "`\n";
604       hoverOS << "***\n";
605     }
606     ast::Type resultType = decl->getResultType();
607     if (auto resultTupleTy = resultType.dyn_cast<ast::TupleType>()) {
608       if (!resultTupleTy.empty()) {
609         hoverOS << "Results:\n";
610         for (auto it : llvm::zip(resultTupleTy.getElementNames(),
611                                  resultTupleTy.getElementTypes())) {
612           StringRef name = std::get<0>(it);
613           hoverOS << "* " << (name.empty() ? "" : (name + ": ")) << "`"
614                   << std::get<1>(it) << "`\n";
615         }
616         hoverOS << "***\n";
617       }
618     } else {
619       hoverOS << "Results:\n* `" << resultType << "`\n";
620       hoverOS << "***\n";
621     }
622 
623     // Format the documentation for the decl.
624     if (Optional<std::string> doc = getDocumentationFor(sourceMgr, decl))
625       hoverOS << "\n" << *doc << "\n";
626   }
627   return hover;
628 }
629 
630 //===----------------------------------------------------------------------===//
631 // PDLDocument: Document Symbols
632 //===----------------------------------------------------------------------===//
633 
634 void PDLDocument::findDocumentSymbols(
635     std::vector<lsp::DocumentSymbol> &symbols) {
636   if (failed(astModule))
637     return;
638 
639   for (const ast::Decl *decl : (*astModule)->getChildren()) {
640     if (!isMainFileLoc(sourceMgr, decl->getLoc()))
641       continue;
642 
643     if (const auto *patternDecl = dyn_cast<ast::PatternDecl>(decl)) {
644       const ast::Name *name = patternDecl->getName();
645 
646       SMRange nameLoc = name ? name->getLoc() : patternDecl->getLoc();
647       SMRange bodyLoc(nameLoc.Start, patternDecl->getBody()->getLoc().End);
648 
649       symbols.emplace_back(
650           name ? name->getName() : "<pattern>", lsp::SymbolKind::Class,
651           lsp::Range(sourceMgr, bodyLoc), lsp::Range(sourceMgr, nameLoc));
652     } else if (const auto *cDecl = dyn_cast<ast::UserConstraintDecl>(decl)) {
653       // TODO: Add source information for the code block body.
654       SMRange nameLoc = cDecl->getName().getLoc();
655       SMRange bodyLoc = nameLoc;
656 
657       symbols.emplace_back(
658           cDecl->getName().getName(), lsp::SymbolKind::Function,
659           lsp::Range(sourceMgr, bodyLoc), lsp::Range(sourceMgr, nameLoc));
660     } else if (const auto *cDecl = dyn_cast<ast::UserRewriteDecl>(decl)) {
661       // TODO: Add source information for the code block body.
662       SMRange nameLoc = cDecl->getName().getLoc();
663       SMRange bodyLoc = nameLoc;
664 
665       symbols.emplace_back(
666           cDecl->getName().getName(), lsp::SymbolKind::Function,
667           lsp::Range(sourceMgr, bodyLoc), lsp::Range(sourceMgr, nameLoc));
668     }
669   }
670 }
671 
672 //===----------------------------------------------------------------------===//
673 // PDLDocument: Code Completion
674 //===----------------------------------------------------------------------===//
675 
676 namespace {
677 class LSPCodeCompleteContext : public CodeCompleteContext {
678 public:
679   LSPCodeCompleteContext(SMLoc completeLoc, llvm::SourceMgr &sourceMgr,
680                          lsp::CompletionList &completionList,
681                          ods::Context &odsContext,
682                          ArrayRef<std::string> includeDirs)
683       : CodeCompleteContext(completeLoc), sourceMgr(sourceMgr),
684         completionList(completionList), odsContext(odsContext),
685         includeDirs(includeDirs) {}
686 
687   void codeCompleteTupleMemberAccess(ast::TupleType tupleType) final {
688     ArrayRef<ast::Type> elementTypes = tupleType.getElementTypes();
689     ArrayRef<StringRef> elementNames = tupleType.getElementNames();
690     for (unsigned i = 0, e = tupleType.size(); i < e; ++i) {
691       // Push back a completion item that uses the result index.
692       lsp::CompletionItem item;
693       item.label = llvm::formatv("{0} (field #{0})", i).str();
694       item.insertText = Twine(i).str();
695       item.filterText = item.sortText = item.insertText;
696       item.kind = lsp::CompletionItemKind::Field;
697       item.detail = llvm::formatv("{0}: {1}", i, elementTypes[i]);
698       item.insertTextFormat = lsp::InsertTextFormat::PlainText;
699       completionList.items.emplace_back(item);
700 
701       // If the element has a name, push back a completion item with that name.
702       if (!elementNames[i].empty()) {
703         item.label =
704             llvm::formatv("{1} (field #{0})", i, elementNames[i]).str();
705         item.filterText = item.label;
706         item.insertText = elementNames[i].str();
707         completionList.items.emplace_back(item);
708       }
709     }
710   }
711 
712   void codeCompleteOperationMemberAccess(ast::OperationType opType) final {
713     const ods::Operation *odsOp = opType.getODSOperation();
714     if (!odsOp)
715       return;
716 
717     ArrayRef<ods::OperandOrResult> results = odsOp->getResults();
718     for (const auto &it : llvm::enumerate(results)) {
719       const ods::OperandOrResult &result = it.value();
720       const ods::TypeConstraint &constraint = result.getConstraint();
721 
722       // Push back a completion item that uses the result index.
723       lsp::CompletionItem item;
724       item.label = llvm::formatv("{0} (field #{0})", it.index()).str();
725       item.insertText = Twine(it.index()).str();
726       item.filterText = item.sortText = item.insertText;
727       item.kind = lsp::CompletionItemKind::Field;
728       switch (result.getVariableLengthKind()) {
729       case ods::VariableLengthKind::Single:
730         item.detail = llvm::formatv("{0}: Value", it.index()).str();
731         break;
732       case ods::VariableLengthKind::Optional:
733         item.detail = llvm::formatv("{0}: Value?", it.index()).str();
734         break;
735       case ods::VariableLengthKind::Variadic:
736         item.detail = llvm::formatv("{0}: ValueRange", it.index()).str();
737         break;
738       }
739       item.documentation = lsp::MarkupContent{
740           lsp::MarkupKind::Markdown,
741           llvm::formatv("{0}\n\n```c++\n{1}\n```\n", constraint.getSummary(),
742                         constraint.getCppClass())
743               .str()};
744       item.insertTextFormat = lsp::InsertTextFormat::PlainText;
745       completionList.items.emplace_back(item);
746 
747       // If the result has a name, push back a completion item with the result
748       // name.
749       if (!result.getName().empty()) {
750         item.label =
751             llvm::formatv("{1} (field #{0})", it.index(), result.getName())
752                 .str();
753         item.filterText = item.label;
754         item.insertText = result.getName().str();
755         completionList.items.emplace_back(item);
756       }
757     }
758   }
759 
760   void codeCompleteOperationAttributeName(StringRef opName) final {
761     const ods::Operation *odsOp = odsContext.lookupOperation(opName);
762     if (!odsOp)
763       return;
764 
765     for (const ods::Attribute &attr : odsOp->getAttributes()) {
766       const ods::AttributeConstraint &constraint = attr.getConstraint();
767 
768       lsp::CompletionItem item;
769       item.label = attr.getName().str();
770       item.kind = lsp::CompletionItemKind::Field;
771       item.detail = attr.isOptional() ? "optional" : "";
772       item.documentation = lsp::MarkupContent{
773           lsp::MarkupKind::Markdown,
774           llvm::formatv("{0}\n\n```c++\n{1}\n```\n", constraint.getSummary(),
775                         constraint.getCppClass())
776               .str()};
777       item.insertTextFormat = lsp::InsertTextFormat::PlainText;
778       completionList.items.emplace_back(item);
779     }
780   }
781 
782   void codeCompleteConstraintName(ast::Type currentType,
783                                   bool allowNonCoreConstraints,
784                                   bool allowInlineTypeConstraints,
785                                   const ast::DeclScope *scope) final {
786     auto addCoreConstraint = [&](StringRef constraint, StringRef mlirType,
787                                  StringRef snippetText = "") {
788       lsp::CompletionItem item;
789       item.label = constraint.str();
790       item.kind = lsp::CompletionItemKind::Class;
791       item.detail = (constraint + " constraint").str();
792       item.documentation = lsp::MarkupContent{
793           lsp::MarkupKind::Markdown,
794           ("A single entity core constraint of type `" + mlirType + "`").str()};
795       item.sortText = "0";
796       item.insertText = snippetText.str();
797       item.insertTextFormat = snippetText.empty()
798                                   ? lsp::InsertTextFormat::PlainText
799                                   : lsp::InsertTextFormat::Snippet;
800       completionList.items.emplace_back(item);
801     };
802 
803     // Insert completions for the core constraints. Some core constraints have
804     // additional characteristics, so we may add then even if a type has been
805     // inferred.
806     if (!currentType) {
807       addCoreConstraint("Attr", "mlir::Attribute");
808       addCoreConstraint("Op", "mlir::Operation *");
809       addCoreConstraint("Value", "mlir::Value");
810       addCoreConstraint("ValueRange", "mlir::ValueRange");
811       addCoreConstraint("Type", "mlir::Type");
812       addCoreConstraint("TypeRange", "mlir::TypeRange");
813     }
814     if (allowInlineTypeConstraints) {
815       /// Attr<Type>.
816       if (!currentType || currentType.isa<ast::AttributeType>())
817         addCoreConstraint("Attr<type>", "mlir::Attribute", "Attr<$1>");
818       /// Value<Type>.
819       if (!currentType || currentType.isa<ast::ValueType>())
820         addCoreConstraint("Value<type>", "mlir::Value", "Value<$1>");
821       /// ValueRange<TypeRange>.
822       if (!currentType || currentType.isa<ast::ValueRangeType>())
823         addCoreConstraint("ValueRange<type>", "mlir::ValueRange",
824                           "ValueRange<$1>");
825     }
826 
827     // If a scope was provided, check it for potential constraints.
828     while (scope) {
829       for (const ast::Decl *decl : scope->getDecls()) {
830         if (const auto *cst = dyn_cast<ast::UserConstraintDecl>(decl)) {
831           if (!allowNonCoreConstraints)
832             continue;
833 
834           lsp::CompletionItem item;
835           item.label = cst->getName().getName().str();
836           item.kind = lsp::CompletionItemKind::Interface;
837           item.sortText = "2_" + item.label;
838 
839           // Skip constraints that are not single-arg. We currently only
840           // complete variable constraints.
841           if (cst->getInputs().size() != 1)
842             continue;
843 
844           // Ensure the input type matched the given type.
845           ast::Type constraintType = cst->getInputs()[0]->getType();
846           if (currentType && !currentType.refineWith(constraintType))
847             continue;
848 
849           // Format the constraint signature.
850           {
851             llvm::raw_string_ostream strOS(item.detail);
852             strOS << "(";
853             llvm::interleaveComma(
854                 cst->getInputs(), strOS, [&](const ast::VariableDecl *var) {
855                   strOS << var->getName().getName() << ": " << var->getType();
856                 });
857             strOS << ") -> " << cst->getResultType();
858           }
859 
860           // Format the documentation for the constraint.
861           if (Optional<std::string> doc = getDocumentationFor(sourceMgr, cst)) {
862             item.documentation =
863                 lsp::MarkupContent{lsp::MarkupKind::Markdown, std::move(*doc)};
864           }
865 
866           completionList.items.emplace_back(item);
867         }
868       }
869 
870       scope = scope->getParentScope();
871     }
872   }
873 
874   void codeCompleteDialectName() final {
875     // Code complete known dialects.
876     for (const ods::Dialect &dialect : odsContext.getDialects()) {
877       lsp::CompletionItem item;
878       item.label = dialect.getName().str();
879       item.kind = lsp::CompletionItemKind::Class;
880       item.insertTextFormat = lsp::InsertTextFormat::PlainText;
881       completionList.items.emplace_back(item);
882     }
883   }
884 
885   void codeCompleteOperationName(StringRef dialectName) final {
886     const ods::Dialect *dialect = odsContext.lookupDialect(dialectName);
887     if (!dialect)
888       return;
889 
890     for (const auto &it : dialect->getOperations()) {
891       const ods::Operation &op = *it.second;
892 
893       lsp::CompletionItem item;
894       item.label = op.getName().drop_front(dialectName.size() + 1).str();
895       item.kind = lsp::CompletionItemKind::Field;
896       item.insertTextFormat = lsp::InsertTextFormat::PlainText;
897       completionList.items.emplace_back(item);
898     }
899   }
900 
901   void codeCompletePatternMetadata() final {
902     auto addSimpleConstraint = [&](StringRef constraint, StringRef desc,
903                                    StringRef snippetText = "") {
904       lsp::CompletionItem item;
905       item.label = constraint.str();
906       item.kind = lsp::CompletionItemKind::Class;
907       item.detail = "pattern metadata";
908       item.documentation =
909           lsp::MarkupContent{lsp::MarkupKind::Markdown, desc.str()};
910       item.insertText = snippetText.str();
911       item.insertTextFormat = snippetText.empty()
912                                   ? lsp::InsertTextFormat::PlainText
913                                   : lsp::InsertTextFormat::Snippet;
914       completionList.items.emplace_back(item);
915     };
916 
917     addSimpleConstraint("benefit", "The `benefit` of matching the pattern.",
918                         "benefit($1)");
919     addSimpleConstraint("recursion",
920                         "The pattern properly handles recursive application.");
921   }
922 
923   void codeCompleteIncludeFilename(StringRef curPath) final {
924     // Normalize the path to allow for interacting with the file system
925     // utilities.
926     SmallString<128> nativeRelDir(llvm::sys::path::convert_to_slash(curPath));
927     llvm::sys::path::native(nativeRelDir);
928 
929     // Set of already included completion paths.
930     StringSet<> seenResults;
931 
932     // Functor used to add a single include completion item.
933     auto addIncludeCompletion = [&](StringRef path, bool isDirectory) {
934       lsp::CompletionItem item;
935       item.label = path.str();
936       item.kind = isDirectory ? lsp::CompletionItemKind::Folder
937                               : lsp::CompletionItemKind::File;
938       if (seenResults.insert(item.label).second)
939         completionList.items.emplace_back(item);
940     };
941 
942     // Process the include directories for this file, adding any potential
943     // nested include files or directories.
944     for (StringRef includeDir : includeDirs) {
945       llvm::SmallString<128> dir = includeDir;
946       if (!nativeRelDir.empty())
947         llvm::sys::path::append(dir, nativeRelDir);
948 
949       std::error_code errorCode;
950       for (auto it = llvm::sys::fs::directory_iterator(dir, errorCode),
951                 e = llvm::sys::fs::directory_iterator();
952            !errorCode && it != e; it.increment(errorCode)) {
953         StringRef filename = llvm::sys::path::filename(it->path());
954 
955         // To know whether a symlink should be treated as file or a directory,
956         // we have to stat it. This should be cheap enough as there shouldn't be
957         // many symlinks.
958         llvm::sys::fs::file_type fileType = it->type();
959         if (fileType == llvm::sys::fs::file_type::symlink_file) {
960           if (auto fileStatus = it->status())
961             fileType = fileStatus->type();
962         }
963 
964         switch (fileType) {
965         case llvm::sys::fs::file_type::directory_file:
966           addIncludeCompletion(filename, /*isDirectory=*/true);
967           break;
968         case llvm::sys::fs::file_type::regular_file: {
969           // Only consider concrete files that can actually be included by PDLL.
970           if (filename.endswith(".pdll") || filename.endswith(".td"))
971             addIncludeCompletion(filename, /*isDirectory=*/false);
972           break;
973         }
974         default:
975           break;
976         }
977       }
978     }
979 
980     // Sort the completion results to make sure the output is deterministic in
981     // the face of different iteration schemes for different platforms.
982     llvm::sort(completionList.items, [](const lsp::CompletionItem &lhs,
983                                         const lsp::CompletionItem &rhs) {
984       return lhs.label < rhs.label;
985     });
986   }
987 
988 private:
989   llvm::SourceMgr &sourceMgr;
990   lsp::CompletionList &completionList;
991   ods::Context &odsContext;
992   ArrayRef<std::string> includeDirs;
993 };
994 } // namespace
995 
996 lsp::CompletionList
997 PDLDocument::getCodeCompletion(const lsp::URIForFile &uri,
998                                const lsp::Position &completePos) {
999   SMLoc posLoc = completePos.getAsSMLoc(sourceMgr);
1000   if (!posLoc.isValid())
1001     return lsp::CompletionList();
1002 
1003   // To perform code completion, we run another parse of the module with the
1004   // code completion context provided.
1005   ods::Context tmpODSContext;
1006   lsp::CompletionList completionList;
1007   LSPCodeCompleteContext lspCompleteContext(posLoc, sourceMgr, completionList,
1008                                             tmpODSContext,
1009                                             sourceMgr.getIncludeDirs());
1010 
1011   ast::Context tmpContext(tmpODSContext);
1012   (void)parsePDLLAST(tmpContext, sourceMgr, /*enableDocumentation=*/true,
1013                      &lspCompleteContext);
1014 
1015   return completionList;
1016 }
1017 
1018 //===----------------------------------------------------------------------===//
1019 // PDLDocument: Signature Help
1020 //===----------------------------------------------------------------------===//
1021 
1022 namespace {
1023 class LSPSignatureHelpContext : public CodeCompleteContext {
1024 public:
1025   LSPSignatureHelpContext(SMLoc completeLoc, llvm::SourceMgr &sourceMgr,
1026                           lsp::SignatureHelp &signatureHelp,
1027                           ods::Context &odsContext)
1028       : CodeCompleteContext(completeLoc), sourceMgr(sourceMgr),
1029         signatureHelp(signatureHelp), odsContext(odsContext) {}
1030 
1031   void codeCompleteCallSignature(const ast::CallableDecl *callable,
1032                                  unsigned currentNumArgs) final {
1033     signatureHelp.activeParameter = currentNumArgs;
1034 
1035     lsp::SignatureInformation signatureInfo;
1036     {
1037       llvm::raw_string_ostream strOS(signatureInfo.label);
1038       strOS << callable->getName()->getName() << "(";
1039       auto formatParamFn = [&](const ast::VariableDecl *var) {
1040         unsigned paramStart = strOS.str().size();
1041         strOS << var->getName().getName() << ": " << var->getType();
1042         unsigned paramEnd = strOS.str().size();
1043         signatureInfo.parameters.emplace_back(lsp::ParameterInformation{
1044             StringRef(strOS.str()).slice(paramStart, paramEnd).str(),
1045             std::make_pair(paramStart, paramEnd), /*paramDoc*/ std::string()});
1046       };
1047       llvm::interleaveComma(callable->getInputs(), strOS, formatParamFn);
1048       strOS << ") -> " << callable->getResultType();
1049     }
1050 
1051     // Format the documentation for the callable.
1052     if (Optional<std::string> doc = getDocumentationFor(sourceMgr, callable))
1053       signatureInfo.documentation = std::move(*doc);
1054 
1055     signatureHelp.signatures.emplace_back(std::move(signatureInfo));
1056   }
1057 
1058   void
1059   codeCompleteOperationOperandsSignature(Optional<StringRef> opName,
1060                                          unsigned currentNumOperands) final {
1061     const ods::Operation *odsOp =
1062         opName ? odsContext.lookupOperation(*opName) : nullptr;
1063     codeCompleteOperationOperandOrResultSignature(
1064         opName, odsOp, odsOp ? odsOp->getOperands() : llvm::None,
1065         currentNumOperands, "operand", "Value");
1066   }
1067 
1068   void codeCompleteOperationResultsSignature(Optional<StringRef> opName,
1069                                              unsigned currentNumResults) final {
1070     const ods::Operation *odsOp =
1071         opName ? odsContext.lookupOperation(*opName) : nullptr;
1072     codeCompleteOperationOperandOrResultSignature(
1073         opName, odsOp, odsOp ? odsOp->getResults() : llvm::None,
1074         currentNumResults, "result", "Type");
1075   }
1076 
1077   void codeCompleteOperationOperandOrResultSignature(
1078       Optional<StringRef> opName, const ods::Operation *odsOp,
1079       ArrayRef<ods::OperandOrResult> values, unsigned currentValue,
1080       StringRef label, StringRef dataType) {
1081     signatureHelp.activeParameter = currentValue;
1082 
1083     // If we have ODS information for the operation, add in the ODS signature
1084     // for the operation. We also verify that the current number of values is
1085     // not more than what is defined in ODS, as this will result in an error
1086     // anyways.
1087     if (odsOp && currentValue < values.size()) {
1088       lsp::SignatureInformation signatureInfo;
1089 
1090       // Build the signature label.
1091       {
1092         llvm::raw_string_ostream strOS(signatureInfo.label);
1093         strOS << "(";
1094         auto formatFn = [&](const ods::OperandOrResult &value) {
1095           unsigned paramStart = strOS.str().size();
1096 
1097           strOS << value.getName() << ": ";
1098 
1099           StringRef constraintDoc = value.getConstraint().getSummary();
1100           std::string paramDoc;
1101           switch (value.getVariableLengthKind()) {
1102           case ods::VariableLengthKind::Single:
1103             strOS << dataType;
1104             paramDoc = constraintDoc.str();
1105             break;
1106           case ods::VariableLengthKind::Optional:
1107             strOS << dataType << "?";
1108             paramDoc = ("optional: " + constraintDoc).str();
1109             break;
1110           case ods::VariableLengthKind::Variadic:
1111             strOS << dataType << "Range";
1112             paramDoc = ("variadic: " + constraintDoc).str();
1113             break;
1114           }
1115 
1116           unsigned paramEnd = strOS.str().size();
1117           signatureInfo.parameters.emplace_back(lsp::ParameterInformation{
1118               StringRef(strOS.str()).slice(paramStart, paramEnd).str(),
1119               std::make_pair(paramStart, paramEnd), paramDoc});
1120         };
1121         llvm::interleaveComma(values, strOS, formatFn);
1122         strOS << ")";
1123       }
1124       signatureInfo.documentation =
1125           llvm::formatv("`op<{0}>` ODS {1} specification", *opName, label)
1126               .str();
1127       signatureHelp.signatures.emplace_back(std::move(signatureInfo));
1128     }
1129 
1130     // If there aren't any arguments yet, we also add the generic signature.
1131     if (currentValue == 0 && (!odsOp || !values.empty())) {
1132       lsp::SignatureInformation signatureInfo;
1133       signatureInfo.label =
1134           llvm::formatv("(<{0}s>: {1}Range)", label, dataType).str();
1135       signatureInfo.documentation =
1136           ("Generic operation " + label + " specification").str();
1137       signatureInfo.parameters.emplace_back(lsp::ParameterInformation{
1138           StringRef(signatureInfo.label).drop_front().drop_back().str(),
1139           std::pair<unsigned, unsigned>(1, signatureInfo.label.size() - 1),
1140           ("All of the " + label + "s of the operation.").str()});
1141       signatureHelp.signatures.emplace_back(std::move(signatureInfo));
1142     }
1143   }
1144 
1145 private:
1146   llvm::SourceMgr &sourceMgr;
1147   lsp::SignatureHelp &signatureHelp;
1148   ods::Context &odsContext;
1149 };
1150 } // namespace
1151 
1152 lsp::SignatureHelp PDLDocument::getSignatureHelp(const lsp::URIForFile &uri,
1153                                                  const lsp::Position &helpPos) {
1154   SMLoc posLoc = helpPos.getAsSMLoc(sourceMgr);
1155   if (!posLoc.isValid())
1156     return lsp::SignatureHelp();
1157 
1158   // To perform code completion, we run another parse of the module with the
1159   // code completion context provided.
1160   ods::Context tmpODSContext;
1161   lsp::SignatureHelp signatureHelp;
1162   LSPSignatureHelpContext completeContext(posLoc, sourceMgr, signatureHelp,
1163                                           tmpODSContext);
1164 
1165   ast::Context tmpContext(tmpODSContext);
1166   (void)parsePDLLAST(tmpContext, sourceMgr, /*enableDocumentation=*/true,
1167                      &completeContext);
1168 
1169   return signatureHelp;
1170 }
1171 
1172 //===----------------------------------------------------------------------===//
1173 // PDLL ViewOutput
1174 //===----------------------------------------------------------------------===//
1175 
1176 void PDLDocument::getPDLLViewOutput(raw_ostream &os,
1177                                     lsp::PDLLViewOutputKind kind) {
1178   if (failed(astModule))
1179     return;
1180   if (kind == lsp::PDLLViewOutputKind::AST) {
1181     (*astModule)->print(os);
1182     return;
1183   }
1184 
1185   // Generate the MLIR for the ast module. We also capture diagnostics here to
1186   // show to the user, which may be useful if PDLL isn't capturing constraints
1187   // expected by PDL.
1188   MLIRContext mlirContext;
1189   SourceMgrDiagnosticHandler diagHandler(sourceMgr, &mlirContext, os);
1190   OwningOpRef<ModuleOp> pdlModule =
1191       codegenPDLLToMLIR(&mlirContext, astContext, sourceMgr, **astModule);
1192   if (!pdlModule)
1193     return;
1194   if (kind == lsp::PDLLViewOutputKind::MLIR) {
1195     pdlModule->print(os, OpPrintingFlags().enableDebugInfo());
1196     return;
1197   }
1198 
1199   // Otherwise, generate the output for C++.
1200   assert(kind == lsp::PDLLViewOutputKind::CPP &&
1201          "unexpected PDLLViewOutputKind");
1202   codegenPDLLToCPP(**astModule, *pdlModule, os);
1203 }
1204 
1205 //===----------------------------------------------------------------------===//
1206 // PDLTextFileChunk
1207 //===----------------------------------------------------------------------===//
1208 
1209 namespace {
1210 /// This class represents a single chunk of an PDL text file.
1211 struct PDLTextFileChunk {
1212   PDLTextFileChunk(uint64_t lineOffset, const lsp::URIForFile &uri,
1213                    StringRef contents,
1214                    const std::vector<std::string> &extraDirs,
1215                    std::vector<lsp::Diagnostic> &diagnostics)
1216       : lineOffset(lineOffset),
1217         document(uri, contents, extraDirs, diagnostics) {}
1218 
1219   /// Adjust the line number of the given range to anchor at the beginning of
1220   /// the file, instead of the beginning of this chunk.
1221   void adjustLocForChunkOffset(lsp::Range &range) {
1222     adjustLocForChunkOffset(range.start);
1223     adjustLocForChunkOffset(range.end);
1224   }
1225   /// Adjust the line number of the given position to anchor at the beginning of
1226   /// the file, instead of the beginning of this chunk.
1227   void adjustLocForChunkOffset(lsp::Position &pos) { pos.line += lineOffset; }
1228 
1229   /// The line offset of this chunk from the beginning of the file.
1230   uint64_t lineOffset;
1231   /// The document referred to by this chunk.
1232   PDLDocument document;
1233 };
1234 } // namespace
1235 
1236 //===----------------------------------------------------------------------===//
1237 // PDLTextFile
1238 //===----------------------------------------------------------------------===//
1239 
1240 namespace {
1241 /// This class represents a text file containing one or more PDL documents.
1242 class PDLTextFile {
1243 public:
1244   PDLTextFile(const lsp::URIForFile &uri, StringRef fileContents,
1245               int64_t version, const std::vector<std::string> &extraDirs,
1246               std::vector<lsp::Diagnostic> &diagnostics);
1247 
1248   /// Return the current version of this text file.
1249   int64_t getVersion() const { return version; }
1250 
1251   //===--------------------------------------------------------------------===//
1252   // LSP Queries
1253   //===--------------------------------------------------------------------===//
1254 
1255   void getLocationsOf(const lsp::URIForFile &uri, lsp::Position defPos,
1256                       std::vector<lsp::Location> &locations);
1257   void findReferencesOf(const lsp::URIForFile &uri, lsp::Position pos,
1258                         std::vector<lsp::Location> &references);
1259   void getDocumentLinks(const lsp::URIForFile &uri,
1260                         std::vector<lsp::DocumentLink> &links);
1261   Optional<lsp::Hover> findHover(const lsp::URIForFile &uri,
1262                                  lsp::Position hoverPos);
1263   void findDocumentSymbols(std::vector<lsp::DocumentSymbol> &symbols);
1264   lsp::CompletionList getCodeCompletion(const lsp::URIForFile &uri,
1265                                         lsp::Position completePos);
1266   lsp::SignatureHelp getSignatureHelp(const lsp::URIForFile &uri,
1267                                       lsp::Position helpPos);
1268   lsp::PDLLViewOutputResult getPDLLViewOutput(lsp::PDLLViewOutputKind kind);
1269 
1270 private:
1271   /// Find the PDL document that contains the given position, and update the
1272   /// position to be anchored at the start of the found chunk instead of the
1273   /// beginning of the file.
1274   PDLTextFileChunk &getChunkFor(lsp::Position &pos);
1275 
1276   /// The full string contents of the file.
1277   std::string contents;
1278 
1279   /// The version of this file.
1280   int64_t version;
1281 
1282   /// The number of lines in the file.
1283   int64_t totalNumLines = 0;
1284 
1285   /// The chunks of this file. The order of these chunks is the order in which
1286   /// they appear in the text file.
1287   std::vector<std::unique_ptr<PDLTextFileChunk>> chunks;
1288 };
1289 } // namespace
1290 
1291 PDLTextFile::PDLTextFile(const lsp::URIForFile &uri, StringRef fileContents,
1292                          int64_t version,
1293                          const std::vector<std::string> &extraDirs,
1294                          std::vector<lsp::Diagnostic> &diagnostics)
1295     : contents(fileContents.str()), version(version) {
1296   // Split the file into separate PDL documents.
1297   // TODO: Find a way to share the split file marker with other tools. We don't
1298   // want to use `splitAndProcessBuffer` here, but we do want to make sure this
1299   // marker doesn't go out of sync.
1300   SmallVector<StringRef, 8> subContents;
1301   StringRef(contents).split(subContents, "// -----");
1302   chunks.emplace_back(std::make_unique<PDLTextFileChunk>(
1303       /*lineOffset=*/0, uri, subContents.front(), extraDirs, diagnostics));
1304 
1305   uint64_t lineOffset = subContents.front().count('\n');
1306   for (StringRef docContents : llvm::drop_begin(subContents)) {
1307     unsigned currentNumDiags = diagnostics.size();
1308     auto chunk = std::make_unique<PDLTextFileChunk>(
1309         lineOffset, uri, docContents, extraDirs, diagnostics);
1310     lineOffset += docContents.count('\n');
1311 
1312     // Adjust locations used in diagnostics to account for the offset from the
1313     // beginning of the file.
1314     for (lsp::Diagnostic &diag :
1315          llvm::drop_begin(diagnostics, currentNumDiags)) {
1316       chunk->adjustLocForChunkOffset(diag.range);
1317 
1318       if (!diag.relatedInformation)
1319         continue;
1320       for (auto &it : *diag.relatedInformation)
1321         if (it.location.uri == uri)
1322           chunk->adjustLocForChunkOffset(it.location.range);
1323     }
1324     chunks.emplace_back(std::move(chunk));
1325   }
1326   totalNumLines = lineOffset;
1327 }
1328 
1329 void PDLTextFile::getLocationsOf(const lsp::URIForFile &uri,
1330                                  lsp::Position defPos,
1331                                  std::vector<lsp::Location> &locations) {
1332   PDLTextFileChunk &chunk = getChunkFor(defPos);
1333   chunk.document.getLocationsOf(uri, defPos, locations);
1334 
1335   // Adjust any locations within this file for the offset of this chunk.
1336   if (chunk.lineOffset == 0)
1337     return;
1338   for (lsp::Location &loc : locations)
1339     if (loc.uri == uri)
1340       chunk.adjustLocForChunkOffset(loc.range);
1341 }
1342 
1343 void PDLTextFile::findReferencesOf(const lsp::URIForFile &uri,
1344                                    lsp::Position pos,
1345                                    std::vector<lsp::Location> &references) {
1346   PDLTextFileChunk &chunk = getChunkFor(pos);
1347   chunk.document.findReferencesOf(uri, pos, references);
1348 
1349   // Adjust any locations within this file for the offset of this chunk.
1350   if (chunk.lineOffset == 0)
1351     return;
1352   for (lsp::Location &loc : references)
1353     if (loc.uri == uri)
1354       chunk.adjustLocForChunkOffset(loc.range);
1355 }
1356 
1357 void PDLTextFile::getDocumentLinks(const lsp::URIForFile &uri,
1358                                    std::vector<lsp::DocumentLink> &links) {
1359   chunks.front()->document.getDocumentLinks(uri, links);
1360   for (const auto &it : llvm::drop_begin(chunks)) {
1361     size_t currentNumLinks = links.size();
1362     it->document.getDocumentLinks(uri, links);
1363 
1364     // Adjust any links within this file to account for the offset of this
1365     // chunk.
1366     for (auto &link : llvm::drop_begin(links, currentNumLinks))
1367       it->adjustLocForChunkOffset(link.range);
1368   }
1369 }
1370 
1371 Optional<lsp::Hover> PDLTextFile::findHover(const lsp::URIForFile &uri,
1372                                             lsp::Position hoverPos) {
1373   PDLTextFileChunk &chunk = getChunkFor(hoverPos);
1374   Optional<lsp::Hover> hoverInfo = chunk.document.findHover(uri, hoverPos);
1375 
1376   // Adjust any locations within this file for the offset of this chunk.
1377   if (chunk.lineOffset != 0 && hoverInfo && hoverInfo->range)
1378     chunk.adjustLocForChunkOffset(*hoverInfo->range);
1379   return hoverInfo;
1380 }
1381 
1382 void PDLTextFile::findDocumentSymbols(
1383     std::vector<lsp::DocumentSymbol> &symbols) {
1384   if (chunks.size() == 1)
1385     return chunks.front()->document.findDocumentSymbols(symbols);
1386 
1387   // If there are multiple chunks in this file, we create top-level symbols for
1388   // each chunk.
1389   for (unsigned i = 0, e = chunks.size(); i < e; ++i) {
1390     PDLTextFileChunk &chunk = *chunks[i];
1391     lsp::Position startPos(chunk.lineOffset);
1392     lsp::Position endPos((i == e - 1) ? totalNumLines - 1
1393                                       : chunks[i + 1]->lineOffset);
1394     lsp::DocumentSymbol symbol("<file-split-" + Twine(i) + ">",
1395                                lsp::SymbolKind::Namespace,
1396                                /*range=*/lsp::Range(startPos, endPos),
1397                                /*selectionRange=*/lsp::Range(startPos));
1398     chunk.document.findDocumentSymbols(symbol.children);
1399 
1400     // Fixup the locations of document symbols within this chunk.
1401     if (i != 0) {
1402       SmallVector<lsp::DocumentSymbol *> symbolsToFix;
1403       for (lsp::DocumentSymbol &childSymbol : symbol.children)
1404         symbolsToFix.push_back(&childSymbol);
1405 
1406       while (!symbolsToFix.empty()) {
1407         lsp::DocumentSymbol *symbol = symbolsToFix.pop_back_val();
1408         chunk.adjustLocForChunkOffset(symbol->range);
1409         chunk.adjustLocForChunkOffset(symbol->selectionRange);
1410 
1411         for (lsp::DocumentSymbol &childSymbol : symbol->children)
1412           symbolsToFix.push_back(&childSymbol);
1413       }
1414     }
1415 
1416     // Push the symbol for this chunk.
1417     symbols.emplace_back(std::move(symbol));
1418   }
1419 }
1420 
1421 lsp::CompletionList PDLTextFile::getCodeCompletion(const lsp::URIForFile &uri,
1422                                                    lsp::Position completePos) {
1423   PDLTextFileChunk &chunk = getChunkFor(completePos);
1424   lsp::CompletionList completionList =
1425       chunk.document.getCodeCompletion(uri, completePos);
1426 
1427   // Adjust any completion locations.
1428   for (lsp::CompletionItem &item : completionList.items) {
1429     if (item.textEdit)
1430       chunk.adjustLocForChunkOffset(item.textEdit->range);
1431     for (lsp::TextEdit &edit : item.additionalTextEdits)
1432       chunk.adjustLocForChunkOffset(edit.range);
1433   }
1434   return completionList;
1435 }
1436 
1437 lsp::SignatureHelp PDLTextFile::getSignatureHelp(const lsp::URIForFile &uri,
1438                                                  lsp::Position helpPos) {
1439   return getChunkFor(helpPos).document.getSignatureHelp(uri, helpPos);
1440 }
1441 
1442 lsp::PDLLViewOutputResult
1443 PDLTextFile::getPDLLViewOutput(lsp::PDLLViewOutputKind kind) {
1444   lsp::PDLLViewOutputResult result;
1445   {
1446     llvm::raw_string_ostream outputOS(result.output);
1447     llvm::interleave(
1448         llvm::make_pointee_range(chunks),
1449         [&](PDLTextFileChunk &chunk) {
1450           chunk.document.getPDLLViewOutput(outputOS, kind);
1451         },
1452         [&] { outputOS << "\n// -----\n\n"; });
1453   }
1454   return result;
1455 }
1456 
1457 PDLTextFileChunk &PDLTextFile::getChunkFor(lsp::Position &pos) {
1458   if (chunks.size() == 1)
1459     return *chunks.front();
1460 
1461   // Search for the first chunk with a greater line offset, the previous chunk
1462   // is the one that contains `pos`.
1463   auto it = llvm::upper_bound(
1464       chunks, pos, [](const lsp::Position &pos, const auto &chunk) {
1465         return static_cast<uint64_t>(pos.line) < chunk->lineOffset;
1466       });
1467   PDLTextFileChunk &chunk = it == chunks.end() ? *chunks.back() : **(--it);
1468   pos.line -= chunk.lineOffset;
1469   return chunk;
1470 }
1471 
1472 //===----------------------------------------------------------------------===//
1473 // PDLLServer::Impl
1474 //===----------------------------------------------------------------------===//
1475 
1476 struct lsp::PDLLServer::Impl {
1477   explicit Impl(const Options &options)
1478       : options(options), compilationDatabase(options.compilationDatabases) {}
1479 
1480   /// PDLL LSP options.
1481   const Options &options;
1482 
1483   /// The compilation database containing additional information for files
1484   /// passed to the server.
1485   lsp::CompilationDatabase compilationDatabase;
1486 
1487   /// The files held by the server, mapped by their URI file name.
1488   llvm::StringMap<std::unique_ptr<PDLTextFile>> files;
1489 };
1490 
1491 //===----------------------------------------------------------------------===//
1492 // PDLLServer
1493 //===----------------------------------------------------------------------===//
1494 
1495 lsp::PDLLServer::PDLLServer(const Options &options)
1496     : impl(std::make_unique<Impl>(options)) {}
1497 lsp::PDLLServer::~PDLLServer() = default;
1498 
1499 void lsp::PDLLServer::addOrUpdateDocument(
1500     const URIForFile &uri, StringRef contents, int64_t version,
1501     std::vector<Diagnostic> &diagnostics) {
1502   // Build the set of additional include directories.
1503   std::vector<std::string> additionalIncludeDirs = impl->options.extraDirs;
1504   const auto &fileInfo = impl->compilationDatabase.getFileInfo(uri.file());
1505   llvm::append_range(additionalIncludeDirs, fileInfo.includeDirs);
1506 
1507   impl->files[uri.file()] = std::make_unique<PDLTextFile>(
1508       uri, contents, version, additionalIncludeDirs, diagnostics);
1509 }
1510 
1511 Optional<int64_t> lsp::PDLLServer::removeDocument(const URIForFile &uri) {
1512   auto it = impl->files.find(uri.file());
1513   if (it == impl->files.end())
1514     return llvm::None;
1515 
1516   int64_t version = it->second->getVersion();
1517   impl->files.erase(it);
1518   return version;
1519 }
1520 
1521 void lsp::PDLLServer::getLocationsOf(const URIForFile &uri,
1522                                      const Position &defPos,
1523                                      std::vector<Location> &locations) {
1524   auto fileIt = impl->files.find(uri.file());
1525   if (fileIt != impl->files.end())
1526     fileIt->second->getLocationsOf(uri, defPos, locations);
1527 }
1528 
1529 void lsp::PDLLServer::findReferencesOf(const URIForFile &uri,
1530                                        const Position &pos,
1531                                        std::vector<Location> &references) {
1532   auto fileIt = impl->files.find(uri.file());
1533   if (fileIt != impl->files.end())
1534     fileIt->second->findReferencesOf(uri, pos, references);
1535 }
1536 
1537 void lsp::PDLLServer::getDocumentLinks(
1538     const URIForFile &uri, std::vector<DocumentLink> &documentLinks) {
1539   auto fileIt = impl->files.find(uri.file());
1540   if (fileIt != impl->files.end())
1541     return fileIt->second->getDocumentLinks(uri, documentLinks);
1542 }
1543 
1544 Optional<lsp::Hover> lsp::PDLLServer::findHover(const URIForFile &uri,
1545                                                 const Position &hoverPos) {
1546   auto fileIt = impl->files.find(uri.file());
1547   if (fileIt != impl->files.end())
1548     return fileIt->second->findHover(uri, hoverPos);
1549   return llvm::None;
1550 }
1551 
1552 void lsp::PDLLServer::findDocumentSymbols(
1553     const URIForFile &uri, std::vector<DocumentSymbol> &symbols) {
1554   auto fileIt = impl->files.find(uri.file());
1555   if (fileIt != impl->files.end())
1556     fileIt->second->findDocumentSymbols(symbols);
1557 }
1558 
1559 lsp::CompletionList
1560 lsp::PDLLServer::getCodeCompletion(const URIForFile &uri,
1561                                    const Position &completePos) {
1562   auto fileIt = impl->files.find(uri.file());
1563   if (fileIt != impl->files.end())
1564     return fileIt->second->getCodeCompletion(uri, completePos);
1565   return CompletionList();
1566 }
1567 
1568 lsp::SignatureHelp lsp::PDLLServer::getSignatureHelp(const URIForFile &uri,
1569                                                      const Position &helpPos) {
1570   auto fileIt = impl->files.find(uri.file());
1571   if (fileIt != impl->files.end())
1572     return fileIt->second->getSignatureHelp(uri, helpPos);
1573   return SignatureHelp();
1574 }
1575 
1576 Optional<lsp::PDLLViewOutputResult>
1577 lsp::PDLLServer::getPDLLViewOutput(const URIForFile &uri,
1578                                    PDLLViewOutputKind kind) {
1579   auto fileIt = impl->files.find(uri.file());
1580   if (fileIt != impl->files.end())
1581     return fileIt->second->getPDLLViewOutput(kind);
1582   return llvm::None;
1583 }
1584