Skip to content

Commit f89de95

Browse files
authored
Allow special element access assignments to create declarations (#33537)
* Start enabling element access special assignment * Treat element access assignment as special assignment in JS * Make declarations for bindable element access expressions * Fix navigationBar crash * Add multi-level test for JS * Propagate element access expressions to more code paths * Fix property access on `this` * Add quick info test * Uhhh I guess this is fine * Fix module["exports"] and property access chained off element access * Add test for this property assignment * Add test for and fix prototype property assignment * Fix teeeest??? * Update APIs * Fix element access declarations on `this` * Fix go-to-definition * Add declaration emit to tests * Reconcile with late-bound symbol element access assignment * Fix baselines * Add JS declaration back to tests * Fix JS declaration emit of non-late-bound string literal property names * Revert accidental auto-format * Use `isAccessExpression` * Add underscore escaping member to test * Fix and test navBar changes
1 parent 053388d commit f89de95

31 files changed

+1078
-129
lines changed

src/compiler/binder.ts

+42-44
Large diffs are not rendered by default.

src/compiler/checker.ts

+27-9
Original file line numberDiff line numberDiff line change
@@ -3041,7 +3041,7 @@ namespace ts {
30413041
return getSymbolOfNode(d.parent);
30423042
}
30433043
if (isClassExpression(d) && isBinaryExpression(d.parent) && d.parent.operatorToken.kind === SyntaxKind.EqualsToken && isAccessExpression(d.parent.left) && isEntityNameExpression(d.parent.left.expression)) {
3044-
if (isModuleExportsPropertyAccessExpression(d.parent.left) || isExportsIdentifier(d.parent.left.expression)) {
3044+
if (isModuleExportsAccessExpression(d.parent.left) || isExportsIdentifier(d.parent.left.expression)) {
30453045
return getSymbolOfNode(getSourceFileOfNode(d));
30463046
}
30473047
checkExpressionCached(d.parent.left.expression);
@@ -4877,6 +4877,15 @@ namespace ts {
48774877
}
48784878
}
48794879

4880+
function getPropertyNameNodeForSymbol(symbol: Symbol, context: NodeBuilderContext) {
4881+
const fromNameType = getPropertyNameNodeForSymbolFromNameType(symbol, context);
4882+
if (fromNameType) {
4883+
return fromNameType;
4884+
}
4885+
const rawName = unescapeLeadingUnderscores(symbol.escapedName);
4886+
return createPropertyNameNodeForIdentifierOrLiteral(rawName);
4887+
}
4888+
48804889
// See getNameForSymbolFromNameType for a stringy equivalent
48814890
function getPropertyNameNodeForSymbolFromNameType(symbol: Symbol, context: NodeBuilderContext) {
48824891
const nameType = symbol.nameType;
@@ -4889,14 +4898,18 @@ namespace ts {
48894898
if (isNumericLiteralName(name) && startsWith(name, "-")) {
48904899
return createComputedPropertyName(createLiteral(+name));
48914900
}
4892-
return isIdentifierText(name, compilerOptions.target) ? createIdentifier(name) : createLiteral(isNumericLiteralName(name) ? +name : name) as StringLiteral | NumericLiteral;
4901+
return createPropertyNameNodeForIdentifierOrLiteral(name);
48934902
}
48944903
if (nameType.flags & TypeFlags.UniqueESSymbol) {
48954904
return createComputedPropertyName(symbolToExpression((<UniqueESSymbolType>nameType).symbol, context, SymbolFlags.Value));
48964905
}
48974906
}
48984907
}
48994908

4909+
function createPropertyNameNodeForIdentifierOrLiteral(name: string) {
4910+
return isIdentifierText(name, compilerOptions.target) ? createIdentifier(name) : createLiteral(isNumericLiteralName(name) ? +name : name) as StringLiteral | NumericLiteral;
4911+
}
4912+
49004913
function cloneNodeBuilderContext(context: NodeBuilderContext): NodeBuilderContext {
49014914
const initial: NodeBuilderContext = { ...context };
49024915
// Make type parameters created within this context not consume the name outside this context
@@ -5772,8 +5785,7 @@ namespace ts {
57725785
return [];
57735786
}
57745787
const staticFlag = isStatic ? ModifierFlags.Static : 0;
5775-
const rawName = unescapeLeadingUnderscores(p.escapedName);
5776-
const name = getPropertyNameNodeForSymbolFromNameType(p, context) || createIdentifier(rawName);
5788+
const name = getPropertyNameNodeForSymbol(p, context);
57775789
const firstPropertyLikeDecl = find(p.declarations, or(isPropertyDeclaration, isAccessor, isVariableDeclaration, isPropertySignature, isBinaryExpression, isPropertyAccessExpression));
57785790
if (p.flags & SymbolFlags.Accessor && useAccessors) {
57795791
const result: AccessorDeclaration[] = [];
@@ -6869,13 +6881,15 @@ namespace ts {
68696881
let types: Type[] | undefined;
68706882
for (const declaration of symbol.declarations) {
68716883
const expression = (isBinaryExpression(declaration) || isCallExpression(declaration)) ? declaration :
6872-
isPropertyAccessExpression(declaration) ? isBinaryExpression(declaration.parent) ? declaration.parent : declaration :
6884+
isAccessExpression(declaration) ? isBinaryExpression(declaration.parent) ? declaration.parent : declaration :
68736885
undefined;
68746886
if (!expression) {
68756887
continue; // Non-assignment declaration merged in (eg, an Identifier to mark the thing as a namespace) - skip over it and pull type info from elsewhere
68766888
}
68776889

6878-
const kind = isPropertyAccessExpression(expression) ? getAssignmentDeclarationPropertyAccessKind(expression) : getAssignmentDeclarationKind(expression);
6890+
const kind = isAccessExpression(expression)
6891+
? getAssignmentDeclarationPropertyAccessKind(expression)
6892+
: getAssignmentDeclarationKind(expression);
68796893
if (kind === AssignmentDeclarationKind.ThisProperty) {
68806894
if (isDeclarationInConstructor(expression)) {
68816895
definedInConstructor = true;
@@ -7250,12 +7264,15 @@ namespace ts {
72507264
else if (
72517265
isBinaryExpression(declaration) ||
72527266
(isInJSFile(declaration) &&
7253-
(isCallExpression(declaration) || isPropertyAccessExpression(declaration) && isBinaryExpression(declaration.parent)))) {
7267+
(isCallExpression(declaration) || (isPropertyAccessExpression(declaration) || isBindableStaticElementAccessExpression(declaration)) && isBinaryExpression(declaration.parent)))) {
72547268
type = getWidenedTypeForAssignmentDeclaration(symbol);
72557269
}
72567270
else if (isJSDocPropertyLikeTag(declaration)
72577271
|| isPropertyAccessExpression(declaration)
7272+
|| isElementAccessExpression(declaration)
72587273
|| isIdentifier(declaration)
7274+
|| isStringLiteralLike(declaration)
7275+
|| isNumericLiteral(declaration)
72597276
|| isClassDeclaration(declaration)
72607277
|| isFunctionDeclaration(declaration)
72617278
|| (isMethodDeclaration(declaration) && !isObjectLiteralMethod(declaration))
@@ -7436,7 +7453,8 @@ namespace ts {
74367453
return anyType;
74377454
}
74387455
else if (declaration.kind === SyntaxKind.BinaryExpression ||
7439-
declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) {
7456+
(declaration.kind === SyntaxKind.PropertyAccessExpression || declaration.kind === SyntaxKind.ElementAccessExpression) &&
7457+
declaration.parent.kind === SyntaxKind.BinaryExpression) {
74407458
return getWidenedTypeForAssignmentDeclaration(symbol);
74417459
}
74427460
else if (symbol.flags & SymbolFlags.ValueModule && declaration && isSourceFile(declaration) && declaration.commonJsModuleIndicator) {
@@ -32134,7 +32152,7 @@ namespace ts {
3213432152
return node;
3213532153
case SyntaxKind.PropertyAccessExpression:
3213632154
do {
32137-
if (isModuleExportsPropertyAccessExpression(node.expression)) {
32155+
if (isModuleExportsAccessExpression(node.expression)) {
3213832156
return node.name;
3213932157
}
3214032158
node = node.expression;

src/compiler/emitter.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2280,7 +2280,7 @@ namespace ts {
22802280
// if the expression doesn't have any comments that will be emitted.
22812281
return !expression.numericLiteralFlags && !stringContains(text, tokenToString(SyntaxKind.DotToken)!);
22822282
}
2283-
else if (isPropertyAccessExpression(expression) || isElementAccessExpression(expression)) {
2283+
else if (isAccessExpression(expression)) {
22842284
// check if constant enum value is integer
22852285
const constantValue = getConstantValue(expression);
22862286
// isFinite handles cases when constantValue is undefined

src/compiler/types.ts

+30-4
Original file line numberDiff line numberDiff line change
@@ -1306,7 +1306,7 @@ namespace ts {
13061306
literal: BooleanLiteral | LiteralExpression | PrefixUnaryExpression;
13071307
}
13081308

1309-
export interface StringLiteral extends LiteralExpression {
1309+
export interface StringLiteral extends LiteralExpression, Declaration {
13101310
kind: SyntaxKind.StringLiteral;
13111311
/* @internal */ textSourceNode?: Identifier | StringLiteralLike | NumericLiteral; // Allows a StringLiteral to get its text from another node (used by transforms).
13121312
/** Note: this is only set when synthesizing a node, not during parsing. */
@@ -1693,7 +1693,7 @@ namespace ts {
16931693
kind: SyntaxKind.RegularExpressionLiteral;
16941694
}
16951695

1696-
export interface NoSubstitutionTemplateLiteral extends LiteralExpression, TemplateLiteralLikeNode {
1696+
export interface NoSubstitutionTemplateLiteral extends LiteralExpression, TemplateLiteralLikeNode, Declaration {
16971697
kind: SyntaxKind.NoSubstitutionTemplateLiteral;
16981698
}
16991699

@@ -1722,7 +1722,7 @@ namespace ts {
17221722
NumericLiteralFlags = Scientific | Octal | HexSpecifier | BinaryOrOctalSpecifier | ContainsSeparator
17231723
}
17241724

1725-
export interface NumericLiteral extends LiteralExpression {
1725+
export interface NumericLiteral extends LiteralExpression, Declaration {
17261726
kind: SyntaxKind.NumericLiteral;
17271727
/* @internal */
17281728
numericLiteralFlags: TokenFlags;
@@ -1885,7 +1885,33 @@ namespace ts {
18851885
;
18861886

18871887
/** @internal */
1888-
export type BindableObjectDefinePropertyCall = CallExpression & { arguments: { 0: EntityNameExpression, 1: StringLiteralLike | NumericLiteral, 2: ObjectLiteralExpression } };
1888+
export type BindableObjectDefinePropertyCall = CallExpression & { arguments: { 0: BindableStaticNameExpression, 1: StringLiteralLike | NumericLiteral, 2: ObjectLiteralExpression } };
1889+
/** @internal */
1890+
export type BindableStaticNameExpression = EntityNameExpression | BindableStaticElementAccessExpression;
1891+
/** @internal */
1892+
export type LiteralLikeElementAccessExpression = ElementAccessExpression & Declaration & {
1893+
argumentExpression: StringLiteralLike | NumericLiteral;
1894+
};
1895+
/** @internal */
1896+
export type BindableStaticElementAccessExpression = LiteralLikeElementAccessExpression & {
1897+
expression: BindableStaticNameExpression;
1898+
};
1899+
/** @internal */
1900+
export type BindableElementAccessExpression = ElementAccessExpression & {
1901+
expression: BindableStaticNameExpression;
1902+
};
1903+
/** @internal */
1904+
export type BindableStaticAccessExpression = PropertyAccessEntityNameExpression | BindableStaticElementAccessExpression;
1905+
/** @internal */
1906+
export type BindableAccessExpression = PropertyAccessEntityNameExpression | BindableElementAccessExpression;
1907+
/** @internal */
1908+
export interface BindableStaticPropertyAssignmentExpression extends BinaryExpression {
1909+
left: BindableStaticAccessExpression;
1910+
}
1911+
/** @internal */
1912+
export interface BindablePropertyAssignmentExpression extends BinaryExpression {
1913+
left: BindableAccessExpression;
1914+
}
18891915

18901916
// see: https://tc39.github.io/ecma262/#prod-SuperCall
18911917
export interface SuperCall extends CallExpression {

0 commit comments

Comments
 (0)