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