From 3ed457ab3b40dd116736a808039454ae62bf52fb Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Fri, 9 Aug 2024 11:38:26 -0700 Subject: [PATCH 1/2] [Sema] Move `getDocCommentProvidingDecl` and `getCascadingDocComment` from `AST` to `Sema` This allows use to re-use logic from Sema in those requests. This commit just moves functions around and does not change any functionality. --- include/swift/AST/Comment.h | 8 - include/swift/AST/Decl.h | 3 + include/swift/AST/PrintOptions.h | 7 - lib/AST/ASTPrinter.cpp | 2 +- lib/AST/DocComment.cpp | 226 ----------------------------- lib/IDE/CommentConversion.cpp | 28 +++- lib/Sema/CMakeLists.txt | 1 + lib/Sema/Comment.cpp | 221 ++++++++++++++++++++++++++++ lib/SymbolGraphGen/Symbol.cpp | 4 +- lib/SymbolGraphGen/SymbolGraph.cpp | 2 +- 10 files changed, 256 insertions(+), 246 deletions(-) create mode 100644 lib/Sema/Comment.cpp diff --git a/include/swift/AST/Comment.h b/include/swift/AST/Comment.h index 756e99f391314..88190defea6ea 100644 --- a/include/swift/AST/Comment.h +++ b/include/swift/AST/Comment.h @@ -96,14 +96,6 @@ class DocComment { DocComment *getSingleDocComment(swift::markup::MarkupContext &Context, const Decl *D); -/// Get the declaration that actually provides a doc comment for another. -const Decl *getDocCommentProvidingDecl(const Decl *D); - -/// Attempt to get a doc comment from the declaration, or other inherited -/// sources, like from base classes or protocols. -DocComment *getCascadingDocComment(swift::markup::MarkupContext &MC, - const Decl *D); - /// Extract comments parts from the given Markup node. swift::markup::CommentParts extractCommentParts(swift::markup::MarkupContext &MC, diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 58b46eafc0c7a..c5ecb89425d66 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -1202,6 +1202,9 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated { std::optional getSourceOrder() const; + /// Get the declaration that actually provides a doc comment for another. + const Decl *getDocCommentProvidingDecl() const; + /// \returns The brief comment attached to this declaration, or the brief /// comment attached to the comment providing decl. StringRef getSemanticBriefComment() const; diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index ff11c75330a64..0865ec21dae28 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -734,13 +734,6 @@ struct PrintOptions { return CurrentPrintabilityChecker->shouldPrint(P, *this); } - /// Retrieve the print options that are suitable to print the testable interface. - static PrintOptions printTestableInterface(bool printFullConvention) { - PrintOptions result = printInterface(printFullConvention); - result.AccessFilter = AccessLevel::Internal; - return result; - } - /// Retrieve the print options that are suitable to print interface for a /// swift file. static PrintOptions printSwiftFileInterface(bool printFullConvention) { diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index fd05ce9e004c6..4cc9b44ca829b 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -743,7 +743,7 @@ class PrintAST : public ASTVisitor { void printSwiftDocumentationComment(const Decl *D) { if (Options.CascadeDocComment) - D = getDocCommentProvidingDecl(D); + D = D->getDocCommentProvidingDecl(); if (!D) return; auto RC = D->getRawComment(); diff --git a/lib/AST/DocComment.cpp b/lib/AST/DocComment.cpp index deb2e81f38c8b..8dd6619c1a83c 100644 --- a/lib/AST/DocComment.cpp +++ b/lib/AST/DocComment.cpp @@ -391,229 +391,3 @@ DocComment *swift::getSingleDocComment(swift::markup::MarkupContext &MC, return nullptr; return DocComment::create(D, MC, RC); } - -namespace { -/// Helper class for finding the comment providing decl for either a brief or -/// raw comment. -template -class CommentProviderFinder final { - using ResultWithDecl = std::pair; - using VisitFnTy = std::optional (*)(const Decl *); - - VisitFnTy VisitFn; - -public: - CommentProviderFinder(VisitFnTy visitFn) : VisitFn(visitFn) {} - -private: - std::optional visit(const Decl *D) { - // Adapt the provided visitor function to also return the decl. - if (auto result = VisitFn(D)) - return {{*result, D}}; - return std::nullopt; - } - - std::optional findOverriddenDecl(const ValueDecl *VD) { - // Only applies to class member. - if (!VD->getDeclContext()->getSelfClassDecl()) - return std::nullopt; - - while (auto *baseDecl = VD->getOverriddenDecl()) { - if (auto result = visit(baseDecl)) - return result; - - VD = baseDecl; - } - return std::nullopt; - } - - /// Check if there is an inherited protocol that has a default implementation - /// of `VD` with a doc comment. - std::optional findDefaultProvidedDecl(const ValueDecl *VD) { - NominalTypeDecl *nominalType = - dyn_cast_or_null(VD->getDeclContext()->getAsDecl()); - if (!nominalType) { - nominalType = VD->getDeclContext()->getExtendedProtocolDecl(); - } - if (!nominalType) - return std::nullopt; - - SmallVector members; - nominalType->lookupQualified(nominalType, DeclNameRef(VD->getName()), - VD->getLoc(), NLOptions::NL_ProtocolMembers, - members); - - std::optional result; - Type vdComparisonTy = VD->getInterfaceType(); - if (!vdComparisonTy) { - return std::nullopt; - } - if (auto fnTy = vdComparisonTy->getAs()) { - // Strip off the 'Self' parameter. - vdComparisonTy = fnTy->getResult(); - } - - for (auto *member : members) { - if (isa(member) || isa(member)) { - if (VD->isStatic() != member->isStatic()) { - continue; - } - Type memberComparisonTy = member->getInterfaceType(); - if (!memberComparisonTy) { - continue; - } - if (auto fnTy = memberComparisonTy->getAs()) { - // Strip off the 'Self' parameter. - memberComparisonTy = fnTy->getResult(); - } - if (!vdComparisonTy->matches(memberComparisonTy, TypeMatchFlags::AllowOverride)) { - continue; - } - } - auto newResult = visit(member); - if (!newResult) - continue; - - if (result) { - // Found two or more decls with doc-comment. - return std::nullopt; - } - result = newResult; - } - return result; - } - - std::optional findRequirementDecl(const ValueDecl *VD) { - std::queue requirements; - while (true) { - for (auto *req : VD->getSatisfiedProtocolRequirements()) { - if (auto result = visit(req)) - return result; - - requirements.push(req); - } - if (requirements.empty()) - return std::nullopt; - - VD = requirements.front(); - requirements.pop(); - } - } - -public: - std::optional findCommentProvider(const Decl *D) { - if (auto result = visit(D)) - return result; - - auto *VD = dyn_cast(D); - if (!VD) - return std::nullopt; - - if (auto result = findOverriddenDecl(VD)) - return result; - - if (auto result = findRequirementDecl(VD)) - return result; - - if (auto result = findDefaultProvidedDecl(VD)) - return result; - - return std::nullopt; - } -}; -} // end anonymous namespace - -const Decl *swift::getDocCommentProvidingDecl(const Decl *D) { - // Search for the first decl we see with a non-empty raw comment. - auto finder = CommentProviderFinder( - [](const Decl *D) -> std::optional { - auto comment = D->getRawComment(); - if (comment.isEmpty()) - return std::nullopt; - return comment; - }); - - auto result = finder.findCommentProvider(D); - return result ? result->second : nullptr; -} - -DocComment *swift::getCascadingDocComment(swift::markup::MarkupContext &MC, - const Decl *D) { - auto *docD = getDocCommentProvidingDecl(D); - if (!docD) - return nullptr; - - auto *doc = getSingleDocComment(MC, docD); - assert(doc && "getDocCommentProvidingDecl() returned decl with no comment"); - - // If the doc-comment is inherited from other decl, add a note about it. - if (docD != D) { - doc->setDecl(D); - if (auto baseD = docD->getDeclContext()->getSelfNominalTypeDecl()) { - doc->addInheritanceNote(MC, baseD); - - // If the doc is inherited from protocol requirement, associate the - // requirement with the doc-comment. - // FIXME: This is to keep the old behavior. - if (isa(baseD)) - doc->setDecl(docD); - } - } - - return doc; -} - -/// Retrieve the brief comment for a given decl \p D, without attempting to -/// walk any requirements or overrides. -static std::optional getDirectBriefComment(const Decl *D) { - if (!D->canHaveComment()) - return std::nullopt; - - auto *ModuleDC = D->getDeclContext()->getModuleScopeContext(); - auto &Ctx = ModuleDC->getASTContext(); - - // If we expect the comment to be in the swiftdoc, check for it if we loaded a - // swiftdoc. If missing from the swiftdoc, we know it will not be in the - // swiftsourceinfo either, so we can bail early. - if (auto *Unit = dyn_cast(ModuleDC)) { - if (Unit->hasLoadedSwiftDoc()) { - auto target = getDocCommentSerializationTargetFor(D); - if (target == DocCommentSerializationTarget::SwiftDocAndSourceInfo) { - auto C = Unit->getCommentForDecl(D); - if (!C) - return std::nullopt; - - return C->Brief; - } - } - } - - // Otherwise, parse the brief from the raw comment itself. This will look into - // the swiftsourceinfo if needed. - auto RC = D->getRawComment(); - if (RC.isEmpty()) - return std::nullopt; - - SmallString<256> BriefStr; - llvm::raw_svector_ostream OS(BriefStr); - printBriefComment(RC, OS); - return Ctx.AllocateCopy(BriefStr.str()); -} - -StringRef SemanticBriefCommentRequest::evaluate(Evaluator &evaluator, - const Decl *D) const { - // Perform a walk over the potential providers of the brief comment, - // retrieving the first one we come across. - CommentProviderFinder finder(getDirectBriefComment); - auto result = finder.findCommentProvider(D); - return result ? result->first : StringRef(); -} - -StringRef Decl::getSemanticBriefComment() const { - if (!this->canHaveComment()) - return StringRef(); - - auto &eval = getASTContext().evaluator; - return evaluateOrDefault(eval, SemanticBriefCommentRequest{this}, - StringRef()); -} diff --git a/lib/IDE/CommentConversion.cpp b/lib/IDE/CommentConversion.cpp index b159abd5070d7..b792bbad61b76 100644 --- a/lib/IDE/CommentConversion.cpp +++ b/lib/IDE/CommentConversion.cpp @@ -469,6 +469,32 @@ std::string ide::extractPlainTextFromComment(const StringRef Text) { return getLineListFromComment(SourceMgr, MC, Text).str(); } +static DocComment *getCascadingDocComment(swift::markup::MarkupContext &MC, + const Decl *D) { + auto *docD = D->getDocCommentProvidingDecl(); + if (!docD) + return nullptr; + + auto *doc = getSingleDocComment(MC, docD); + assert(doc && "getDocCommentProvidingDecl() returned decl with no comment"); + + // If the doc-comment is inherited from other decl, add a note about it. + if (docD != D) { + doc->setDecl(D); + if (auto baseD = docD->getDeclContext()->getSelfNominalTypeDecl()) { + doc->addInheritanceNote(MC, baseD); + + // If the doc is inherited from protocol requirement, associate the + // requirement with the doc-comment. + // FIXME: This is to keep the old behavior. + if (isa(baseD)) + doc->setDecl(docD); + } + } + + return doc; +} + bool ide::getDocumentationCommentAsXML(const Decl *D, raw_ostream &OS, TypeOrExtensionDecl SynthesizedTarget) { auto MaybeClangNode = D->getClangNode(); @@ -519,7 +545,7 @@ bool ide::getRawDocumentationComment(const Decl *D, raw_ostream &OS) { return true; } - const Decl *docD = getDocCommentProvidingDecl(D); + const Decl *docD = D->getDocCommentProvidingDecl(); if (!docD) { return false; } diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index b36735ae224e2..d56fdbec5e219 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -2,6 +2,7 @@ add_swift_host_library(swiftSema STATIC AssociatedTypeInference.cpp BuilderTransform.cpp + Comment.cpp CSApply.cpp CSBindings.cpp CSSyntacticElement.cpp diff --git a/lib/Sema/Comment.cpp b/lib/Sema/Comment.cpp new file mode 100644 index 0000000000000..84e1a96faea47 --- /dev/null +++ b/lib/Sema/Comment.cpp @@ -0,0 +1,221 @@ +//===--- CommentConversion.cpp - Conversion of comments to other formats --===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/Comment.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/Decl.h" +#include "swift/AST/TypeCheckRequests.h" +#include + +using namespace swift; + +namespace { +/// Helper class for finding the comment providing decl for either a brief or +/// raw comment. +template +class CommentProviderFinder final { + using ResultWithDecl = std::pair; + using VisitFnTy = std::optional (*)(const Decl *); + + VisitFnTy VisitFn; + +public: + CommentProviderFinder(VisitFnTy visitFn) : VisitFn(visitFn) {} + +private: + std::optional visit(const Decl *D) { + // Adapt the provided visitor function to also return the decl. + if (auto result = VisitFn(D)) + return {{*result, D}}; + return std::nullopt; + } + + std::optional findOverriddenDecl(const ValueDecl *VD) { + // Only applies to class member. + if (!VD->getDeclContext()->getSelfClassDecl()) + return std::nullopt; + + while (auto *baseDecl = VD->getOverriddenDecl()) { + if (auto result = visit(baseDecl)) + return result; + + VD = baseDecl; + } + return std::nullopt; + } + + /// Check if there is an inherited protocol that has a default implementation + /// of `VD` with a doc comment. + std::optional findDefaultProvidedDecl(const ValueDecl *VD) { + NominalTypeDecl *nominalType = + dyn_cast_or_null(VD->getDeclContext()->getAsDecl()); + if (!nominalType) { + nominalType = VD->getDeclContext()->getExtendedProtocolDecl(); + } + if (!nominalType) + return std::nullopt; + + SmallVector members; + nominalType->lookupQualified(nominalType, DeclNameRef(VD->getName()), + VD->getLoc(), NLOptions::NL_ProtocolMembers, + members); + + std::optional result; + Type vdComparisonTy = VD->getInterfaceType(); + if (!vdComparisonTy) { + return std::nullopt; + } + if (auto fnTy = vdComparisonTy->getAs()) { + // Strip off the 'Self' parameter. + vdComparisonTy = fnTy->getResult(); + } + + for (auto *member : members) { + if (isa(member) || + isa(member)) { + if (VD->isStatic() != member->isStatic()) { + continue; + } + Type memberComparisonTy = member->getInterfaceType(); + if (!memberComparisonTy) { + continue; + } + if (auto fnTy = memberComparisonTy->getAs()) { + // Strip off the 'Self' parameter. + memberComparisonTy = fnTy->getResult(); + } + if (!vdComparisonTy->matches(memberComparisonTy, + TypeMatchFlags::AllowOverride)) { + continue; + } + } + auto newResult = visit(member); + if (!newResult) + continue; + + if (result) { + // Found two or more decls with doc-comment. + return std::nullopt; + } + result = newResult; + } + return result; + } + + std::optional findRequirementDecl(const ValueDecl *VD) { + std::queue requirements; + while (true) { + for (auto *req : VD->getSatisfiedProtocolRequirements()) { + if (auto result = visit(req)) + return result; + + requirements.push(req); + } + if (requirements.empty()) + return std::nullopt; + + VD = requirements.front(); + requirements.pop(); + } + } + +public: + std::optional findCommentProvider(const Decl *D) { + if (auto result = visit(D)) + return result; + + auto *VD = dyn_cast(D); + if (!VD) + return std::nullopt; + + if (auto result = findOverriddenDecl(VD)) + return result; + + if (auto result = findRequirementDecl(VD)) + return result; + + if (auto result = findDefaultProvidedDecl(VD)) + return result; + + return std::nullopt; + } +}; +} // end anonymous namespace + +const Decl *Decl::getDocCommentProvidingDecl() const { + // Search for the first decl we see with a non-empty raw comment. + auto finder = CommentProviderFinder( + [](const Decl *D) -> std::optional { + auto comment = D->getRawComment(); + if (comment.isEmpty()) + return std::nullopt; + return comment; + }); + + auto result = finder.findCommentProvider(this); + return result ? result->second : nullptr; +} + +StringRef swift::Decl::getSemanticBriefComment() const { + if (!canHaveComment()) + return StringRef(); + + auto &eval = getASTContext().evaluator; + return evaluateOrDefault(eval, SemanticBriefCommentRequest{this}, + StringRef()); +} + +/// Retrieve the brief comment for a given decl \p D, without attempting to +/// walk any requirements or overrides. +static std::optional getDirectBriefComment(const Decl *D) { + if (!D->canHaveComment()) + return std::nullopt; + + auto *ModuleDC = D->getDeclContext()->getModuleScopeContext(); + auto &Ctx = ModuleDC->getASTContext(); + + // If we expect the comment to be in the swiftdoc, check for it if we loaded a + // swiftdoc. If missing from the swiftdoc, we know it will not be in the + // swiftsourceinfo either, so we can bail early. + if (auto *Unit = dyn_cast(ModuleDC)) { + if (Unit->hasLoadedSwiftDoc()) { + auto target = getDocCommentSerializationTargetFor(D); + if (target == DocCommentSerializationTarget::SwiftDocAndSourceInfo) { + auto C = Unit->getCommentForDecl(D); + if (!C) + return std::nullopt; + + return C->Brief; + } + } + } + + // Otherwise, parse the brief from the raw comment itself. This will look into + // the swiftsourceinfo if needed. + auto RC = D->getRawComment(); + if (RC.isEmpty()) + return std::nullopt; + + SmallString<256> BriefStr; + llvm::raw_svector_ostream OS(BriefStr); + printBriefComment(RC, OS); + return Ctx.AllocateCopy(BriefStr.str()); +} + +StringRef SemanticBriefCommentRequest::evaluate(Evaluator &evaluator, + const Decl *D) const { + // Perform a walk over the potential providers of the brief comment, + // retrieving the first one we come across. + CommentProviderFinder finder(getDirectBriefComment); + auto result = finder.findCommentProvider(D); + return result ? result->first : StringRef(); +} diff --git a/lib/SymbolGraphGen/Symbol.cpp b/lib/SymbolGraphGen/Symbol.cpp index fcc3352e616a4..70dce0675ed46 100644 --- a/lib/SymbolGraphGen/Symbol.cpp +++ b/lib/SymbolGraphGen/Symbol.cpp @@ -213,7 +213,7 @@ void Symbol::serializeRange(size_t InitialIndentation, const ValueDecl *Symbol::getDeclInheritingDocs() const { // get the decl that would provide docs for this symbol const auto *DocCommentProvidingDecl = - dyn_cast_or_null(getDocCommentProvidingDecl(D)); + dyn_cast_or_null(D->getDocCommentProvidingDecl()); // if the decl is the same as the one for this symbol, we're not // inheriting docs, so return null. however, if this symbol is @@ -359,7 +359,7 @@ void Symbol::serializeDocComment(llvm::json::OStream &OS) const { const auto *DocCommentProvidingDecl = D; if (!Graph->Walker.Options.SkipInheritedDocs) { DocCommentProvidingDecl = - dyn_cast_or_null(getDocCommentProvidingDecl(D)); + dyn_cast_or_null(D->getDocCommentProvidingDecl()); if (!DocCommentProvidingDecl) { DocCommentProvidingDecl = D; } diff --git a/lib/SymbolGraphGen/SymbolGraph.cpp b/lib/SymbolGraphGen/SymbolGraph.cpp index 5c239f93efc71..e25b61a2c682d 100644 --- a/lib/SymbolGraphGen/SymbolGraph.cpp +++ b/lib/SymbolGraphGen/SymbolGraph.cpp @@ -727,7 +727,7 @@ bool SymbolGraph::isImplicitlyPrivate(const Decl *D, // If we've been asked to skip protocol implementations, filter them out here. if (Walker.Options.SkipProtocolImplementations && getProtocolRequirement(VD)) { // Allow them to stay if they have their own doc comment - const auto *DocCommentProvidingDecl = getDocCommentProvidingDecl(VD); + const auto *DocCommentProvidingDecl = VD->getDocCommentProvidingDecl(); if (DocCommentProvidingDecl != VD) return true; } From f169bdbd543ff6ed60a8391296e2105496a98db4 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 7 Aug 2024 17:29:42 -0700 Subject: [PATCH 2/2] [IDE] Call into `matchWitnessStructure` to check if we should inherit a comment from an overridden declaration --- include/swift/AST/TypeCheckRequests.h | 18 +++++++ include/swift/AST/TypeCheckerTypeIDZone.def | 3 ++ lib/AST/DocComment.cpp | 14 +++++ lib/Sema/Comment.cpp | 23 ++++---- lib/Sema/TypeCheckProtocol.cpp | 59 +++++++++++++++------ lib/Sema/TypeChecker.h | 7 +++ 6 files changed, 94 insertions(+), 30 deletions(-) diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 62785a878ec1a..8209fcfae13ea 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -4684,6 +4684,24 @@ class RawCommentRequest bool isCached() const { return true; } }; +/// Get the declaration that actually provides a doc comment for another. +class DocCommentProvidingDeclRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + const Decl *evaluate(Evaluator &evaluator, const Decl *D) const; + +public: + // Separate caching. + bool isCached() const { return true; } +}; + /// Retrieve the brief portion of a declaration's document comment, potentially /// walking to find the comment providing decl if needed. class SemanticBriefCommentRequest diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index a68ac6b598c14..f4bb0a717e328 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -530,6 +530,9 @@ SWIFT_REQUEST(TypeChecker, LocalDiscriminatorsRequest, SWIFT_REQUEST(TypeChecker, RawCommentRequest, RawComment(const Decl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, DocCommentProvidingDeclRequest, + const Decl *(const Decl *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, SemanticBriefCommentRequest, StringRef(const Decl *), Cached, NoLocationInfo) diff --git a/lib/AST/DocComment.cpp b/lib/AST/DocComment.cpp index 8dd6619c1a83c..f05eb8095dbb1 100644 --- a/lib/AST/DocComment.cpp +++ b/lib/AST/DocComment.cpp @@ -391,3 +391,17 @@ DocComment *swift::getSingleDocComment(swift::markup::MarkupContext &MC, return nullptr; return DocComment::create(D, MC, RC); } + +const Decl *Decl::getDocCommentProvidingDecl() const { + return evaluateOrDefault(getASTContext().evaluator, + DocCommentProvidingDeclRequest{this}, nullptr); +} + +StringRef Decl::getSemanticBriefComment() const { + if (!canHaveComment()) + return StringRef(); + + auto &eval = getASTContext().evaluator; + return evaluateOrDefault(eval, SemanticBriefCommentRequest{this}, + StringRef()); +} diff --git a/lib/Sema/Comment.cpp b/lib/Sema/Comment.cpp index 84e1a96faea47..bb65f30c2f8af 100644 --- a/lib/Sema/Comment.cpp +++ b/lib/Sema/Comment.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "swift/AST/Comment.h" +#include "TypeChecker.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/TypeCheckRequests.h" @@ -80,9 +81,11 @@ class CommentProviderFinder final { } for (auto *member : members) { - if (isa(member) || - isa(member)) { - if (VD->isStatic() != member->isStatic()) { + if (VD == member || member->isInvalid()) { + continue; + } + if (!isa(member)) { + if (!swift::TypeChecker::witnessStructureMatches(member, VD)) { continue; } Type memberComparisonTy = member->getInterfaceType(); @@ -151,7 +154,8 @@ class CommentProviderFinder final { }; } // end anonymous namespace -const Decl *Decl::getDocCommentProvidingDecl() const { +const Decl *DocCommentProvidingDeclRequest::evaluate(Evaluator &evaluator, + const Decl *D) const { // Search for the first decl we see with a non-empty raw comment. auto finder = CommentProviderFinder( [](const Decl *D) -> std::optional { @@ -161,19 +165,10 @@ const Decl *Decl::getDocCommentProvidingDecl() const { return comment; }); - auto result = finder.findCommentProvider(this); + auto result = finder.findCommentProvider(D); return result ? result->second : nullptr; } -StringRef swift::Decl::getSemanticBriefComment() const { - if (!canHaveComment()) - return StringRef(); - - auto &eval = getASTContext().evaluator; - return evaluateOrDefault(eval, SemanticBriefCommentRequest{this}, - StringRef()); -} - /// Retrieve the brief comment for a given decl \p D, without attempting to /// walk any requirements or overrides. static std::optional getDirectBriefComment(const Decl *D) { diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp index c0230f306144d..c67a152ac5836 100644 --- a/lib/Sema/TypeCheckProtocol.cpp +++ b/lib/Sema/TypeCheckProtocol.cpp @@ -499,15 +499,12 @@ checkEffects(AbstractStorageDecl *witness, AbstractStorageDecl *req) { return std::nullopt; // OK } -RequirementMatch swift::matchWitness( - DeclContext *dc, ValueDecl *req, ValueDecl *witness, - llvm::function_ref< - std::tuple, Type, Type>(void)> - setup, - llvm::function_ref(Type, Type)> matchTypes, - llvm::function_ref)> - finalize) { - +/// Implementation of `matchWitnessStructure` that also sets a few out paramters +/// to be used by `matchWitness`. +static std::optional +matchWitnessStructureImpl(ValueDecl *req, ValueDecl *witness, + bool &decomposeFunctionType, bool &ignoreReturnType, + Type &reqThrownError, Type &witnessThrownError) { assert(!req->isInvalid() && "Cannot have an invalid requirement here"); /// Make sure the witness is of the same kind as the requirement. @@ -530,7 +527,7 @@ RequirementMatch swift::matchWitness( if (witness->isRecursiveValidation()) { return RequirementMatch(witness, MatchKind::Circularity); } - + // If the witness is invalid, record that and stop now. if (witness->isInvalid()) { return RequirementMatch(witness, MatchKind::WitnessInvalid); @@ -541,10 +538,6 @@ RequirementMatch swift::matchWitness( const auto &witnessAttrs = witness->getAttrs(); // Perform basic matching of the requirement and witness. - bool decomposeFunctionType = false; - bool ignoreReturnType = false; - Type reqThrownError; - Type witnessThrownError; if (isa(req) && isa(witness)) { auto funcReq = cast(req); auto funcWitness = cast(witness); @@ -617,7 +610,7 @@ RequirementMatch swift::matchWitness( decomposeFunctionType = true; } else if (auto *witnessASD = dyn_cast(witness)) { auto *reqASD = cast(req); - + // Check that the static-ness matches. if (reqASD->isStatic() != witnessASD->isStatic()) return RequirementMatch(witness, MatchKind::StaticNonStaticConflict); @@ -701,8 +694,42 @@ RequirementMatch swift::matchWitness( // If the requirement is @objc, the witness must not be marked with @nonobjc. // @objc-ness will be inferred (separately) and the selector will be checked // later. - if (req->isObjC() && witness->getAttrs().hasAttribute()) + if (req->isObjC() && witness->getAttrs().hasAttribute()) { return RequirementMatch(witness, MatchKind::NonObjC); + } + return std::nullopt; +} + +bool swift::TypeChecker::witnessStructureMatches(ValueDecl *req, + const ValueDecl *witness) { + bool decomposeFunctionType = false; + bool ignoreReturnType = false; + Type reqThrownError; + Type witnessThrownError; + return matchWitnessStructureImpl(req, const_cast(witness), + decomposeFunctionType, ignoreReturnType, + reqThrownError, + witnessThrownError) == std::nullopt; +} + +RequirementMatch swift::matchWitness( + DeclContext *dc, ValueDecl *req, ValueDecl *witness, + llvm::function_ref< + std::tuple, Type, Type>(void)> + setup, + llvm::function_ref(Type, Type)> matchTypes, + llvm::function_ref)> + finalize) { + bool decomposeFunctionType = false; + bool ignoreReturnType = false; + Type reqThrownError; + Type witnessThrownError; + + if (auto StructuralMismatch = matchWitnessStructureImpl( + req, witness, decomposeFunctionType, ignoreReturnType, reqThrownError, + witnessThrownError)) { + return *StructuralMismatch; + } // Set up the match, determining the requirement and witness types // in the process. diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 19e992aa933cf..dc309ae16f5e0 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -487,6 +487,13 @@ void diagnoseDuplicateCaptureVars(CaptureListExpr *expr); Type checkReferenceOwnershipAttr(VarDecl *D, Type interfaceType, ReferenceOwnershipAttr *attr); +/// Checks if the given witness can structurally match the requirement, ie. +/// whether it has a matching kind like static vs. non-static, matching decl, +/// etc. It does not check if the types of the witness is able to satisfy the +/// requirement. +/// If there is a matching failure, returns a `false`, otherwise return `true`. +bool witnessStructureMatches(ValueDecl *req, const ValueDecl *witness); + /// Infer default value witnesses for all requirements in the given protocol. void inferDefaultWitnesses(ProtocolDecl *proto);