Fixed bug where value-space identifiers got classified as interfaces when sharing the same name as an interface.

This commit is contained in:
Daniel Rosenwasser 2014-10-01 12:44:17 -07:00
parent f2880ce5b7
commit a6e991a3db
6 changed files with 117 additions and 97 deletions

View File

@ -7077,23 +7077,6 @@ module ts {
return mapToArray(symbols);
}
// True if the given identifier is the name of a type declaration node (class, interface, enum, type parameter, etc)
function isTypeDeclarationName(name: Node): boolean {
return name.kind == SyntaxKind.Identifier &&
isTypeDeclaration(name.parent) &&
(<Declaration>name.parent).name === name;
}
function isTypeDeclaration(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.TypeParameter:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
return true;
}
}
// True if the given identifier is part of a type reference
function isTypeReferenceIdentifier(entityName: EntityName): boolean {
var node: Node = entityName;
@ -7172,75 +7155,6 @@ module ts {
return false;
}
function isTypeNode(node: Node): boolean {
if (node.kind >= SyntaxKind.FirstTypeNode && node.kind <= SyntaxKind.LastTypeNode) {
return true;
}
switch (node.kind) {
case SyntaxKind.AnyKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.StringKeyword:
case SyntaxKind.BooleanKeyword:
return true;
case SyntaxKind.VoidKeyword:
return node.parent.kind !== SyntaxKind.PrefixOperator;
case SyntaxKind.StringLiteral:
// Specialized signatures can have string literals as their parameters' type names
return node.parent.kind === SyntaxKind.Parameter;
// Identifiers and qualified names may be type nodes, depending on their context. Climb
// above them to find the lowest container
case SyntaxKind.Identifier:
// If the identifier is the RHS of a qualified name, then it's a type iff its parent is.
if (node.parent.kind === SyntaxKind.QualifiedName) {
node = node.parent;
}
// Fall through
case SyntaxKind.QualifiedName:
// At this point, node is either a qualified name or an identifier
var parent = node.parent;
if (parent.kind === SyntaxKind.TypeQuery) {
return false;
}
// Do not recursively call isTypeNode on the parent. In the example:
//
// var a: A.B.C;
//
// Calling isTypeNode would consider the qualified name A.B a type node. Only C or
// A.B.C is a type node.
if (parent.kind >= SyntaxKind.FirstTypeNode && parent.kind <= SyntaxKind.LastTypeNode) {
return true;
}
switch (parent.kind) {
case SyntaxKind.TypeParameter:
return node === (<TypeParameterDeclaration>parent).constraint;
case SyntaxKind.Property:
case SyntaxKind.Parameter:
case SyntaxKind.VariableDeclaration:
return node === (<VariableDeclaration>parent).type;
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.Constructor:
case SyntaxKind.Method:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return node === (<FunctionDeclaration>parent).type;
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
return node === (<SignatureDeclaration>parent).type;
case SyntaxKind.TypeAssertion:
return node === (<TypeAssertion>parent).type;
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return (<CallExpression>parent).typeArguments.indexOf(node) >= 0;
}
}
return false;
}
function isInRightSideOfImportOrExportAssignment(node: EntityName) {
while (node.parent.kind === SyntaxKind.QualifiedName) {
node = node.parent;

View File

@ -402,6 +402,100 @@ module ts {
return false;
}
/**
* Note: this function only works when given a node with valid parent pointers.
*/
export function isTypeNode(node: Node): boolean {
if (node.kind >= SyntaxKind.FirstTypeNode && node.kind <= SyntaxKind.LastTypeNode) {
return true;
}
switch (node.kind) {
case SyntaxKind.AnyKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.StringKeyword:
case SyntaxKind.BooleanKeyword:
return true;
case SyntaxKind.VoidKeyword:
return node.parent.kind !== SyntaxKind.PrefixOperator;
case SyntaxKind.StringLiteral:
// Specialized signatures can have string literals as their parameters' type names
return node.parent.kind === SyntaxKind.Parameter;
// Identifiers and qualified names may be type nodes, depending on their context. Climb
// above them to find the lowest container
case SyntaxKind.Identifier:
// If the identifier is the RHS of a qualified name, then it's a type iff its parent is.
if (node.parent.kind === SyntaxKind.QualifiedName) {
node = node.parent;
}
// Fall through
case SyntaxKind.QualifiedName:
// At this point, node is either a qualified name or an identifier
var parent = node.parent;
if (parent.kind === SyntaxKind.TypeQuery) {
return false;
}
// Do not recursively call isTypeNode on the parent. In the example:
//
// var a: A.B.C;
//
// Calling isTypeNode would consider the qualified name A.B a type node. Only C or
// A.B.C is a type node.
if (parent.kind >= SyntaxKind.FirstTypeNode && parent.kind <= SyntaxKind.LastTypeNode) {
return true;
}
switch (parent.kind) {
case SyntaxKind.TypeParameter:
return node === (<TypeParameterDeclaration>parent).constraint;
case SyntaxKind.Property:
case SyntaxKind.Parameter:
case SyntaxKind.VariableDeclaration:
return node === (<VariableDeclaration>parent).type;
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.Constructor:
case SyntaxKind.Method:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
return node === (<FunctionDeclaration>parent).type;
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
return node === (<SignatureDeclaration>parent).type;
case SyntaxKind.TypeAssertion:
return node === (<TypeAssertion>parent).type;
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return (<CallExpression>parent).typeArguments.indexOf(node) >= 0;
}
}
return false;
}
/**
* Note: this function only works when given a node with valid parent pointers.
*
* returns true if the given identifier is the name of a type declaration node (class, interface, enum, type parameter, etc)
*/
export function isTypeDeclarationName(name: Node): boolean {
return name.kind == SyntaxKind.Identifier &&
isTypeDeclaration(name.parent) &&
(<Declaration>name.parent).name === name;
}
export function isTypeDeclaration(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.TypeParameter:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
return true;
}
}
export function getContainingFunction(node: Node): SignatureDeclaration {
while (true) {
node = node.parent;

View File

@ -1582,7 +1582,7 @@ module FourSlash {
private verifyClassifications(expected: { classificationType: string; text: string }[], actual: ts.ClassifiedSpan[]) {
if (actual.length !== expected.length) {
this.raiseError('verifySyntacticClassification failed - expected total classifications to be ' + expected.length + ', but was ' + actual.length);
this.raiseError('verifyClassifications failed - expected total classifications to be ' + expected.length + ', but was ' + actual.length);
}
for (var i = 0; i < expected.length; i++) {
@ -1591,7 +1591,7 @@ module FourSlash {
var expectedType: string = (<any>ts.ClassificationTypeNames)[expectedClassification.classificationType];
if (expectedType !== actualClassification.classificationType) {
this.raiseError('verifySyntacticClassification failed - expected classifications type to be ' +
this.raiseError('verifyClassifications failed - expected classifications type to be ' +
expectedType + ', but was ' +
actualClassification.classificationType);
}
@ -1599,7 +1599,7 @@ module FourSlash {
var actualSpan = actualClassification.textSpan;
var actualText = this.activeFile.content.substr(actualSpan.start(), actualSpan.length());
if (expectedClassification.text !== actualText) {
this.raiseError('verifySyntacticClassification failed - expected classificatied text to be ' +
this.raiseError('verifyClassifications failed - expected classificatied text to be ' +
expectedClassification.text + ', but was ' +
actualText);
}

View File

@ -4034,7 +4034,7 @@ module ts {
return result;
function classifySymbol(symbol: Symbol) {
function classifySymbol(symbol: Symbol, isInTypePosition: boolean) {
var flags = symbol.getFlags();
if (flags & SymbolFlags.Class) {
@ -4043,14 +4043,16 @@ module ts {
else if (flags & SymbolFlags.Enum) {
return ClassificationTypeNames.enumName;
}
else if (flags & SymbolFlags.Interface) {
return ClassificationTypeNames.interfaceName;
}
else if (flags & SymbolFlags.Module) {
return ClassificationTypeNames.moduleName;
}
else if (flags & SymbolFlags.TypeParameter) {
return ClassificationTypeNames.typeParameterName;
else if (isInTypePosition) {
if (flags & SymbolFlags.Interface) {
return ClassificationTypeNames.interfaceName;
}
else if (flags & SymbolFlags.TypeParameter) {
return ClassificationTypeNames.typeParameterName;
}
}
}
@ -4060,7 +4062,7 @@ module ts {
if (node.kind === SyntaxKind.Identifier && node.getWidth() > 0) {
var symbol = typeInfoResolver.getSymbolInfo(node);
if (symbol) {
var type = classifySymbol(symbol);
var type = classifySymbol(symbol, isTypeNode(node) || isTypeDeclarationName(node));
if (type) {
result.push({
textSpan: new TypeScript.TextSpan(node.getStart(), node.getWidth()),

View File

@ -6,7 +6,6 @@
//// }
//// interface X extends M.I { }
debugger;
var c = classification;
verify.semanticClassificationsAre(
c.moduleName("M"), c.interfaceName("I"), c.interfaceName("X"), c.moduleName("M"), c.interfaceName("I"));

View File

@ -0,0 +1,11 @@
/// <reference path="fourslash.ts"/>
//// interface Thing {
//// toExponential(): number;
//// }
////
//// var Thing = 0;
//// Thing.toExponential();
var c = classification;
verify.semanticClassificationsAre(c.interfaceName("Thing"));