Skip to content

Clean up GetElementOrPropertyAccessName #951

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 21 additions & 25 deletions internal/ast/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -1292,26 +1292,20 @@ func IsThisParameter(node *Node) bool {

// Does not handle signed numeric names like `a[+0]` - handling those would require handling prefix unary expressions
// throughout late binding handling as well, which is awkward (but ultimately probably doable if there is demand)
func GetElementOrPropertyAccessArgumentExpressionOrName(node *Node) *Node {
func GetElementOrPropertyAccessName(node *Node) *Node {
switch node.Kind {
case KindPropertyAccessExpression:
return node.Name()
if IsIdentifier(node.Name()) {
return node.Name()
}
return nil
case KindElementAccessExpression:
arg := SkipParentheses(node.AsElementAccessExpression().ArgumentExpression)
if IsStringOrNumericLiteralLike(arg) {
if arg := SkipParentheses(node.AsElementAccessExpression().ArgumentExpression); IsStringOrNumericLiteralLike(arg) {
return arg
}
return node
}
panic("Unhandled case in GetElementOrPropertyAccessArgumentExpressionOrName")
}

func GetElementOrPropertyAccessName(node *Node) string {
name := GetElementOrPropertyAccessArgumentExpressionOrName(node)
if name == nil {
return ""
return nil
}
return name.Text()
panic("Unhandled case in GetElementOrPropertyAccessName")
}

func IsExpressionWithTypeArgumentsInClassExtendsClause(node *Node) bool {
Expand Down Expand Up @@ -1356,7 +1350,11 @@ func GetNonAssignedNameOfDeclaration(declaration *Node) *Node {
bin := declaration.AsBinaryExpression()
kind := GetAssignmentDeclarationKind(bin)
if kind == JSDeclarationKindProperty || kind == JSDeclarationKindThisProperty {
return GetElementOrPropertyAccessArgumentExpressionOrName(bin.Left)
if name := GetElementOrPropertyAccessName(bin.Left); name != nil {
return name
} else {
return bin.Left
}
}
return nil
case KindExportAssignment, KindJSExportAssignment:
Expand Down Expand Up @@ -1426,24 +1424,19 @@ func GetAssignmentDeclarationKind(bin *BinaryExpression) JSDeclarationKind {
if IsModuleExportsAccessExpression(bin.Left) {
return JSDeclarationKindModuleExports
} else if (IsModuleExportsAccessExpression(bin.Left.Expression()) || IsExportsIdentifier(bin.Left.Expression())) &&
hasJSBindableName(bin.Left) {
GetElementOrPropertyAccessName(bin.Left) != nil {
return JSDeclarationKindExportsProperty
}
if bin.Left.Expression().Kind == KindThisKeyword {
return JSDeclarationKindThisProperty
}
if bin.Left.Kind == KindPropertyAccessExpression && IsIdentifier(bin.Left.Expression()) && hasJSBindableName(bin.Left) ||
if bin.Left.Kind == KindPropertyAccessExpression && IsIdentifier(bin.Left.Expression()) && IsIdentifier(bin.Left.Name()) ||
bin.Left.Kind == KindElementAccessExpression && IsIdentifier(bin.Left.Expression()) {
return JSDeclarationKindProperty
}
return JSDeclarationKindNone
}

func hasJSBindableName(node *Node) bool {
name := GetElementOrPropertyAccessArgumentExpressionOrName(node)
return IsIdentifier(name) || IsStringLiteralLike(name)
}

/**
* A declaration has a dynamic name if all of the following are true:
* 1. The declaration has a computed property name.
Expand Down Expand Up @@ -2679,9 +2672,12 @@ func IsVariableDeclarationInitializedToRequire(node *Node) bool {
}

func IsModuleExportsAccessExpression(node *Node) bool {
return (IsPropertyAccessExpression(node) || isLiteralLikeElementAccess(node)) &&
IsModuleIdentifier(node.Expression()) &&
GetElementOrPropertyAccessName(node) == "exports"
if IsAccessExpression(node) && IsModuleIdentifier(node.Expression()) {
if name := GetElementOrPropertyAccessName(node); name != nil {
return name.Text() == "exports"
}
}
return false
}

func isLiteralLikeElementAccess(node *Node) bool {
Expand Down
4 changes: 2 additions & 2 deletions internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -3575,7 +3575,7 @@ func (c *Checker) checkTestingKnownTruthyType(condExpr *ast.Node, condType *Type
if ast.IsLogicalOrCoalescingBinaryExpression(condExpr) {
location = ast.SkipParentheses(condExpr.AsBinaryExpression().Right)
}
if isModuleExportsAccessExpression(location) {
if ast.IsModuleExportsAccessExpression(location) {
return
}
if ast.IsLogicalOrCoalescingBinaryExpression(location) {
Expand Down Expand Up @@ -27743,7 +27743,7 @@ func (c *Checker) canGetContextualTypeForAssignmentDeclaration(node *ast.Node) b
return false
}
// and now for one single case of object literal methods
name := ast.GetElementOrPropertyAccessArgumentExpressionOrName(left)
name := ast.GetElementOrPropertyAccessName(left)
if name == nil {
return false
} else {
Expand Down
4 changes: 0 additions & 4 deletions internal/checker/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -1632,10 +1632,6 @@ func minAndMax[T any](slice []T, getValue func(value T) int) (int, int) {
return minValue, maxValue
}

func isModuleExportsAccessExpression(node *ast.Node) bool {
return ast.IsAccessExpression(node) && ast.IsModuleIdentifier(node.Expression()) && ast.GetElementOrPropertyAccessName(node) == "exports"
}

func getNonModifierTokenRangeOfNode(node *ast.Node) core.TextRange {
pos := node.Pos()
if node.Modifiers() != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/parser/reparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (p *Parser) reparseCommonJS(node *ast.Node) {
nodes[0].Flags = ast.NodeFlagsReparsed
nodes[0].Loc = bin.Loc
// TODO: Name can sometimes be a string literal, so downstream code needs to handle this
export = p.factory.NewCommonJSExport(p.newModifierList(bin.Loc, nodes), ast.GetElementOrPropertyAccessArgumentExpressionOrName(bin.Left), bin.Right)
export = p.factory.NewCommonJSExport(p.newModifierList(bin.Loc, nodes), ast.GetElementOrPropertyAccessName(bin.Left), bin.Right)
}
if export != nil {
export.Flags = ast.NodeFlagsReparsed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
elementAccessExpressionInJS.js(1,5): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ exports: { [key: string]: any; }; }'.
No index signature with a parameter of type 'string' was found on type '{ exports: { [key: string]: any; }; }'.
elementAccessExpressionInJS.js(3,32): error TS7006: Parameter 'index' implicitly has an 'any' type.


==== declarations.d.ts (0 errors) ====
declare var module: {
exports: {
[key: string]: any;
};
}
==== elementAccessExpressionInJS.js (2 errors) ====
if (module[calculatePropertyName(1)]) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ exports: { [key: string]: any; }; }'.
!!! error TS7053: No index signature with a parameter of type 'string' was found on type '{ exports: { [key: string]: any; }; }'.
}
function calculatePropertyName(index) {
~~~~~
!!! error TS7006: Parameter 'index' implicitly has an 'any' type.
// this would be some webpack index in real life
return `property${index}`;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//// [tests/cases/conformance/salsa/deepElementAccessExpressionInJS.ts] ////

=== declarations.d.ts ===
declare var module: {
>module : Symbol(module, Decl(declarations.d.ts, 0, 11))

exports: {
>exports : Symbol(exports, Decl(declarations.d.ts, 0, 21))

[key: string]: any;
>key : Symbol(key, Decl(declarations.d.ts, 2, 9))

};
}
=== elementAccessExpressionInJS.js ===
if (module[calculatePropertyName(1)]) {
>module : Symbol(module, Decl(declarations.d.ts, 0, 11))
>calculatePropertyName : Symbol(calculatePropertyName, Decl(elementAccessExpressionInJS.js, 1, 1))
}
function calculatePropertyName(index) {
>calculatePropertyName : Symbol(calculatePropertyName, Decl(elementAccessExpressionInJS.js, 1, 1))
>index : Symbol(index, Decl(elementAccessExpressionInJS.js, 2, 31))

// this would be some webpack index in real life
return `property${index}`;
>index : Symbol(index, Decl(elementAccessExpressionInJS.js, 2, 31))
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//// [tests/cases/conformance/salsa/deepElementAccessExpressionInJS.ts] ////

=== declarations.d.ts ===
declare var module: {
>module : { exports: { [key: string]: any; }; }

exports: {
>exports : { [key: string]: any; }

[key: string]: any;
>key : string

};
}
=== elementAccessExpressionInJS.js ===
if (module[calculatePropertyName(1)]) {
>module[calculatePropertyName(1)] : any
>module : { exports: { [key: string]: any; }; }
>calculatePropertyName(1) : string
>calculatePropertyName : (index: any) => string
>1 : 1
}
function calculatePropertyName(index) {
>calculatePropertyName : (index: any) => string
>index : any

// this would be some webpack index in real life
return `property${index}`;
>`property${index}` : string
>index : any
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// @allowJs: true
// @checkJs: true
// @strict: true
// @target: es6
// @outDir: ./out
// @filename: declarations.d.ts
declare var module: {
exports: {
[key: string]: any;
};
}
// @filename: elementAccessExpressionInJS.js
if (module[calculatePropertyName(1)]) {
}
function calculatePropertyName(index) {
// this would be some webpack index in real life
return `property${index}`;
}