-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Index][CodeCompletion] Fix #keyPath indexing when a type is referenced #20020
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -218,8 +218,16 @@ Optional<Type> TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, | |
diag::expr_unsupported_objc_key_path_component, | ||
(unsigned)kind); | ||
continue; | ||
case KeyPathExpr::Component::Kind::OptionalWrap: | ||
case KeyPathExpr::Component::Kind::Property: | ||
case KeyPathExpr::Component::Kind::Type: { | ||
//For code completion purposes, we only care about the last expression. | ||
if (&component == &expr->getComponents().back()) { | ||
return component.getComponentType(); | ||
} else { | ||
continue; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is also changing the behavior for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's because you could in theory be reaching the keypath property from other properties too, like in |
||
} | ||
} | ||
case KeyPathExpr::Component::Kind::OptionalWrap: | ||
case KeyPathExpr::Component::Kind::Subscript: | ||
llvm_unreachable("already resolved!"); | ||
} | ||
|
@@ -321,10 +329,11 @@ Optional<Type> TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, | |
if (auto var = dyn_cast<VarDecl>(found)) { | ||
// Resolve this component to the variable we found. | ||
auto varRef = ConcreteDeclRef(var); | ||
Type type = var->getInterfaceType(); | ||
auto resolved = | ||
KeyPathExpr::Component::forProperty(varRef, Type(), componentNameLoc); | ||
KeyPathExpr::Component::forProperty(varRef, type, componentNameLoc); | ||
resolvedComponents.push_back(resolved); | ||
updateState(/*isProperty=*/true, var->getInterfaceType()); | ||
updateState(/*isProperty=*/true, type); | ||
|
||
// Check that the property is @objc. | ||
if (!var->isObjC()) { | ||
|
@@ -390,6 +399,11 @@ Optional<Type> TypeChecker::checkObjCKeyPathExpr(DeclContext *dc, | |
break; | ||
} | ||
|
||
// Resolve this component to the type we found. | ||
auto typeRef = ConcreteDeclRef(type); | ||
auto resolved = | ||
KeyPathExpr::Component::forType(typeRef, newType, componentNameLoc); | ||
resolvedComponents.push_back(resolved); | ||
updateState(/*isProperty=*/false, newType); | ||
continue; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,7 +57,7 @@ func testKeyPath(a: A, b: B) { | |
let _: String = #keyPath(A.propString) | ||
|
||
// Property of String property (which looks on NSString) | ||
let _: String = #keyPath(A.propString.URLsInText) | ||
let _: String = #keyPath(A.propString.urlsInText) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not clear to me, was this change necessary because of your other changes ? I don't see how. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test started to fail stating the name of the property changed, and I assumed it wasn't the case before because the expression wasn't being indexed, although it does seem weird. Was it not supposed to happen?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 'Indexing' them should not have any effect in compiler errors, is the case that these were not even typechecked before ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you 100% sure your changes are triggering the failure ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can actually reproduce this in Xcode 10 with old Swift 2 properties:
It will work and return
EDIT: After debugging the rename warning, I was able to confirm that they weren't being fully typechecked before as the changes now result in this specific logic getting called: As the key paths that included types never stopped being This was the main culprit: https://github.com/apple/swift/blob/df64e8a5dcd96e2b402be4445e0d8e913fedf71b/lib/Sema/TypeCheckExprObjC.cpp#L417 which got treated by: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @DougGregor @rudkx please review, with these changes, properties from type references in #keypaths are properly typechecked now. |
||
|
||
// String property with a suffix | ||
let _: String = #keyPath(A.propString).description | ||
|
@@ -72,36 +72,36 @@ func testKeyPath(a: A, b: B) { | |
|
||
// Array property (make sure we look at the array element). | ||
let _: String = #keyPath(A.propArray) | ||
let _: String = #keyPath(A.propArray.URLsInText) | ||
let _: String = #keyPath(A.propArray.urlsInText) | ||
|
||
// Dictionary property (make sure we look at the value type). | ||
let _: String = #keyPath(A.propDict.anyKeyName) | ||
let _: String = #keyPath(A.propDict.anyKeyName.propA) | ||
|
||
// Set property (make sure we look at the set element). | ||
let _: String = #keyPath(A.propSet) | ||
let _: String = #keyPath(A.propSet.URLsInText) | ||
let _: String = #keyPath(A.propSet.urlsInText) | ||
|
||
// AnyObject property | ||
let _: String = #keyPath(A.propAnyObject.URLsInText) | ||
let _: String = #keyPath(A.propAnyObject.urlsInText) | ||
let _: String = #keyPath(A.propAnyObject.propA) | ||
let _: String = #keyPath(A.propAnyObject.propB) | ||
let _: String = #keyPath(A.propAnyObject.description) | ||
|
||
// NSString property | ||
let _: String = #keyPath(A.propNSString.URLsInText) | ||
let _: String = #keyPath(A.propNSString.urlsInText) | ||
|
||
// NSArray property (AnyObject array element). | ||
let _: String = #keyPath(A.propNSArray) | ||
let _: String = #keyPath(A.propNSArray.URLsInText) | ||
let _: String = #keyPath(A.propNSArray.urlsInText) | ||
|
||
// NSDictionary property (AnyObject value type). | ||
let _: String = #keyPath(A.propNSDict.anyKeyName) | ||
let _: String = #keyPath(A.propNSDict.anyKeyName.propA) | ||
|
||
// NSSet property (AnyObject set element). | ||
let _: String = #keyPath(A.propNSSet) | ||
let _: String = #keyPath(A.propNSSet.URLsInText) | ||
let _: String = #keyPath(A.propNSSet.urlsInText) | ||
|
||
// Property with keyword name. | ||
let _: String = #keyPath(A.repeat) | ||
|
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.
It's not clear to me how this relates to code completion.
Uh oh!
There was an error while loading. Please reload this page.
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 might have treated this in the wrong place, but the issue I was having was that this method seems to be called twice on code completion - first to resolve the properties and then again in order to return the property that's trying to be completed, as far as I could tell. I couldn't find another case where this happened, so I assumed that code completion was the only case where the resolved enum cases would trigger here. Is it the wrong place?
The main idea for this block is that if I had something like
#keyPath(MyType.myProperty.
, I had to ignore the type and returnmyProperty
for code completion to show the correct results.