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 ¬e : 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