brintos

brintos / llvm-project-archived public Read only

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