mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 16:38:05 -06:00
Fixed bug where value-space identifiers got classified as interfaces when sharing the same name as an interface.
This commit is contained in:
parent
f2880ce5b7
commit
a6e991a3db
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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()),
|
||||
|
||||
@ -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"));
|
||||
|
||||
11
tests/cases/fourslash/semanticClassification2.ts
Normal file
11
tests/cases/fourslash/semanticClassification2.ts
Normal 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"));
|
||||
Loading…
x
Reference in New Issue
Block a user