-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Reuse checker's property accessibility check for completions #45388
Conversation
This reverts commit 8dd1cf6.
src/compiler/types.ts
Outdated
@@ -4354,6 +4354,7 @@ namespace ts { | |||
|
|||
/* @internal */ getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): readonly TypeParameter[] | undefined; | |||
/* @internal */ isDeclarationVisible(node: Declaration | AnyImportSyntax): boolean; | |||
/* @internal */ isPropertyAccessible(node: Node, isSuper: boolean, writing: boolean, type: Type, prop: Symbol): boolean; |
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'm guessing this should be internal, unless we add some code here that determines what value isSuper
and writing
should be, and possibly get the type or the property symbol ourselves instead of as arguments to avoid incorrect usages of the function.
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 would definitely start this as internal. When are isSuper
and writing
true? I think calculating them for the caller is a good idea.
I had another idea in a comment in completions.ts
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.
Decided this function shouldn't (and actually can't) calculate isSuper
and isWrite
. This function does not assume we have an existing property access-y node, which we would need to calculate those arguments, so the caller should specify what are the appropriate arguments.
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 making completions use the same algorithm as the checker is a good idea.
src/compiler/types.ts
Outdated
@@ -4354,6 +4354,7 @@ namespace ts { | |||
|
|||
/* @internal */ getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): readonly TypeParameter[] | undefined; | |||
/* @internal */ isDeclarationVisible(node: Declaration | AnyImportSyntax): boolean; | |||
/* @internal */ isPropertyAccessible(node: Node, isSuper: boolean, writing: boolean, type: Type, prop: Symbol): boolean; |
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 would definitely start this as internal. When are isSuper
and writing
true? I think calculating them for the caller is a good idea.
I had another idea in a comment in completions.ts
I agree with Nathan - this functionality shouldn't move too much around in the checker, and should instead keep consistent with the existing styles. |
7631247
to
4ffa810
Compare
} | ||
// In js files properties of unions are allowed in completion |
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.
Deleted this special casing of JS files because it's already dealt with in completions by addTypeProperties
.
@@ -4216,7 +4216,7 @@ namespace ts { | |||
|
|||
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number | undefined; | |||
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName | ImportTypeNode, propertyName: string): boolean; | |||
/** Exclude accesses to private properties or methods with a `this` parameter that `type` doesn't satisfy. */ |
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.
Updating comment since the implementation of this function no longer verifies this
type for performance reasons (see #31377).
@@ -28128,19 +28175,45 @@ namespace ts { | |||
propertyName: __String, | |||
type: Type): boolean { | |||
|
|||
// Short-circuiting for improved performance. |
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.
Copied this from the existing isValidPropertyAccessWithType
function.
It seemed to be there for performance reasons, doesn't look like it would change behavior, so I added it assuming someone had a good reason to write it in the first place.
On the second pass here, I reviewed the checker functions used by completions related to checking property accessibility. |
This PR doesn't have any linked issues. Please open an issue that references this PR. From there we can discuss and prioritise. |
* @param location The location node where we want to check if the property is accessible. | ||
* @param isSuper True if the access is from `super.`. | ||
* @param writing True if this is a write property access, false if it is a read property access. | ||
* @param type The type of the object whose property is being accessed. (Not the type of the property.) |
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.
minor: I've seen containingType
as a pretty good name for this.
@@ -27291,11 +27292,29 @@ namespace ts { | |||
node: PropertyAccessExpression | QualifiedName | PropertyAccessExpression | VariableDeclaration | ParameterDeclaration | ImportTypeNode | PropertyAssignment | ShorthandPropertyAssignment | BindingElement, | |||
isSuper: boolean, writing: boolean, type: Type, prop: Symbol, reportError = true): boolean { | |||
|
|||
const flags = getDeclarationModifierFlagsFromSymbol(prop, writing); | |||
const errorNode = node.kind === SyntaxKind.QualifiedName ? node.right : | |||
const errorNode = !reportError ? undefined : node.kind === SyntaxKind.QualifiedName ? node.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.
const errorNode = !reportError ? undefined : node.kind === SyntaxKind.QualifiedName ? node.right : | |
const errorNode = !reportError ? undefined : | |
node.kind === SyntaxKind.QualifiedName ? node.right : |
This is a very rough draft PR attempting to use the checker's logic for checking property accessibility when filtering visible/accessible properties that should be offered for completion.
This PR was inspired by bug #45045, caused by trying to access a type's undefined symbol when checking if we should offer a property for completions in an object binding pattern (in
tryGetObjectLikeCompletions
from completions).Looking at the code in completions, I thought the type checker must already do this sort of property accessibility check and set out to see if we could reuse the checker's code for this purpose.
The checker function that does this is
checkPropertyAccessibility
.This function returns a
boolean
saying if a property access is valid, given a property access-like node, the property's symbol, the type where the property comes from, and some options (writing
if it's a write access,isSuper
if we're accessing a super property e.g.super.foo
).But, it also reports an invalid property error if the
reportError
flag is set to true.Based on
checkPropertyAccessibility
, I created the functioncheckPropertyAccessibilityAtNode
that has the same implementation, but with two differences:checkPropertyAccessibility
takes as argument nodes that are property access-like only. This is enough for type checking purposes, and also if we are in a completions scenario where we have an actual property access (e.g.foo.|
), because then we have such a property access-like node. But, in the case of offering completions for an object binding pattern, we are trying to figure out if a property is accessible when we don't yet have a property access-like (i.e. a binding element) node. To that end, the new functioncheckPropertyAccessibilityAtNode
accepts anyNode
, indicating the location in the AST where we'd want to access the property.checkPropertyAccessibility
,checkPropertyAccessibilityAtNode
does not report errors, but instead, if the property access is invalid, it returns diagnostics info (diagnostic message + arguments).I changed
checkPropertyAccessibility
to call the newcheckPropertyAccessibilityAtNode
and report an error (i.e. callerror(...)
) depending on the returned value.I also added a
boolean
-returning wrapper over thecheckPropertyAccessibilityAtNode
function (isPropertyAccessible
), exposed by the checker and used by completions.I only replaced the existing property accessibility completions-specific code to use the new
isPropertyAccessible
function intryGetObjectLikeCompletions
, but there might be other places that could use this new checker function.The variable/function/type names and overall code structure is in no way ideal, but I wanted to know if the idea makes any sense before investing more time on making things pretty. I'm also very open to suggestions on alternative ways to achieve the same thing, or just explanations on why that sort of reuse shouldn't be done.