Skip to content

Commit 31d5450

Browse files
committed
nested type names, addresses #423 and #127
1 parent 46cdc82 commit 31d5450

File tree

9 files changed

+192
-57
lines changed

9 files changed

+192
-57
lines changed

src/ast.ts

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export enum NodeKind {
3131

3232
// types
3333
TYPE,
34+
TYPENAME,
3435
TYPEPARAMETER,
3536
PARAMETER,
3637
SIGNATURE,
@@ -155,8 +156,26 @@ export abstract class Node {
155156

156157
// types
157158

158-
static createType(
159+
static createTypeName(
159160
name: IdentifierExpression,
161+
range: Range
162+
): TypeName {
163+
var typeName = new TypeName();
164+
typeName.range = range;
165+
typeName.identifier = name; name.parent = typeName;
166+
typeName.next = null;
167+
return typeName;
168+
}
169+
170+
static createSimpleTypeName(
171+
name: string,
172+
range: Range
173+
): TypeName {
174+
return Node.createTypeName(Node.createIdentifierExpression(name, range), range);
175+
}
176+
177+
static createType(
178+
name: TypeName,
160179
typeArguments: CommonTypeNode[] | null,
161180
isNullable: bool,
162181
range: Range
@@ -173,7 +192,7 @@ export abstract class Node {
173192
range: Range
174193
): TypeNode {
175194
return Node.createType(
176-
Node.createIdentifierExpression("", range),
195+
Node.createSimpleTypeName("", range),
177196
null,
178197
false,
179198
range
@@ -1066,12 +1085,22 @@ export abstract class CommonTypeNode extends Node {
10661085
isNullable: bool;
10671086
}
10681087

1088+
/** Represents a type name. */
1089+
export class TypeName extends Node {
1090+
kind = NodeKind.TYPENAME;
1091+
1092+
/** Identifier of this part. */
1093+
identifier: IdentifierExpression;
1094+
/** Next part of the type name or `null` if this is the last part. */
1095+
next: TypeName | null;
1096+
}
1097+
10691098
/** Represents a type annotation. */
10701099
export class TypeNode extends CommonTypeNode {
10711100
kind = NodeKind.TYPE;
10721101

1073-
/** Identifier reference. */
1074-
name: IdentifierExpression;
1102+
/** Type name. */
1103+
name: TypeName;
10751104
/** Type argument references. */
10761105
typeArguments: CommonTypeNode[] | null;
10771106
}

src/compiler.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5068,7 +5068,9 @@ export class Compiler extends DiagnosticEmitter {
50685068
let argumentExprs = new Array<ExpressionRef>(numArguments);
50695069
for (let i = 0; i < numParameters; ++i) {
50705070
let typeNode = parameterNodes[i].type;
5071-
let name = typeNode.kind == NodeKind.TYPE ? (<TypeNode>typeNode).name.text : null;
5071+
let templateName = typeNode.kind == NodeKind.TYPE && !(<TypeNode>typeNode).name.next
5072+
? (<TypeNode>typeNode).name.identifier.text
5073+
: null;
50725074
let argumentExpression = i < numArguments
50735075
? argumentNodes[i]
50745076
: parameterNodes[i].initializer;
@@ -5079,8 +5081,8 @@ export class Compiler extends DiagnosticEmitter {
50795081
);
50805082
return module.createUnreachable();
50815083
}
5082-
if (name !== null && inferredTypes.has(name)) {
5083-
let inferredType = inferredTypes.get(name);
5084+
if (templateName !== null && inferredTypes.has(templateName)) {
5085+
let inferredType = inferredTypes.get(templateName);
50845086
if (inferredType) {
50855087
argumentExprs[i] = this.compileExpressionRetainType(argumentExpression, inferredType, WrapMode.NONE);
50865088
let commonType: Type | null;
@@ -5099,7 +5101,7 @@ export class Compiler extends DiagnosticEmitter {
50995101
inferredType = this.currentType;
51005102
// ++numInferred;
51015103
}
5102-
inferredTypes.set(name, inferredType);
5104+
inferredTypes.set(templateName, inferredType);
51035105
} else {
51045106
let concreteType = this.resolver.resolveType(
51055107
parameterNodes[i].type,

src/extra/ast.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313

1414
CommonTypeNode,
1515
TypeNode,
16+
TypeName,
1617
TypeParameterNode,
1718
SignatureNode,
1819

@@ -358,8 +359,7 @@ export class ASTBuilder {
358359
return;
359360
}
360361
var typeNode = <TypeNode>node;
361-
assert(typeNode.name.text.length);
362-
this.visitIdentifierExpression(typeNode.name);
362+
this.visitTypeName((<TypeNode>node).name);
363363
var typeArguments = typeNode.typeArguments;
364364
if (typeArguments) {
365365
let numTypeArguments = typeArguments.length;
@@ -377,6 +377,17 @@ export class ASTBuilder {
377377
}
378378
}
379379

380+
visitTypeName(node: TypeName): void {
381+
this.visitIdentifierExpression(node.identifier);
382+
var sb = this.sb;
383+
var current = node.next;
384+
while (current) {
385+
sb.push(".");
386+
this.visitIdentifierExpression(current.identifier);
387+
current = current.next;
388+
}
389+
}
390+
380391
visitTypeParameter(node: TypeParameterNode): void {
381392
this.visitIdentifierExpression(node.name);
382393
var extendsType = node.extendsType;
@@ -1540,5 +1551,9 @@ export class ASTBuilder {
15401551
}
15411552

15421553
function isTypeOmitted(type: CommonTypeNode): bool {
1543-
return type.kind == NodeKind.TYPE && !changetype<TypeNode>(type).name.text.length;
1554+
if (type.kind == NodeKind.TYPE) {
1555+
let name = (<TypeNode>type).name;
1556+
return !(name.next || name.identifier.text.length);
1557+
}
1558+
return false;
15441559
}

src/parser.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -431,42 +431,41 @@ export class Parser extends DiagnosticEmitter {
431431
// 'void'
432432
} else if (token == Token.VOID) {
433433
type = Node.createType(
434-
Node.createIdentifierExpression("void", tn.range()), [], false, tn.range(startPos, tn.pos)
434+
Node.createSimpleTypeName("void", tn.range()), [], false, tn.range(startPos, tn.pos)
435435
);
436436

437437
// 'this'
438438
} else if (token == Token.THIS) {
439439
type = Node.createType(
440-
Node.createThisExpression(tn.range()), [], false, tn.range(startPos, tn.pos)
440+
Node.createSimpleTypeName("this", tn.range()), [], false, tn.range(startPos, tn.pos)
441441
);
442442

443443
// 'true'
444444
} else if (token == Token.TRUE || token == Token.FALSE) {
445445
type = Node.createType(
446-
Node.createIdentifierExpression("bool", tn.range()), [], false, tn.range(startPos, tn.pos)
446+
Node.createSimpleTypeName("bool", tn.range()), [], false, tn.range(startPos, tn.pos)
447447
);
448448

449449
// StringLiteral
450450
} else if (token == Token.STRINGLITERAL) {
451451
tn.readString();
452452
type = Node.createType(
453-
Node.createIdentifierExpression("string", tn.range()), [], false, tn.range(startPos, tn.pos)
453+
Node.createSimpleTypeName("string", tn.range()), [], false, tn.range(startPos, tn.pos)
454454
);
455455

456456
// Identifier
457457
} else if (token == Token.IDENTIFIER) {
458-
let identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
458+
let first = Node.createSimpleTypeName(tn.readIdentifier(), tn.range());
459+
let current = first;
459460
let parameters = new Array<TypeNode>();
460461
let nullable = false;
461462

462463
// Identifier ('.' Identifier)+
463464
while (tn.skip(Token.DOT)) {
464465
if (tn.skip(Token.IDENTIFIER)) {
465-
// TODO: this works for now, but the representation isn't great
466-
identifier = Node.createIdentifierExpression(
467-
identifier.text + "." + tn.readIdentifier(),
468-
tn.range(identifier.range.start, tn.pos)
469-
);
466+
let next = Node.createSimpleTypeName(tn.readIdentifier(), tn.range());
467+
current.next = next;
468+
current = next;
470469
} else {
471470
this.error(
472471
DiagnosticCode.Identifier_expected,
@@ -507,7 +506,7 @@ export class Parser extends DiagnosticEmitter {
507506
return null;
508507
}
509508
}
510-
type = Node.createType(identifier, parameters, nullable, tn.range(startPos, tn.pos));
509+
type = Node.createType(first, parameters, nullable, tn.range(startPos, tn.pos));
511510

512511
} else {
513512
if (!suppressErrors) {
@@ -548,7 +547,7 @@ export class Parser extends DiagnosticEmitter {
548547
}
549548
}
550549
type = Node.createType(
551-
Node.createIdentifierExpression("Array", bracketRange),
550+
Node.createSimpleTypeName("Array", bracketRange),
552551
[ type ],
553552
nullable,
554553
tn.range(startPos, tn.pos)

src/program.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ export class Program extends DiagnosticEmitter {
401401
null,
402402
this.nativeDummySignature || (this.nativeDummySignature = Node.createSignature([],
403403
Node.createType( // ^ AST signature doesn't really matter, is overridden anyway
404-
Node.createIdentifierExpression(CommonSymbols.void_, range),
404+
Node.createSimpleTypeName(CommonSymbols.void_, range),
405405
null, false, range
406406
),
407407
null, false, range)
@@ -661,14 +661,14 @@ export class Program extends DiagnosticEmitter {
661661
for (let i = 0, k = queuedExtends.length; i < k; ++i) {
662662
let thisPrototype = queuedExtends[i];
663663
let extendsNode = assert(thisPrototype.extendsNode); // must be present if in queuedExtends
664-
let baseElement = resolver.resolveIdentifier(extendsNode.name, null, thisPrototype.parent); // reports
664+
let baseElement = resolver.resolveTypeName(extendsNode.name, thisPrototype.parent); // reports
665665
if (!baseElement) continue;
666666
if (baseElement.kind == ElementKind.CLASS_PROTOTYPE) {
667667
let basePrototype = <ClassPrototype>baseElement;
668668
if (basePrototype.hasDecorator(DecoratorFlags.SEALED)) {
669669
this.error(
670670
DiagnosticCode.Class_0_is_sealed_and_cannot_be_extended,
671-
extendsNode.range, extendsNode.name.text
671+
extendsNode.range, (<ClassPrototype>baseElement).identifierNode.text
672672
);
673673
}
674674
if (

src/resolver.ts

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
CommonTypeNode,
3838
NodeKind,
3939
TypeNode,
40+
TypeName,
4041
TypeParameterNode,
4142
Node,
4243
Range,
@@ -174,45 +175,39 @@ export class Resolver extends DiagnosticEmitter {
174175
// now dealing with TypeNode
175176
assert(node.kind == NodeKind.TYPE);
176177
var typeNode = <TypeNode>node;
177-
var typeName = typeNode.name.text;
178+
var typeName = typeNode.name;
178179
var typeArgumentNodes = typeNode.typeArguments;
179180

180-
// look up in contextual type arguments, i.e. `T`
181-
if (contextualTypeArguments && contextualTypeArguments.has(typeName)) {
182-
let type = contextualTypeArguments.get(typeName)!;
183-
if (typeArgumentNodes !== null && typeArgumentNodes.length) {
184-
if (reportMode == ReportMode.REPORT) {
185-
this.error(
186-
DiagnosticCode.Type_0_is_not_generic,
187-
node.range, type.toString()
188-
);
189-
}
190-
}
191-
if (node.isNullable) {
192-
if (!type.is(TypeFlags.REFERENCE)) {
181+
// look up in contextual type arguments if a simple type name
182+
if (!typeName.next) {
183+
if (contextualTypeArguments && contextualTypeArguments.has(typeName.identifier.text)) {
184+
let type = contextualTypeArguments.get(typeName.identifier.text)!;
185+
if (typeArgumentNodes !== null && typeArgumentNodes.length) {
193186
if (reportMode == ReportMode.REPORT) {
194187
this.error(
195-
DiagnosticCode.Basic_type_0_cannot_be_nullable,
188+
DiagnosticCode.Type_0_is_not_generic,
196189
node.range, type.toString()
197190
);
198191
}
199192
}
200-
return type.asNullable();
193+
if (node.isNullable) {
194+
if (!type.is(TypeFlags.REFERENCE)) {
195+
if (reportMode == ReportMode.REPORT) {
196+
this.error(
197+
DiagnosticCode.Basic_type_0_cannot_be_nullable,
198+
node.range, type.toString()
199+
);
200+
}
201+
}
202+
return type.asNullable();
203+
}
204+
return type;
201205
}
202-
return type;
203206
}
204207

205208
// look up in context
206-
var element = context.lookup(typeName);
207-
if (!element) {
208-
if (reportMode == ReportMode.REPORT) {
209-
this.error(
210-
DiagnosticCode.Cannot_find_name_0,
211-
typeNode.name.range, typeName
212-
);
213-
}
214-
return null;
215-
}
209+
var element = this.resolveTypeName(typeName, context, reportMode);
210+
if (!element) return null;
216211

217212
// use shadow type if present (i.e. namespace sharing a type)
218213
if (element.shadowType) element = element.shadowType;
@@ -270,7 +265,7 @@ export class Resolver extends DiagnosticEmitter {
270265
if (reportMode == ReportMode.REPORT) {
271266
this.error(
272267
DiagnosticCode.Basic_type_0_cannot_be_nullable,
273-
typeNode.name.range, typeNode.name.text
268+
typeNode.name.range, typeName.identifier.text
274269
);
275270
}
276271
} else {
@@ -281,7 +276,7 @@ export class Resolver extends DiagnosticEmitter {
281276
}
282277

283278
// handle special native type
284-
if (typeNode.name.text == CommonSymbols.native) {
279+
if (!typeName.next && typeName.identifier.text == CommonSymbols.native) {
285280
if (!(typeArgumentNodes && typeArgumentNodes.length == 1)) {
286281
if (reportMode == ReportMode.REPORT) {
287282
this.error(
@@ -334,7 +329,7 @@ export class Resolver extends DiagnosticEmitter {
334329
} else if (typeArgumentNodes && typeArgumentNodes.length) {
335330
this.error(
336331
DiagnosticCode.Type_0_is_not_generic,
337-
typeNode.range, typeNode.name.text
332+
typeNode.range, typeName.identifier.text
338333
);
339334
// recoverable
340335
}
@@ -349,12 +344,42 @@ export class Resolver extends DiagnosticEmitter {
349344
if (reportMode == ReportMode.REPORT) {
350345
this.error(
351346
DiagnosticCode.Cannot_find_name_0,
352-
typeNode.name.range, typeName
347+
typeNode.name.range, typeName.identifier.text
353348
);
354349
}
355350
return null;
356351
}
357352

353+
/** Resolves the specified type name relative to the given context. */
354+
resolveTypeName(typeName: TypeName, context: Element, reportMode = ReportMode.REPORT): Element | null {
355+
var element = context.lookup(typeName.identifier.text);
356+
if (!element) {
357+
if (reportMode == ReportMode.REPORT) {
358+
this.error(
359+
DiagnosticCode.Cannot_find_name_0,
360+
typeName.range, typeName.identifier.text
361+
);
362+
}
363+
return null;
364+
}
365+
var prev = typeName;
366+
var next = typeName.next;
367+
while (next) {
368+
if (!(element = element.lookupInSelf(next.identifier.text))) {
369+
if (reportMode == ReportMode.REPORT) {
370+
this.error(
371+
DiagnosticCode.Property_0_does_not_exist_on_type_1,
372+
next.range, next.identifier.text, prev.identifier.text
373+
);
374+
}
375+
return null;
376+
}
377+
prev = next;
378+
next = next.next;
379+
}
380+
return element;
381+
}
382+
358383
/** Resolves an array of type arguments to concrete types. */
359384
resolveTypeArguments(
360385
typeParameters: TypeParameterNode[],

0 commit comments

Comments
 (0)