Wire getDefinitionAtPosition using the new compiler implementation

This commit is contained in:
Mohamed Hegazy
2014-07-31 19:58:18 -07:00
parent 13bda5247b
commit c406662bc4
27 changed files with 372 additions and 50 deletions
+11 -2
View File
@@ -11,6 +11,11 @@ module ts {
var nextNodeId = 1;
var nextMergeId = 1;
// Unknown symbol can survive across different type checking sessions (e.g. in the language service)
// We do use referential comparison to know if a symbol is the unknown symbol. creating a new symbol
// every time would defy that purpose. So we need to have a single object to represent the "unknown" symbol.
var unknownSymbol: Symbol;
export function createTypeChecker(program: Program): TypeChecker {
var Symbol = objectAllocator.getSymbolConstructor();
@@ -24,9 +29,12 @@ module ts {
var undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined");
var argumentsSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "arguments");
var unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown");
var resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__");
if (!unknownSymbol) {
unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown");
}
var anyType = createIntrinsicType(TypeFlags.Any, "any");
var stringType = createIntrinsicType(TypeFlags.String, "string");
var numberType = createIntrinsicType(TypeFlags.Number, "number");
@@ -78,6 +86,7 @@ module ts {
getTypeOfSymbol: getTypeOfSymbol,
getDeclaredTypeOfSymbol: getDeclaredTypeOfSymbol,
getPropertiesOfType: getPropertiesOfType,
getPropertyOfType: getPropertyOfType,
getSignaturesOfType: getSignaturesOfType,
getIndexTypeOfType: getIndexTypeOfType,
getReturnTypeOfSignature: getReturnTypeOfSignature,
@@ -6277,7 +6286,7 @@ module ts {
}
return getNodeLinks(node).resolvedSymbol;
}
return resolveEntityName(identifier, identifier, SymbolFlags.Value);
return resolveName(identifier, identifier.text, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
}
if (isDeclarationIdentifier(identifier)) {
return getSymbolOfNode(identifier.parent);
+1
View File
@@ -593,6 +593,7 @@ module ts {
getTypeOfSymbol(symbol: Symbol): Type;
getDeclaredTypeOfSymbol(symbol: Symbol): Type;
getPropertiesOfType(type: Type): Symbol[];
getPropertyOfType(type: Type, propetyName: string): Symbol;
getSignaturesOfType(type: Type, kind: SignatureKind): Signature[];
getIndexTypeOfType(type: Type, kind: IndexKind): Type;
getReturnTypeOfSignature(signature: Signature): Type;
+234 -7
View File
@@ -53,6 +53,7 @@ module ts {
getFlags(): TypeFlags;
getSymbol(): Symbol;
getProperties(): Symbol[];
getProperty(propertyName: string): Symbol;
getApparentProperties(): Symbol[];
getCallSignatures(): Signature[];
getConstructSignatures(): Signature[];
@@ -251,6 +252,9 @@ module ts {
getProperties(): Symbol[] {
return this.checker.getPropertiesOfType(this);
}
getProperty(propertyName: string): Symbol {
return this.checker.getPropertyOfType(this, propertyName);
}
getApparentProperties(): Symbol[]{
return this.checker.getAugmentedPropertiesOfApparentType(this);
}
@@ -643,6 +647,8 @@ module ts {
static typeParameterElement = "type parameter";
static primitiveType = "primitive type";
static label = "label";
}
export class ScriptElementKindModifier {
@@ -1384,12 +1390,18 @@ module ts {
/// Diagnostics
function getSyntacticDiagnostics(filename: string) {
synchronizeHostData();
return program.getDiagnostics(program.getSourceFile(filename));
filename = TypeScript.switchToForwardSlashes(filename);
return program.getDiagnostics(getDocument(filename).getSourceFile());
}
function getSemanticDiagnostics(filename: string) {
synchronizeHostData();
return typeChecker.getDiagnostics(program.getSourceFile(filename));
filename = TypeScript.switchToForwardSlashes(filename)
return typeChecker.getDiagnostics(getDocument(filename).getSourceFile());
}
function getCompilerOptionsDiagnostics() {
@@ -1756,7 +1768,7 @@ module ts {
}
}
function getEnclosingDeclaration(node: Node): Node {
function getContainerNode(node: Node): Node {
while (true) {
node = node.parent;
if (!node) {
@@ -1849,7 +1861,7 @@ module ts {
return {
memberName: new TypeScript.MemberNameString(typeChecker.typeToString(type)),
docComment: "",
fullSymbolName: typeChecker.symbolToString(symbol, getEnclosingDeclaration(node)),
fullSymbolName: typeChecker.symbolToString(symbol, getContainerNode(node)),
kind: getSymbolKind(symbol),
minChar: node.pos,
limChar: node.end
@@ -1865,7 +1877,7 @@ module ts {
return {
memberName: new TypeScript.MemberNameString(""),
docComment: "",
fullSymbolName: typeChecker.typeToString(type, getEnclosingDeclaration(node)),
fullSymbolName: typeChecker.typeToString(type, getContainerNode(node)),
kind: getTypeKind(type),
minChar: node.pos,
limChar: node.end
@@ -1874,6 +1886,221 @@ module ts {
}
}
/// Goto definition
function getDefinitionAtPosition(filename: string, position: number): DefinitionInfo[]{
function getTargetLabel(statement: BreakOrContinueStatement, labelName: string): Identifier {
var current = statement.parent;
while (current) {
switch (current.kind) {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.Method:
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ClassDeclaration:
// Label targets can not be accorss function boundries, so do not walk any further
return undefined;
case SyntaxKind.LabelledStatement:
if ((<LabelledStatement>current).label.text === labelName) {
return (<LabelledStatement>current).label;
}
break;
}
current = current.parent;
}
return undefined;
}
function isJumpStatementTarget(node: Node): boolean {
return node.kind === SyntaxKind.Identifier &&
(node.parent.kind === SyntaxKind.BreakStatement || node.parent.kind === SyntaxKind.ContinueStatement) &&
(<BreakOrContinueStatement>node.parent).label === node;
}
function isCallExpressionTarget(node: Node): boolean {
if (node.parent.kind === SyntaxKind.PropertyAccess && (<PropertyAccess>node.parent).right === node)
node = node.parent;
return node.parent.kind === SyntaxKind.CallExpression && (<CallExpression>node.parent).func === node;
}
function isNewExpressionTarget(node: Node): boolean {
if (node.parent.kind === SyntaxKind.PropertyAccess && (<PropertyAccess>node.parent).right === node)
node = node.parent;
return node.parent.kind === SyntaxKind.NewExpression && (<CallExpression>node.parent).func === node;
}
function isFunctionDeclaration(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.Method:
case SyntaxKind.FunctionExpression:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.ArrowFunction:
return true;
}
return false;
}
function isNameOfFunctionDeclaration(node: Node): boolean {
return node.kind === SyntaxKind.Identifier &&
isFunctionDeclaration(node.parent) && (<FunctionDeclaration>node.parent).name === node;
}
function getDefinitionInfo(node: Node, symbolKind: string, symbolName: string, containerName: string): DefinitionInfo {
return {
fileName: node.getSourceFile().filename,
minChar: node.getStart(),
limChar: node.getEnd(),
kind: symbolKind,
name: symbolName,
containerName: containerName,
containerKind: undefined
};
}
function tryAddSignature(signatureDeclarations: Declaration[], selectConstructors: boolean, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) {
var declarations: Declaration[] = [];
var definition: Declaration;
forEach(signatureDeclarations, d => {
if ((selectConstructors && d.kind === SyntaxKind.Constructor) ||
(!selectConstructors && (d.kind === SyntaxKind.FunctionDeclaration || d.kind === SyntaxKind.Method))) {
declarations.push(d);
if ((<FunctionDeclaration>d).body) definition = d;
}
});
if (definition) {
result.push(getDefinitionInfo(definition, symbolKind, symbolName, containerName));
return true;
}
else if (declarations.length) {
result.push(getDefinitionInfo(declarations[declarations.length - 1], symbolKind, symbolName, containerName));
return true;
}
return false
}
function tryAddConstructSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) {
// Applicaple only if we are:in a new expression, or we are on a constructor declaration
// and in either case the symbol has a construct signature definition, i.e.class
if (isNewExpressionTarget(location) || location.kind === SyntaxKind.ConstructorKeyword) {
if (symbol.flags & SymbolFlags.Class) {
var classDeclaration = <ClassDeclaration>symbol.getDeclarations()[0];
Debug.assert(classDeclaration && classDeclaration.kind === SyntaxKind.ClassDeclaration);
return tryAddSignature(classDeclaration.members, /*selectConstructors*/ true, symbolKind, symbolName, containerName, result);
}
}
return false;
}
function tryAddCallSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) {
if (isCallExpressionTarget(location) || isNewExpressionTarget(location) || isNameOfFunctionDeclaration(location)) {
return tryAddSignature(symbol.declarations, /*selectConstructors*/ false, symbolKind, symbolName, containerName, result);
}
return false;
}
synchronizeHostData();
filename = TypeScript.switchToForwardSlashes(filename);
var document = getDocument(filename);
var node = getNodeAtPosition(document.getSourceFile(), position);
if (!node) {
return undefined;
}
// Labels
if (isJumpStatementTarget(node)) {
var labelName = (<Identifier>node).text;
var label = getTargetLabel((<BreakOrContinueStatement>node.parent), (<Identifier>node).text);
return label ? [getDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined;
}
/// Trible slash reference comments
var comment = forEach(document.getSourceFile().referencedFiles, r => (r.pos <= position && position < r.end) ? r : undefined);
if (comment) {
var targetFilename = normalizePath(combinePaths(getDirectoryPath(filename), comment.filename));
if (program.getSourceFile(targetFilename)) {
return [{
fileName: targetFilename, minChar: 0, limChar: 0,
kind: ScriptElementKind.scriptElement,
name: comment.filename, containerName: undefined, containerKind: undefined
}];
}
return undefined;
}
var symbol: Symbol;
switch (node.kind) {
case SyntaxKind.Identifier:
symbol = typeChecker.getSymbolOfIdentifier(<Identifier>node);
break;
case SyntaxKind.ThisKeyword:
case SyntaxKind.SuperKeyword:
var type = typeChecker.getTypeOfExpression(node);
symbol = type.getSymbol();
break;
case SyntaxKind.ConstructorKeyword:
// constructor keyword for an overload, should take us to the definition if it exist
var container = getContainerNode(node);
if (container && container.kind === SyntaxKind.ClassDeclaration) {
symbol = (<ClassDeclaration>container).symbol;
}
break;
case SyntaxKind.StringLiteral:
// Property access
if (node.parent.kind === SyntaxKind.IndexedAccess && (<IndexedAccess>node.parent).index === node) {
var objectType = typeChecker.getTypeOfExpression((<IndexedAccess>node.parent).object);
Debug.assert(objectType);
symbol = objectType.getProperty((<LiteralExpression>node).text);
}
// External module name in an import declaration
else if (node.parent.kind === SyntaxKind.ImportDeclaration && (<ImportDeclaration>node.parent).externalModuleName === node) {
var importSymbol = typeChecker.getSymbolOfNode(node.parent);
var moduleType = typeChecker.getTypeOfSymbol(importSymbol);
symbol = moduleType ? moduleType.symbol : undefined;
}
break;
}
// Could not find a symbol e.g. node is string or number keyword,
// or the symbol was an internal symbol (transient) e.g. undefined symbol
if (!symbol || symbol.flags & SymbolFlags.Transient) {
return undefined;
}
var result: DefinitionInfo[] = [];
var declarations = symbol.getDeclarations();
var symbolName = typeChecker.symbolToString(symbol, node);
var symbolKind = getSymbolKind(symbol);
var containerSymbol = symbol.parent;
var containerName = containerSymbol ? typeChecker.symbolToString(containerSymbol, node) : "";
var containerKind = containerSymbol ? getSymbolKind(symbol) : "";
if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) &&
!tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) {
// Just add all the declarations.
forEach(declarations, declaration => {
result.push(getDefinitionInfo(declaration, symbolKind, symbolName, containerName));
});
}
return result;
}
/// Syntactic features
function getSyntaxTree(filename: string): TypeScript.SyntaxTree {
filename = TypeScript.switchToForwardSlashes(filename);
@@ -2043,7 +2270,7 @@ module ts {
getCompletionEntryDetails: getCompletionEntryDetails,
getTypeAtPosition: getTypeAtPosition,
getSignatureAtPosition: (filename, position): SignatureInfo => undefined,
getDefinitionAtPosition: (filename, position) => [],
getDefinitionAtPosition: getDefinitionAtPosition,
getReferencesAtPosition: (filename, position) => [],
getOccurrencesAtPosition: (filename, position) => [],
getImplementorsAtPosition: (filename, position) => [],
@@ -2233,4 +2460,4 @@ module ts {
}
initializeServices();
}
}
@@ -7,7 +7,7 @@
//// /*staticMethodDefinition*/static method();
//// /*instanceMethodDefinition*/public method();
////}
/////
////
/////*ambientVariableReference*/ambientVar = 1;
/////*ambientFunctionReference*/ambientFunction();
////var ambientClassVariable = new /*constructorReference*/ambientClass();
@@ -11,11 +11,11 @@
goTo.marker('constructorOverloadReference1');
goTo.definition();
verify.caretAtMarker('constructorOverload1');
verify.caretAtMarker('constructorDefinition');
goTo.marker('constructorOverloadReference2');
goTo.definition();
verify.caretAtMarker('constructorOverload2');
verify.caretAtMarker('constructorDefinition');
goTo.marker('constructorOverload1');
goTo.definition();
@@ -5,7 +5,7 @@
/////*remoteFunctionDefinition*/function remoteFunction() { }
/////*remoteClassDefinition*/class remoteClass { }
/////*remoteInterfaceDefinition*/interface remoteInterface{ }
////module /*remoteModuleDefinition*/remoteModule{ export var foo = 1;}
/////*remoteModuleDefinition*/module remoteModule{ export var foo = 1;}
// @Filename: goToDefinitionDifferentFile_Consumption.ts
/////*remoteVariableReference*/remoteVariable = 1;
@@ -5,7 +5,7 @@
/////*remoteFunctionDefinition*/function rem2Fn() { }
/////*remoteClassDefinition*/class rem2Cls { }
/////*remoteInterfaceDefinition*/interface rem2Int{}
////module /*remoteModuleDefinition*/rem2Mod { export var foo; }
/////*remoteModuleDefinition*/module rem2Mod { export var foo; }
// @Filename: Remote1.ts
////var remVar;
@@ -0,0 +1,12 @@
/// <reference path='fourslash.ts'/>
// @Filename: b.ts
////import n = require('a/*1*/');
////var x = new n.Foo();
// @Filename: a.ts
//// /*2*/export class Foo {}
goTo.marker('1');
goTo.definition();
verify.caretAtMarker('2');
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts'/>
// @Filename: b.ts
////import n = require('a/*1*/');
////var x = new n.Foo();
// @Filename: a.ts
/////*2*/class Foo {}
////export var x = 0;
goTo.marker('1');
goTo.definition();
verify.caretAtMarker('2');
@@ -0,0 +1,14 @@
/// <reference path='fourslash.ts'/>
// @Filename: b.ts
////import n = require('e/*1*/');
////var x = new n.Foo();
// @Filename: a.ts
/////*2*/declare module "e" {
//// class Foo { }
////}
goTo.marker('1');
goTo.definition();
verify.caretAtMarker('2');
@@ -0,0 +1,7 @@
/// <reference path='fourslash.ts'/>
// @Filename: b.ts
////import n = require('unknown/*1*/');
goTo.marker('1');
verify.not.definitionLocationExists();
@@ -9,11 +9,11 @@
goTo.marker('functionOverloadReference1');
goTo.definition();
verify.caretAtMarker('functionOverload1');
verify.caretAtMarker('functionOverloadDefinition');
goTo.marker('functionOverloadReference2');
goTo.definition();
verify.caretAtMarker('functionOverload2');
verify.caretAtMarker('functionOverloadDefinition');
goTo.marker('functionOverload');
goTo.definition();
@@ -0,0 +1,26 @@
/// <reference path='fourslash.ts' />
/////*label1Definition*/label1: while (true) {
//// /*label2Definition*/label2: while (true) {
//// break /*1*/label1;
//// continue /*2*/label2;
//// () => { break /*3*/label1; }
//// continue /*4*/unknownLabel;
//// }
////}
goTo.marker('1');
goTo.definition();
verify.caretAtMarker('label1Definition');
goTo.marker('2');
goTo.definition();
verify.caretAtMarker('label2Definition');
// no labels accross function bounderies
goTo.marker('3');
verify.not.definitionLocationExists();
// undefined label
goTo.marker('4');
verify.not.definitionLocationExists();
@@ -1,11 +1,11 @@
/// <reference path='fourslash.ts' />
////class MethodOverload {
//// /*staticMethodOverload1*/static method();
//// /*staticMethodOverload2*/static method(foo: string);
//// static me/*staticMethodOverload1*/thod();
//// static me/*staticMethodOverload2*/thod(foo: string);
/////*staticMethodDefinition*/static method(foo?: any) { }
//// /*instanceMethodOverload1*/public method(): any;
//// /*instanceMethodOverload2*/public method(foo: string);
//// public met/*instanceMethodOverload1*/hod(): any;
//// public met/*instanceMethodOverload2*/hod(foo: string);
/////*instanceMethodDefinition*/public method(foo?: any) { return "foo" }
////}
@@ -20,19 +20,19 @@
goTo.marker('staticMethodReference1');
goTo.definition();
verify.caretAtMarker('staticMethodOverload1');
verify.caretAtMarker('staticMethodDefinition');
goTo.marker('staticMethodReference2');
goTo.definition();
verify.caretAtMarker('staticMethodOverload2');
verify.caretAtMarker('staticMethodDefinition');
goTo.marker('instanceMethodReference1');
goTo.definition();
verify.caretAtMarker('instanceMethodOverload1');
verify.caretAtMarker('instanceMethodDefinition');
goTo.marker('instanceMethodReference2');
goTo.definition();
verify.caretAtMarker('instanceMethodOverload2');
verify.caretAtMarker('instanceMethodDefinition');
goTo.marker('staticMethodOverload1');
goTo.definition();
@@ -1,9 +1,11 @@
/// <reference path='fourslash.ts' />
/// <reference path='fourslash.ts' />
// @Filename: a.ts
/////*interfaceDefintion1*/interface IFoo {
//// instance1: number;
////}
// @Filename: b.ts
/////*interfaceDefintion2*/interface IFoo {
//// instance2: number;
////}
@@ -14,33 +16,36 @@
////
////var ifoo: IFo/*interfaceReference*/o;
goTo.marker('interfaceReference');
goTo.definition(0);
verify.caretAtMarker('interfaceDefintion1');
goTo.marker('interfaceReference');
goTo.definition(1);
verify.caretAtMarker('interfaceDefintion2');
goTo.marker('interfaceReference');
goTo.definition(2);
verify.caretAtMarker('interfaceDefintion3');
goTo.marker('interfaceReference');
goTo.definition(0);
verify.caretAtMarker('interfaceDefintion1');
goTo.marker('interfaceReference');
goTo.definition(1);
verify.caretAtMarker('interfaceDefintion2');
goTo.marker('interfaceReference');
goTo.definition(2);
verify.caretAtMarker('interfaceDefintion3');
// @Filename: c.ts
/////*moduleDefintion1*/module Module {
//// export class c1 { }
////}
// @Filename: d.ts
/////*moduleDefintion2*/module Module {
//// export class c2 { }
////}
// @Filename: e.ts
////Modul/*moduleReference*/e;
goTo.marker('moduleReference');
goTo.definition(0);
verify.caretAtMarker('moduleDefintion1');
goTo.marker('moduleReference');
goTo.definition(1);
verify.caretAtMarker('moduleDefintion2');
goTo.marker('moduleReference');
goTo.definition(0);
verify.caretAtMarker('moduleDefintion1');
goTo.marker('moduleReference');
goTo.definition(1);
verify.caretAtMarker('moduleDefintion2');
@@ -4,7 +4,7 @@
/////*localFunctionDefinition*/function localFunction() { }
/////*localClassDefinition*/class localClass { }
/////*localInterfaceDefinition*/interface localInterface{ }
////module /*localModuleDefinition*/localModule{ export var foo = 1;}
/////*localModuleDefinition*/module localModule{ export var foo = 1;}
////
////
/////*localVariableReference*/localVariable = 1;
@@ -1,9 +1,10 @@
/// <reference path='fourslash.ts'/>
////
// @Filename: a.ts
//// //MyFile Comments
//// //more comments
//// /// <reference path="so/**/mePath.ts" />
//// /// <reference path="so/*unknownFile*/mePath.ts" />
//// /// <reference path="b/*knownFile*/.ts" />
////
//// class clsInOverload {
//// static fnOverload();
@@ -12,5 +13,12 @@
//// }
////
goTo.marker();
// @Filename: b.ts
/////*fileB*/
goTo.marker("unknownFile");
verify.not.definitionLocationExists();
goTo.marker("knownFile");
goTo.definition();
verify.caretAtMarker('fileB');