Skip to content

Commit e6ca44e

Browse files
author
Andy Hanson
committed
WIP
1 parent 0783743 commit e6ca44e

File tree

8 files changed

+113
-67
lines changed

8 files changed

+113
-67
lines changed

src/compiler/checker.ts

Lines changed: 57 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -944,28 +944,39 @@ namespace ts {
944944

945945

946946
function checkAndReportErrorForExtendingInterface(errorLocation: Node): boolean {
947-
let parentClassExpression = errorLocation;
948-
while (parentClassExpression) {
949-
const kind = parentClassExpression.kind;
950-
if (kind === SyntaxKind.Identifier || kind === SyntaxKind.PropertyAccessExpression) {
951-
parentClassExpression = parentClassExpression.parent;
952-
continue;
953-
}
954-
if (kind === SyntaxKind.ExpressionWithTypeArguments) {
955-
break;
956-
}
947+
const parentExpression = climbToSupportedExpressionWithTypeArguments(errorLocation);
948+
if (!parentExpression) {
957949
return false;
958950
}
959-
if (!parentClassExpression) {
960-
return false;
961-
}
962-
const expression = (<ExpressionWithTypeArguments>parentClassExpression).expression;
951+
const expression = parentExpression.expression;
952+
963953
if (resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true)) {
964954
error(errorLocation, Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements, getTextOfNode(expression));
965955
return true;
966956
}
967957
return false;
968958
}
959+
/**
960+
* Climbs up parents to a SupportedExpressionWIthTypeArguments.
961+
* Does *not* just climb to an ExpressionWithTypeArguments; instead, ensures that this really is supported.
962+
*/
963+
function climbToSupportedExpressionWithTypeArguments(node: Node): SupportedExpressionWithTypeArguments | undefined {
964+
while (node) {
965+
switch (node.kind) {
966+
case SyntaxKind.Identifier:
967+
case SyntaxKind.PropertyAccessExpression:
968+
node = node.parent;
969+
break;
970+
case SyntaxKind.ExpressionWithTypeArguments:
971+
Debug.assert(isSupportedExpressionWithTypeArguments(<ExpressionWithTypeArguments>node));
972+
return <SupportedExpressionWithTypeArguments>node;
973+
default:
974+
return undefined;
975+
}
976+
}
977+
return undefined;
978+
}
979+
969980

970981
function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void {
971982
Debug.assert((result.flags & SymbolFlags.BlockScopedVariable) !== 0);
@@ -1250,7 +1261,7 @@ namespace ts {
12501261
}
12511262

12521263
// Resolves a qualified name and any involved aliases
1253-
function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean): Symbol {
1264+
function resolveEntityName(name: EntityNameOrEntityNameExpression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean): Symbol | undefined {
12541265
if (nodeIsMissing(name)) {
12551266
return undefined;
12561267
}
@@ -1265,7 +1276,7 @@ namespace ts {
12651276
}
12661277
}
12671278
else if (name.kind === SyntaxKind.QualifiedName || name.kind === SyntaxKind.PropertyAccessExpression) {
1268-
const left = name.kind === SyntaxKind.QualifiedName ? (<QualifiedName>name).left : (<PropertyAccessExpression>name).expression;
1279+
const left = name.kind === SyntaxKind.QualifiedName ? (<QualifiedName>name).left : (<PropertyAccessEntityNameExpression>name).expression;
12691280
const right = name.kind === SyntaxKind.QualifiedName ? (<QualifiedName>name).right : (<PropertyAccessExpression>name).name;
12701281

12711282
const namespace = resolveEntityName(left, SymbolFlags.Namespace, ignoreErrors);
@@ -1814,7 +1825,7 @@ namespace ts {
18141825
}
18151826
}
18161827

1817-
function isEntityNameVisible(entityName: EntityName | Expression, enclosingDeclaration: Node): SymbolVisibilityResult {
1828+
function isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult {
18181829
// get symbol of the first identifier of the entityName
18191830
let meaning: SymbolFlags;
18201831
if (entityName.parent.kind === SyntaxKind.TypeQuery || isExpressionWithTypeArgumentsInClassExtendsClause(entityName.parent)) {
@@ -4885,7 +4896,7 @@ namespace ts {
48854896
return getDeclaredTypeOfSymbol(symbol);
48864897
}
48874898

4888-
function getTypeReferenceName(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference): LeftHandSideExpression | EntityName {
4899+
function getTypeReferenceName(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference): EntityNameOrEntityNameExpression | undefined {
48894900
switch (node.kind) {
48904901
case SyntaxKind.TypeReference:
48914902
return (<TypeReferenceNode>node).typeName;
@@ -4894,8 +4905,9 @@ namespace ts {
48944905
case SyntaxKind.ExpressionWithTypeArguments:
48954906
// We only support expressions that are simple qualified names. For other
48964907
// expressions this produces undefined.
4897-
if (isSupportedExpressionWithTypeArguments(<ExpressionWithTypeArguments>node)) {
4898-
return (<ExpressionWithTypeArguments>node).expression;
4908+
const expr = <ExpressionWithTypeArguments>node;
4909+
if (isSupportedExpressionWithTypeArguments(expr)) {
4910+
return expr.expression;
48994911
}
49004912

49014913
// fall through;
@@ -4906,7 +4918,7 @@ namespace ts {
49064918

49074919
function resolveTypeReferenceName(
49084920
node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference,
4909-
typeReferenceName: LeftHandSideExpression | EntityName) {
4921+
typeReferenceName: EntityNameExpression | EntityName) {
49104922

49114923
if (!typeReferenceName) {
49124924
return unknownSymbol;
@@ -4947,15 +4959,14 @@ namespace ts {
49474959
const typeReferenceName = getTypeReferenceName(node);
49484960
symbol = resolveTypeReferenceName(node, typeReferenceName);
49494961
type = getTypeReferenceType(node, symbol);
4950-
4951-
links.resolvedSymbol = symbol;
4952-
links.resolvedType = type;
49534962
}
49544963
else {
49554964
// We only support expressions that are simple qualified names. For other expressions this produces undefined.
4956-
const typeNameOrExpression = node.kind === SyntaxKind.TypeReference ? (<TypeReferenceNode>node).typeName :
4957-
isSupportedExpressionWithTypeArguments(<ExpressionWithTypeArguments>node) ? (<ExpressionWithTypeArguments>node).expression :
4958-
undefined;
4965+
const typeNameOrExpression: EntityNameOrEntityNameExpression = node.kind === SyntaxKind.TypeReference
4966+
? (<TypeReferenceNode>node).typeName
4967+
: isSupportedExpressionWithTypeArguments(<ExpressionWithTypeArguments>node)
4968+
? (<SupportedExpressionWithTypeArguments>node).expression
4969+
: undefined;
49594970
symbol = typeNameOrExpression && resolveEntityName(typeNameOrExpression, SymbolFlags.Type) || unknownSymbol;
49604971
type = symbol === unknownSymbol ? unknownType :
49614972
symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface) ? getTypeFromClassOrInterfaceReference(node, symbol) :
@@ -16594,20 +16605,21 @@ namespace ts {
1659416605
}
1659516606
}
1659616607

16597-
function getFirstIdentifier(node: EntityName | Expression): Identifier {
16598-
while (true) {
16599-
if (node.kind === SyntaxKind.QualifiedName) {
16600-
node = (<QualifiedName>node).left;
16601-
}
16602-
else if (node.kind === SyntaxKind.PropertyAccessExpression) {
16603-
node = (<PropertyAccessExpression>node).expression;
16604-
}
16605-
else {
16606-
break;
16607-
}
16608+
function getFirstIdentifier(node: EntityNameOrEntityNameExpression): Identifier {
16609+
switch (node.kind) {
16610+
case SyntaxKind.Identifier:
16611+
return <Identifier>node;
16612+
case SyntaxKind.QualifiedName:
16613+
do {
16614+
node = (<QualifiedName>node).left;
16615+
} while (node.kind !== SyntaxKind.Identifier);
16616+
return <Identifier>node;
16617+
case SyntaxKind.PropertyAccessExpression:
16618+
do {
16619+
node = (<PropertyAccessEntityNameExpression>node).expression;
16620+
} while (node.kind !== SyntaxKind.Identifier);
16621+
return <Identifier>node;
1660816622
}
16609-
Debug.assert(node.kind === SyntaxKind.Identifier);
16610-
return <Identifier>node;
1661116623
}
1661216624

1661316625
function checkExternalImportOrExportDeclaration(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): boolean {
@@ -17306,7 +17318,7 @@ namespace ts {
1730617318
return getLeftSideOfImportEqualsOrExportAssignment(node) !== undefined;
1730717319
}
1730817320

17309-
function getSymbolOfEntityNameOrPropertyAccessExpression(entityName: EntityName | PropertyAccessExpression): Symbol {
17321+
function getSymbolOfEntityNameOrPropertyAccessExpression(entityName: EntityName | PropertyAccessExpression): Symbol | undefined {
1731017322
if (isDeclarationName(entityName)) {
1731117323
return getSymbolOfNode(entityName.parent);
1731217324
}
@@ -17325,8 +17337,8 @@ namespace ts {
1732517337
}
1732617338
}
1732717339

17328-
if (entityName.parent.kind === SyntaxKind.ExportAssignment) {
17329-
return resolveEntityName(<Identifier>entityName,
17340+
if (entityName.parent.kind === SyntaxKind.ExportAssignment && isEntityNameExpression(<Identifier | PropertyAccessExpression>entityName)) {
17341+
return resolveEntityName(<EntityNameExpression>entityName,
1733017342
/*all meanings*/ SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias);
1733117343
}
1733217344

@@ -17340,7 +17352,7 @@ namespace ts {
1734017352
}
1734117353

1734217354
if (isRightSideOfQualifiedNameOrPropertyAccess(entityName)) {
17343-
entityName = <QualifiedName | PropertyAccessExpression>entityName.parent;
17355+
entityName = <QualifiedName | PropertyAccessEntityNameExpression>entityName.parent;
1734417356
}
1734517357

1734617358
if (isHeritageClauseElementIdentifier(<EntityName>entityName)) {
@@ -18053,7 +18065,7 @@ namespace ts {
1805318065
};
1805418066

1805518067
// defined here to avoid outer scope pollution
18056-
function getTypeReferenceDirectivesForEntityName(node: EntityName | PropertyAccessExpression): string[] {
18068+
function getTypeReferenceDirectivesForEntityName(node: EntityNameOrEntityNameExpression): string[] {
1805718069
// program does not have any files with type reference directives - bail out
1805818070
if (!fileToDirective) {
1805918071
return undefined;

src/compiler/declarationEmitter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ namespace ts {
441441
}
442442
}
443443

444-
function emitEntityName(entityName: EntityName | PropertyAccessExpression) {
444+
function emitEntityName(entityName: EntityNameOrEntityNameExpression) {
445445
const visibilityResult = resolver.isEntityNameVisible(entityName,
446446
// Aliases can be written asynchronously so use correct enclosing declaration
447447
entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration ? entityName.parent : enclosingDeclaration);
@@ -454,7 +454,7 @@ namespace ts {
454454
function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) {
455455
if (isSupportedExpressionWithTypeArguments(node)) {
456456
Debug.assert(node.expression.kind === SyntaxKind.Identifier || node.expression.kind === SyntaxKind.PropertyAccessExpression);
457-
emitEntityName(<Identifier | PropertyAccessExpression>node.expression);
457+
emitEntityName(node.expression);
458458
if (node.typeArguments) {
459459
write("<");
460460
emitCommaList(node.typeArguments, emitType);

src/compiler/types.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -981,13 +981,19 @@ namespace ts {
981981
multiLine?: boolean;
982982
}
983983

984+
export type EntityNameExpression = Identifier | PropertyAccessEntityNameExpression;
985+
export type EntityNameOrEntityNameExpression = EntityName | EntityNameExpression;
986+
984987
// @kind(SyntaxKind.PropertyAccessExpression)
985988
export interface PropertyAccessExpression extends MemberExpression, Declaration {
986989
expression: LeftHandSideExpression;
987990
name: Identifier;
988991
}
989-
990-
export type IdentifierOrPropertyAccess = Identifier | PropertyAccessExpression;
992+
/** Brand for a PropertyAccessExpression which, like a QualifiedName, consists of a sequence of identifiers separated by dots. */
993+
export interface PropertyAccessEntityNameExpression extends PropertyAccessExpression {
994+
_propertyAccessExpressionLikeQualifiedNameBrand?: any;
995+
expression: EntityNameExpression;
996+
}
991997

992998
// @kind(SyntaxKind.ElementAccessExpression)
993999
export interface ElementAccessExpression extends MemberExpression {
@@ -1007,6 +1013,10 @@ namespace ts {
10071013
expression: LeftHandSideExpression;
10081014
typeArguments?: NodeArray<TypeNode>;
10091015
}
1016+
export interface SupportedExpressionWithTypeArguments extends ExpressionWithTypeArguments {
1017+
_supportedExpressionWithTypeArgumentsBrand?: any;
1018+
expression: EntityNameExpression;
1019+
}
10101020

10111021
// @kind(SyntaxKind.NewExpression)
10121022
export interface NewExpression extends CallExpression, PrimaryExpression { }
@@ -2019,7 +2029,7 @@ namespace ts {
20192029
writeTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
20202030
writeBaseConstructorTypeOfClass(node: ClassLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void;
20212031
isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessibilityResult;
2022-
isEntityNameVisible(entityName: EntityName | Expression, enclosingDeclaration: Node): SymbolVisibilityResult;
2032+
isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult;
20232033
// Returns the constant value this property access resolves to, or 'undefined' for a non-constant
20242034
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
20252035
getReferencedValueDeclaration(reference: Identifier): Declaration;
@@ -2028,7 +2038,7 @@ namespace ts {
20282038
moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean;
20292039
isArgumentsLocalBinding(node: Identifier): boolean;
20302040
getExternalModuleFileFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration): SourceFile;
2031-
getTypeReferenceDirectivesForEntityName(name: EntityName | PropertyAccessExpression): string[];
2041+
getTypeReferenceDirectivesForEntityName(name: EntityNameOrEntityNameExpression): string[];
20322042
getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): string[];
20332043
}
20342044

src/compiler/utilities.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,14 +1033,14 @@ namespace ts {
10331033
&& (<PropertyAccessExpression | ElementAccessExpression>node).expression.kind === SyntaxKind.SuperKeyword;
10341034
}
10351035

1036-
1037-
export function getEntityNameFromTypeNode(node: TypeNode): EntityName | Expression {
1036+
export function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression {
10381037
if (node) {
10391038
switch (node.kind) {
10401039
case SyntaxKind.TypeReference:
10411040
return (<TypeReferenceNode>node).typeName;
10421041
case SyntaxKind.ExpressionWithTypeArguments:
1043-
return (<ExpressionWithTypeArguments>node).expression;
1042+
Debug.assert(isSupportedExpressionWithTypeArguments(<ExpressionWithTypeArguments>node));
1043+
return (<SupportedExpressionWithTypeArguments>node).expression;
10441044
case SyntaxKind.Identifier:
10451045
case SyntaxKind.QualifiedName:
10461046
return (<EntityName><Node>node);
@@ -2680,24 +2680,28 @@ namespace ts {
26802680
isClassLike(node.parent.parent);
26812681
}
26822682

2683-
// Returns false if this heritage clause element's expression contains something unsupported
2684-
// (i.e. not a name or dotted name).
2685-
export function isSupportedExpressionWithTypeArguments(node: ExpressionWithTypeArguments): boolean {
2686-
return isSupportedExpressionWithTypeArgumentsRest(node.expression);
2683+
export function isSupportedExpressionWithTypeArguments(node: ExpressionWithTypeArguments): node is SupportedExpressionWithTypeArguments {
2684+
return isEntityNameExpression(node.expression);
26872685
}
26882686

2689-
function isSupportedExpressionWithTypeArgumentsRest(node: Expression): boolean {
2690-
if (node.kind === SyntaxKind.Identifier) {
2691-
return true;
2692-
}
2693-
else if (isPropertyAccessExpression(node)) {
2694-
return isSupportedExpressionWithTypeArgumentsRest(node.expression);
2695-
}
2696-
else {
2697-
return false;
2687+
export function isEntityNameExpression(node: Expression): node is EntityNameExpression {
2688+
for (; ; ) {
2689+
switch (node.kind) {
2690+
case SyntaxKind.Identifier:
2691+
return true;
2692+
case SyntaxKind.PropertyAccessExpression:
2693+
node = (<PropertyAccessExpression>node).expression;
2694+
break;
2695+
default:
2696+
return false;
2697+
}
26982698
}
26992699
}
27002700

2701+
export function isPropertyAccessAnEntityNameExpression(node: PropertyAccessExpression): node is PropertyAccessEntityNameExpression {
2702+
return isEntityNameExpression(node.expression);
2703+
}
2704+
27012705
export function isRightSideOfQualifiedNameOrPropertyAccess(node: Node) {
27022706
return (node.parent.kind === SyntaxKind.QualifiedName && (<QualifiedName>node.parent).right === node) ||
27032707
(node.parent.kind === SyntaxKind.PropertyAccessExpression && (<PropertyAccessExpression>node.parent).name === node);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//// [exportDefaultProperty.ts]
2+
export default "".length
3+
4+
5+
//// [exportDefaultProperty.js]
6+
"use strict";
7+
exports.__esModule = true;
8+
exports["default"] = "".length;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
=== tests/cases/compiler/exportDefaultProperty.ts ===
2+
export default "".length
3+
>"".length : Symbol(String.length, Decl(lib.d.ts, --, --))
4+
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
5+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
=== tests/cases/compiler/exportDefaultProperty.ts ===
2+
export default "".length
3+
>"".length : number
4+
>"" : string
5+
>length : number
6+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default "".length

0 commit comments

Comments
 (0)