-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Inherit doc comments from default implementations of inherited protocols #73066
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inherit doc comments from default implementations of inherited protocols #73066
Conversation
lib/AST/DocComment.cpp
Outdated
@@ -426,24 +426,24 @@ class CommentProviderFinder final { | |||
return std::nullopt; | |||
} | |||
|
|||
/// Check if there is an inherited protocol that has a default implementaiton |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Check if there is an inherited protocol that has a default implementaiton | |
/// Check if there is an inherited protocol that has a default implementation |
@@ -0,0 +1,34 @@ | |||
protocol BaseProtocol {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could add a test for the case where there is both a requirement and a default
619a27a
to
bdfe29f
Compare
@swift-ci Please smoke test |
NominalTypeDecl *nominalType = | ||
dyn_cast_or_null<NominalTypeDecl>(VD->getDeclContext()->getAsDecl()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if VD
is defined in an extension? Do we want to support that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do with the lines below, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That only handles protocol extensions, right? I was thinking of e.g a struct extension
bdfe29f
to
a174859
Compare
// `RemoteP.P` also has an extension with a default implementation for `extraFunc` that does have | ||
// docs, but overriding it here should prevent those from appearing | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@QuietMisdreavus This comments seems to imply the exact opposite of what I want to achieve in this PR. Is there any reason why we didn’t want to inherit documentation from extensions?
For context, the concrete example I want to fix with this PR is that this implementation of _ArrayProtocol.filter
https://github.com/apple/swift/blob/c531f2914fda0b84d8af091a0081799101f76d9b/stdlib/public/core/ArrayType.swift#L73-L78 should inherit the documentation from the documentation of Sequence.filter
, which is implemented in an extension https://github.com/apple/swift/blob/5df0f053d95c1143ac34ac88faafdfa3902a3224/stdlib/public/core/Sequence.swift#L717-L739
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @franklinsch (and possibly also @bitjammer for the underlying discussion of inheriting docs in the first place)
I think it should respect the presence of the -skip-inherited-docs
flag, but otherwise i don't remember off the top of my head.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we already respect the -skip-inherited-docs
flag because we’re not calling into getDocCommentProvidingDecl
if it is set and this PR only modifies getDocCommentProvidingDecl
lib/AST/DocComment.cpp
Outdated
if (!memberComparisonTy->matches(vdComparisonTy, TypeMatchFlags::AllowOverride)) { | ||
continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be the other way around? e.g allowing:
protocol P {}
extension P {
/// hello
func foo() -> Int? { 0 }
}
struct S: P {
func foo() -> Int { 0 }
}
But not:
protocol P {}
extension P {
/// hello
func foo() -> Int { 0 }
}
struct S: P {
func foo() -> Int? { 0 }
}
Not that these are really overrides anyway, but if we want similar type-matching rules, then I think it makes sense to flip the ordering.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, yes. I thought that I had this case covered with the throws
case but apparently throws
works either way 🤷🏽
nominalType->lookupQualified(nominalType, DeclNameRef(VD->getName()), | ||
VD->getLoc(), NLOptions::NL_ProtocolMembers, | ||
members); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thought of a couple more edge cases :)
- static mismatch
protocol P {}
extension P {
/// hello
static func foo() {}
}
struct S: P {
func foo() {}
}
func bar(_ x: S) {
x.foo()
}
- enum case matching a function (if we want this to work it would need to be a static function)
protocol P {}
extension P {
/// hello
func foo(_ x: Int) -> E { .foo(0) }
}
enum E: P {
case foo(Int)
}
func bar(_ x: E) {
E.foo(0)
}
- this is quite silly, but technically this would match:
class C {
/// hello
struct foo {}
}
class D: C {
var foo: C.foo.Type { C.foo.self }
}
func bar(_ x: D) {
let y = x.foo
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good cases, thanks. Case 3 does inherit but I decided to not care and not add a test case for it. If it broke, I don’t think we would care.
if (auto fnTy = vdComparisonTy->getAs<AnyFunctionType>()) { | ||
// Strip off the 'Self' parameter. | ||
vdComparisonTy = fnTy->getResult(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wonder if we ought to have a utility for removing the curried self, could be used in a couple of other places:
a174859
to
0795102
Compare
@swift-ci Please smoke test |
0795102
to
d2b3942
Compare
@swift-ci Please smoke test |
@@ -426,24 +426,49 @@ class CommentProviderFinder final { | |||
return std::nullopt; | |||
} | |||
|
|||
/// Check if there is an inherited protocol that has a default implementation | |||
/// of `VD` with a doc comment. | |||
std::optional<ResultWithDecl> findDefaultProvidedDecl(const ValueDecl *VD) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just duplicating logic in the witness checker. Default witnesses are recorded in the ProtocolDecl.
See TypeChecker::inferDefaultWitnesses() and ProtocolDecl::getDefaultWitness().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That’s not sufficient though. We aren’t dealing with protocol requirements here, just default methods on a protocol.
For example in the following test, InheritedProtocol.hello()
needs to inherit the documentation from BaseProtocol.hello
but there is no protocol requirement that hello()
can be a witness for. Any suggestion of how to implement that without my implementation?
protocol BaseProtocol {}
extension BaseProtocol {
/// Say hello
func hello() { }
}
protocol InheritedProtocol: BaseProtocol {}
extension InheritedProtocol {
func hello() { }
}
For example when we have ```swift protocol BaseProtocol {} extension BaseProtocol { /// Say hello func hello() { } } struct MyStruct: BaseProtocol { func hello() {} } ``` Then `MyStruct.hello` should inherit the doc comment from the implementation of `BaseProtocol.hello`. Currently no doc comment is associated with `MyStruct.hello`. rdar://126240546
d2b3942
to
eec3d75
Compare
@swift-ci Please smoke test Linux |
For example when we have
Then
MyStruct.hello
should inherit the doc comment from the implementation ofBaseProtocol.hello
. Currently no doc comment is associated withMyStruct.hello
.rdar://126240546
The changes in offsets in the test cases is because
init(rawValue:)
now inherits the doc comment ofRawRepresentable
, which shifts the lines in generated interfaces around.