Skip to content

Commit a6e991a

Browse files
Fixed bug where value-space identifiers got classified as interfaces when sharing the same name as an interface.
1 parent f2880ce commit a6e991a

File tree

6 files changed

+117
-97
lines changed

6 files changed

+117
-97
lines changed

src/compiler/checker.ts

Lines changed: 0 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -7077,23 +7077,6 @@ module ts {
70777077
return mapToArray(symbols);
70787078
}
70797079

7080-
// True if the given identifier is the name of a type declaration node (class, interface, enum, type parameter, etc)
7081-
function isTypeDeclarationName(name: Node): boolean {
7082-
return name.kind == SyntaxKind.Identifier &&
7083-
isTypeDeclaration(name.parent) &&
7084-
(<Declaration>name.parent).name === name;
7085-
}
7086-
7087-
function isTypeDeclaration(node: Node): boolean {
7088-
switch (node.kind) {
7089-
case SyntaxKind.TypeParameter:
7090-
case SyntaxKind.ClassDeclaration:
7091-
case SyntaxKind.InterfaceDeclaration:
7092-
case SyntaxKind.EnumDeclaration:
7093-
return true;
7094-
}
7095-
}
7096-
70977080
// True if the given identifier is part of a type reference
70987081
function isTypeReferenceIdentifier(entityName: EntityName): boolean {
70997082
var node: Node = entityName;
@@ -7172,75 +7155,6 @@ module ts {
71727155
return false;
71737156
}
71747157

7175-
function isTypeNode(node: Node): boolean {
7176-
if (node.kind >= SyntaxKind.FirstTypeNode && node.kind <= SyntaxKind.LastTypeNode) {
7177-
return true;
7178-
}
7179-
7180-
switch (node.kind) {
7181-
case SyntaxKind.AnyKeyword:
7182-
case SyntaxKind.NumberKeyword:
7183-
case SyntaxKind.StringKeyword:
7184-
case SyntaxKind.BooleanKeyword:
7185-
return true;
7186-
case SyntaxKind.VoidKeyword:
7187-
return node.parent.kind !== SyntaxKind.PrefixOperator;
7188-
case SyntaxKind.StringLiteral:
7189-
// Specialized signatures can have string literals as their parameters' type names
7190-
return node.parent.kind === SyntaxKind.Parameter;
7191-
// Identifiers and qualified names may be type nodes, depending on their context. Climb
7192-
// above them to find the lowest container
7193-
case SyntaxKind.Identifier:
7194-
// If the identifier is the RHS of a qualified name, then it's a type iff its parent is.
7195-
if (node.parent.kind === SyntaxKind.QualifiedName) {
7196-
node = node.parent;
7197-
}
7198-
// Fall through
7199-
case SyntaxKind.QualifiedName:
7200-
// At this point, node is either a qualified name or an identifier
7201-
var parent = node.parent;
7202-
if (parent.kind === SyntaxKind.TypeQuery) {
7203-
return false;
7204-
}
7205-
// Do not recursively call isTypeNode on the parent. In the example:
7206-
//
7207-
// var a: A.B.C;
7208-
//
7209-
// Calling isTypeNode would consider the qualified name A.B a type node. Only C or
7210-
// A.B.C is a type node.
7211-
if (parent.kind >= SyntaxKind.FirstTypeNode && parent.kind <= SyntaxKind.LastTypeNode) {
7212-
return true;
7213-
}
7214-
switch (parent.kind) {
7215-
case SyntaxKind.TypeParameter:
7216-
return node === (<TypeParameterDeclaration>parent).constraint;
7217-
case SyntaxKind.Property:
7218-
case SyntaxKind.Parameter:
7219-
case SyntaxKind.VariableDeclaration:
7220-
return node === (<VariableDeclaration>parent).type;
7221-
case SyntaxKind.FunctionDeclaration:
7222-
case SyntaxKind.FunctionExpression:
7223-
case SyntaxKind.ArrowFunction:
7224-
case SyntaxKind.Constructor:
7225-
case SyntaxKind.Method:
7226-
case SyntaxKind.GetAccessor:
7227-
case SyntaxKind.SetAccessor:
7228-
return node === (<FunctionDeclaration>parent).type;
7229-
case SyntaxKind.CallSignature:
7230-
case SyntaxKind.ConstructSignature:
7231-
case SyntaxKind.IndexSignature:
7232-
return node === (<SignatureDeclaration>parent).type;
7233-
case SyntaxKind.TypeAssertion:
7234-
return node === (<TypeAssertion>parent).type;
7235-
case SyntaxKind.CallExpression:
7236-
case SyntaxKind.NewExpression:
7237-
return (<CallExpression>parent).typeArguments.indexOf(node) >= 0;
7238-
}
7239-
}
7240-
7241-
return false;
7242-
}
7243-
72447158
function isInRightSideOfImportOrExportAssignment(node: EntityName) {
72457159
while (node.parent.kind === SyntaxKind.QualifiedName) {
72467160
node = node.parent;

src/compiler/parser.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,100 @@ module ts {
402402
return false;
403403
}
404404

405+
/**
406+
* Note: this function only works when given a node with valid parent pointers.
407+
*/
408+
export function isTypeNode(node: Node): boolean {
409+
if (node.kind >= SyntaxKind.FirstTypeNode && node.kind <= SyntaxKind.LastTypeNode) {
410+
return true;
411+
}
412+
413+
switch (node.kind) {
414+
case SyntaxKind.AnyKeyword:
415+
case SyntaxKind.NumberKeyword:
416+
case SyntaxKind.StringKeyword:
417+
case SyntaxKind.BooleanKeyword:
418+
return true;
419+
case SyntaxKind.VoidKeyword:
420+
return node.parent.kind !== SyntaxKind.PrefixOperator;
421+
case SyntaxKind.StringLiteral:
422+
// Specialized signatures can have string literals as their parameters' type names
423+
return node.parent.kind === SyntaxKind.Parameter;
424+
// Identifiers and qualified names may be type nodes, depending on their context. Climb
425+
// above them to find the lowest container
426+
case SyntaxKind.Identifier:
427+
// If the identifier is the RHS of a qualified name, then it's a type iff its parent is.
428+
if (node.parent.kind === SyntaxKind.QualifiedName) {
429+
node = node.parent;
430+
}
431+
// Fall through
432+
case SyntaxKind.QualifiedName:
433+
// At this point, node is either a qualified name or an identifier
434+
var parent = node.parent;
435+
if (parent.kind === SyntaxKind.TypeQuery) {
436+
return false;
437+
}
438+
// Do not recursively call isTypeNode on the parent. In the example:
439+
//
440+
// var a: A.B.C;
441+
//
442+
// Calling isTypeNode would consider the qualified name A.B a type node. Only C or
443+
// A.B.C is a type node.
444+
if (parent.kind >= SyntaxKind.FirstTypeNode && parent.kind <= SyntaxKind.LastTypeNode) {
445+
return true;
446+
}
447+
switch (parent.kind) {
448+
case SyntaxKind.TypeParameter:
449+
return node === (<TypeParameterDeclaration>parent).constraint;
450+
case SyntaxKind.Property:
451+
case SyntaxKind.Parameter:
452+
case SyntaxKind.VariableDeclaration:
453+
return node === (<VariableDeclaration>parent).type;
454+
case SyntaxKind.FunctionDeclaration:
455+
case SyntaxKind.FunctionExpression:
456+
case SyntaxKind.ArrowFunction:
457+
case SyntaxKind.Constructor:
458+
case SyntaxKind.Method:
459+
case SyntaxKind.GetAccessor:
460+
case SyntaxKind.SetAccessor:
461+
return node === (<FunctionDeclaration>parent).type;
462+
case SyntaxKind.CallSignature:
463+
case SyntaxKind.ConstructSignature:
464+
case SyntaxKind.IndexSignature:
465+
return node === (<SignatureDeclaration>parent).type;
466+
case SyntaxKind.TypeAssertion:
467+
return node === (<TypeAssertion>parent).type;
468+
case SyntaxKind.CallExpression:
469+
case SyntaxKind.NewExpression:
470+
return (<CallExpression>parent).typeArguments.indexOf(node) >= 0;
471+
}
472+
}
473+
474+
return false;
475+
}
476+
477+
/**
478+
* Note: this function only works when given a node with valid parent pointers.
479+
*
480+
* returns true if the given identifier is the name of a type declaration node (class, interface, enum, type parameter, etc)
481+
*/
482+
export function isTypeDeclarationName(name: Node): boolean {
483+
return name.kind == SyntaxKind.Identifier &&
484+
isTypeDeclaration(name.parent) &&
485+
(<Declaration>name.parent).name === name;
486+
}
487+
488+
489+
export function isTypeDeclaration(node: Node): boolean {
490+
switch (node.kind) {
491+
case SyntaxKind.TypeParameter:
492+
case SyntaxKind.ClassDeclaration:
493+
case SyntaxKind.InterfaceDeclaration:
494+
case SyntaxKind.EnumDeclaration:
495+
return true;
496+
}
497+
}
498+
405499
export function getContainingFunction(node: Node): SignatureDeclaration {
406500
while (true) {
407501
node = node.parent;

src/harness/fourslash.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1582,7 +1582,7 @@ module FourSlash {
15821582

15831583
private verifyClassifications(expected: { classificationType: string; text: string }[], actual: ts.ClassifiedSpan[]) {
15841584
if (actual.length !== expected.length) {
1585-
this.raiseError('verifySyntacticClassification failed - expected total classifications to be ' + expected.length + ', but was ' + actual.length);
1585+
this.raiseError('verifyClassifications failed - expected total classifications to be ' + expected.length + ', but was ' + actual.length);
15861586
}
15871587

15881588
for (var i = 0; i < expected.length; i++) {
@@ -1591,15 +1591,15 @@ module FourSlash {
15911591

15921592
var expectedType: string = (<any>ts.ClassificationTypeNames)[expectedClassification.classificationType];
15931593
if (expectedType !== actualClassification.classificationType) {
1594-
this.raiseError('verifySyntacticClassification failed - expected classifications type to be ' +
1594+
this.raiseError('verifyClassifications failed - expected classifications type to be ' +
15951595
expectedType + ', but was ' +
15961596
actualClassification.classificationType);
15971597
}
15981598

15991599
var actualSpan = actualClassification.textSpan;
16001600
var actualText = this.activeFile.content.substr(actualSpan.start(), actualSpan.length());
16011601
if (expectedClassification.text !== actualText) {
1602-
this.raiseError('verifySyntacticClassification failed - expected classificatied text to be ' +
1602+
this.raiseError('verifyClassifications failed - expected classificatied text to be ' +
16031603
expectedClassification.text + ', but was ' +
16041604
actualText);
16051605
}

src/services/services.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4034,7 +4034,7 @@ module ts {
40344034

40354035
return result;
40364036

4037-
function classifySymbol(symbol: Symbol) {
4037+
function classifySymbol(symbol: Symbol, isInTypePosition: boolean) {
40384038
var flags = symbol.getFlags();
40394039

40404040
if (flags & SymbolFlags.Class) {
@@ -4043,14 +4043,16 @@ module ts {
40434043
else if (flags & SymbolFlags.Enum) {
40444044
return ClassificationTypeNames.enumName;
40454045
}
4046-
else if (flags & SymbolFlags.Interface) {
4047-
return ClassificationTypeNames.interfaceName;
4048-
}
40494046
else if (flags & SymbolFlags.Module) {
40504047
return ClassificationTypeNames.moduleName;
40514048
}
4052-
else if (flags & SymbolFlags.TypeParameter) {
4053-
return ClassificationTypeNames.typeParameterName;
4049+
else if (isInTypePosition) {
4050+
if (flags & SymbolFlags.Interface) {
4051+
return ClassificationTypeNames.interfaceName;
4052+
}
4053+
else if (flags & SymbolFlags.TypeParameter) {
4054+
return ClassificationTypeNames.typeParameterName;
4055+
}
40544056
}
40554057
}
40564058

@@ -4060,7 +4062,7 @@ module ts {
40604062
if (node.kind === SyntaxKind.Identifier && node.getWidth() > 0) {
40614063
var symbol = typeInfoResolver.getSymbolInfo(node);
40624064
if (symbol) {
4063-
var type = classifySymbol(symbol);
4065+
var type = classifySymbol(symbol, isTypeNode(node) || isTypeDeclarationName(node));
40644066
if (type) {
40654067
result.push({
40664068
textSpan: new TypeScript.TextSpan(node.getStart(), node.getWidth()),

tests/cases/fourslash/semanticClassification1.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
//// }
77
//// interface X extends M.I { }
88

9-
debugger;
109
var c = classification;
1110
verify.semanticClassificationsAre(
1211
c.moduleName("M"), c.interfaceName("I"), c.interfaceName("X"), c.moduleName("M"), c.interfaceName("I"));
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/// <reference path="fourslash.ts"/>
2+
3+
//// interface Thing {
4+
//// toExponential(): number;
5+
//// }
6+
////
7+
//// var Thing = 0;
8+
//// Thing.toExponential();
9+
10+
var c = classification;
11+
verify.semanticClassificationsAre(c.interfaceName("Thing"));

0 commit comments

Comments
 (0)