-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[CodeCompletion] Missing init completions for dotExpr #16868
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
[CodeCompletion] Missing init completions for dotExpr #16868
Conversation
Fixes non-visible inherited convenience initializers Fixes non-visible initializers from protocol extensions Fixes visible initializers on dynamic metatypes for postfix and parenPostfix expressions
restore accidentally removed constructors from 'self' replace illegal direct call completions on dynamic metatypes with .init, except archetypes.
f5cc50f
to
7ef24b1
Compare
/cc @benlangmuir |
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.
Thanks for working on this!
class A { init(foo: Int) {}} func test() { var B = A.self B(#^HERE^# // will show global completion results }
IMO this is incorrect and there shouldn't be any completions at all if the sink is empty to begin with, but nevertheless, is this is supposed to be correct?
If something fails - e.g. cannot typecheck B - then we should assume conservatively that expressions are valid. Also while B(1) is rejected, B\n(1) is not, so it's not a bad idea to be lenient here in general.
lib/IDE/CodeCompletion.cpp
Outdated
Reason == DeclVisibilityKind::MemberOfCurrentNominal || | ||
Reason == DeclVisibilityKind::MemberOfSuper || | ||
Reason == DeclVisibilityKind::MemberOfProtocolImplementedByCurrentNominal; | ||
auto instTy = ExprType->castTo<AnyMetatypeType>()->getInstanceType(); |
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 shouldn't be using ExprType
directly here, since we don't know its relation to this call to addConstructorCall
- see addConstructorCallsFromType
for example where we don't know what ExprType
will be. It looks like the intention was that this code either uses BaseType
(if it is specified) or ExprType
- e.g. inside of getTypeOfMember
. That's pretty hard to understand, and in practice it looks like BaseType
won't always be a metatype even though it sets IsOnMetatype=true
.
How do you feel about the following?
- Make
BaseType
required whenIsOnMetatype=true
and pass inExprType
when we call this method. - Either accept both metatypes and non-metatypes for
BaseType
and handle that explicitly (e.g. if it's a metatype, get the instance type otherwise use the type directly), or require callers to always provide one or the other and assert that (e.g. always pass a metatype or always pass a non-metatype).
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.
To be honest I don't like the name isOnMetatype
at all. We don't call initializers on metatypes to begin with, right? I renamed it to isOnType
, made some clean-up and changed the code not to have a need to use ExprType
here. I decided not to touch anything else yet, since getTypeOfMember
, which is the only place where baseType
is used, is incorrectly used as well (see SR-7802). After fixing that, we might not need baseType
anymore.
lib/IDE/CodeCompletion.cpp
Outdated
addConstructorCall(CD, Reason, None, Result); | ||
if (IsStaticMetatype || CD->isRequired() || IsSelfRefExpr || | ||
Ty->is<ArchetypeType>() || | ||
!(Ty->is<ClassType>() || HaveLParen)) |
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 condition needs a comment explaining what's going on. For example, why are we checking IsSelfRefExpr
or is<ArchetypeType>
?
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 agree. In fact I got it wrong and I might have avoided mistakes with a comment. I will add failing tests to make sure the correct expression doesn't break without consequences.
Roughly, I've fixed all of the following cases concerning SR-7789 and cleaned up the code. class Foo {
init() {}
init(a : Int) {}
init(foo: String) {
self.#^HERE^# // shows initializers
}
class func foo() {
self.#^HERE^# // doesn't show initializers
}
}
class Derived: Foo {
class func foo() {
super.#^HERE^# // doesn't show initializers
}
convenience init() {
super.#^HERE^# // shows initializers
}
}
func foo() {
var MT= Foo.self
MT.#^HERE^# // no initializers (OK)
MT(#^HERE^# // shows initializers
MT#^HERE^# // shows initializers
}
protocol P {
init(bar: Int)
}
func test<S: P>(_ arg: S) {
S.#^HERE^# // doesn't show initializers
} |
But there's a problem: I can't make the class func foo() {
super.#^HERE^# // shows super init completions
} test to pass in |
When the test fails it should print out the exact command line that it runs for every RUN line, so you should be able to use that to reproduce. |
Great...It didn't work because the two checks were together and the first one apparently prevents the following one from typechecking. class func test3() {
super#^CLASS_FUNC_SUPER_NODOT^#
super.#^CLASS_FUNC_SUPER_DOT^#
} This breaks class func test3() {
super.#^CLASS_FUNC_SUPER_DOT^#
super#^CLASS_FUNC_SUPER_NODOT^#
} |
Just in case, ready to proceed :) |
test/IDE/complete_after_super.swift
Outdated
} | ||
// CLASS_FUNC_SUPER: Decl[Constructor]/CurrNominal: {{.init|init}}()[#SuperBaseB#] | ||
// CLASS_FUNC_SUPER: Decl[Constructor]/CurrNominal: {{.init|init}}({#a: Double#})[#SuperBaseB#] | ||
// CLASS_FUNC_SUPER: Decl[Constructor]/CurrNominal: {{.init|init}}({#int: Int#})[#SuperBaseB#] | ||
} |
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 brace is now unmatched after your changes (it was originally matching the one in test3() {
).
@swift-ci please smoke test |
Ready to ship? |
Yup, thanks for the reminder! |
Fixes non-visible inherited convenience initializers
Fixes non-visible initializers from protocol extensions
Fixes visible initializers on dynamic metatypes for postfix and parenPostfix expressions
Resolves SR-2206, SR-7256, SR-7789, the last being a bug found while fixing the other 2.
Note
The following logic causes global completion results (all of the literals, stdlib decls etc.) to show when there aren't any function completions found for
ParenPostfix
. This includes cases when nothing is found, some of which are directly related to SR-7789 – before, they showed initializers, now, being fixed, they show nothing (empty completion results) and thus fall under the case when global lookup is done and shown.https://github.com/apple/swift/blob/ba9cc174e457704c1d9e959a0bac3cb28b9fd9d8/lib/IDE/CodeCompletion.cpp#L5404-L5407
Example:
IMO this is incorrect and there shouldn't be any completions at all if the sink is empty to begin with, but nevertheless, is this is supposed to be correct?
UDP
SR-7789 is fixed, however. I think this should be moved to a separate bug report if needed. I recently found out it's present in some cases which aren't related to SR-7789. In particular, parenPostfix initializer completions for some generic types.