Skip to content

Commit

Permalink
quickInfo: Don't check for type === undefined, check for any (#17815
Browse files Browse the repository at this point in the history
)

* quickInfo: Don't check for `type === undefined`, check for `any`

* Fixes:
* We still want to do some work even if type is `any`
* Second test for `if (type)` is necessary because it might not have been assigned.
  • Loading branch information
Andy authored Aug 19, 2017
1 parent a136f55 commit 5e8e735
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 100 deletions.
201 changes: 101 additions & 100 deletions src/services/symbolDisplay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,124 +111,123 @@ namespace ts.SymbolDisplay {

let signature: Signature;
type = isThisExpression ? typeChecker.getTypeAtLocation(location) : typeChecker.getTypeOfSymbolAtLocation(symbol.exportSymbol || symbol, location);
if (type) {
if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) {
const right = (<PropertyAccessExpression>location.parent).name;
// Either the location is on the right of a property access, or on the left and the right is missing
if (right === location || (right && right.getFullWidth() === 0)) {
location = location.parent;
}
}

// try get the call/construct signature from the type if it matches
let callExpressionLike: CallExpression | NewExpression | JsxOpeningLikeElement;
if (isCallOrNewExpression(location)) {
callExpressionLike = <CallExpression | NewExpression>location;
}
else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) {
callExpressionLike = <CallExpression | NewExpression>location.parent;
if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) {
const right = (<PropertyAccessExpression>location.parent).name;
// Either the location is on the right of a property access, or on the left and the right is missing
if (right === location || (right && right.getFullWidth() === 0)) {
location = location.parent;
}
else if (location.parent && isJsxOpeningLikeElement(location.parent) && isFunctionLike(symbol.valueDeclaration)) {
callExpressionLike = <JsxOpeningLikeElement>location.parent;
}

// try get the call/construct signature from the type if it matches
let callExpressionLike: CallExpression | NewExpression | JsxOpeningLikeElement;
if (isCallOrNewExpression(location)) {
callExpressionLike = <CallExpression | NewExpression>location;
}
else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) {
callExpressionLike = <CallExpression | NewExpression>location.parent;
}
else if (location.parent && isJsxOpeningLikeElement(location.parent) && isFunctionLike(symbol.valueDeclaration)) {
callExpressionLike = <JsxOpeningLikeElement>location.parent;
}

if (callExpressionLike) {
const candidateSignatures: Signature[] = [];
signature = typeChecker.getResolvedSignature(callExpressionLike, candidateSignatures);
if (!signature && candidateSignatures.length) {
// Use the first candidate:
signature = candidateSignatures[0];
}

if (callExpressionLike) {
const candidateSignatures: Signature[] = [];
signature = typeChecker.getResolvedSignature(callExpressionLike, candidateSignatures);
if (!signature && candidateSignatures.length) {
// Use the first candidate:
signature = candidateSignatures[0];
}
const useConstructSignatures = callExpressionLike.kind === SyntaxKind.NewExpression || (isCallExpression(callExpressionLike) && callExpressionLike.expression.kind === SyntaxKind.SuperKeyword);

const useConstructSignatures = callExpressionLike.kind === SyntaxKind.NewExpression || (isCallExpression(callExpressionLike) && callExpressionLike.expression.kind === SyntaxKind.SuperKeyword);
const allSignatures = useConstructSignatures ? type.getConstructSignatures() : type.getCallSignatures();

const allSignatures = useConstructSignatures ? type.getConstructSignatures() : type.getCallSignatures();
if (!contains(allSignatures, signature.target) && !contains(allSignatures, signature)) {
// Get the first signature if there is one -- allSignatures may contain
// either the original signature or its target, so check for either
signature = allSignatures.length ? allSignatures[0] : undefined;
}

if (!contains(allSignatures, signature.target) && !contains(allSignatures, signature)) {
// Get the first signature if there is one -- allSignatures may contain
// either the original signature or its target, so check for either
signature = allSignatures.length ? allSignatures[0] : undefined;
if (signature) {
if (useConstructSignatures && (symbolFlags & SymbolFlags.Class)) {
// Constructor
symbolKind = ScriptElementKind.constructorImplementationElement;
addPrefixForAnyFunctionOrVar(type.symbol, symbolKind);
}

if (signature) {
if (useConstructSignatures && (symbolFlags & SymbolFlags.Class)) {
// Constructor
symbolKind = ScriptElementKind.constructorImplementationElement;
addPrefixForAnyFunctionOrVar(type.symbol, symbolKind);
else if (symbolFlags & SymbolFlags.Alias) {
symbolKind = ScriptElementKind.alias;
pushTypePart(symbolKind);
displayParts.push(spacePart());
if (useConstructSignatures) {
displayParts.push(keywordPart(SyntaxKind.NewKeyword));
displayParts.push(spacePart());
}
else if (symbolFlags & SymbolFlags.Alias) {
symbolKind = ScriptElementKind.alias;
pushTypePart(symbolKind);
addFullSymbolName(symbol);
}
else {
addPrefixForAnyFunctionOrVar(symbol, symbolKind);
}

switch (symbolKind) {
case ScriptElementKind.jsxAttribute:
case ScriptElementKind.memberVariableElement:
case ScriptElementKind.variableElement:
case ScriptElementKind.constElement:
case ScriptElementKind.letElement:
case ScriptElementKind.parameterElement:
case ScriptElementKind.localVariableElement:
// If it is call or construct signature of lambda's write type name
displayParts.push(punctuationPart(SyntaxKind.ColonToken));
displayParts.push(spacePart());
if (useConstructSignatures) {
displayParts.push(keywordPart(SyntaxKind.NewKeyword));
displayParts.push(spacePart());
}
addFullSymbolName(symbol);
}
else {
addPrefixForAnyFunctionOrVar(symbol, symbolKind);
}
if (!(type.flags & TypeFlags.Object && (<ObjectType>type).objectFlags & ObjectFlags.Anonymous) && type.symbol) {
addRange(displayParts, symbolToDisplayParts(typeChecker, type.symbol, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments));
}
addSignatureDisplayParts(signature, allSignatures, TypeFormatFlags.WriteArrowStyleSignature);
break;

switch (symbolKind) {
case ScriptElementKind.jsxAttribute:
case ScriptElementKind.memberVariableElement:
case ScriptElementKind.variableElement:
case ScriptElementKind.constElement:
case ScriptElementKind.letElement:
case ScriptElementKind.parameterElement:
case ScriptElementKind.localVariableElement:
// If it is call or construct signature of lambda's write type name
displayParts.push(punctuationPart(SyntaxKind.ColonToken));
displayParts.push(spacePart());
if (useConstructSignatures) {
displayParts.push(keywordPart(SyntaxKind.NewKeyword));
displayParts.push(spacePart());
}
if (!(type.flags & TypeFlags.Object && (<ObjectType>type).objectFlags & ObjectFlags.Anonymous) && type.symbol) {
addRange(displayParts, symbolToDisplayParts(typeChecker, type.symbol, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments));
}
addSignatureDisplayParts(signature, allSignatures, TypeFormatFlags.WriteArrowStyleSignature);
break;

default:
// Just signature
addSignatureDisplayParts(signature, allSignatures);
}
hasAddedSymbolInfo = true;
default:
// Just signature
addSignatureDisplayParts(signature, allSignatures);
}
hasAddedSymbolInfo = true;
}
else if ((isNameOfFunctionDeclaration(location) && !(symbolFlags & SymbolFlags.Accessor)) || // name of function declaration
(location.kind === SyntaxKind.ConstructorKeyword && location.parent.kind === SyntaxKind.Constructor)) { // At constructor keyword of constructor declaration
// get the signature from the declaration and write it
const functionDeclaration = <FunctionLike>location.parent;
// Use function declaration to write the signatures only if the symbol corresponding to this declaration
const locationIsSymbolDeclaration = find(symbol.declarations, declaration =>
declaration === (location.kind === SyntaxKind.ConstructorKeyword ? functionDeclaration.parent : functionDeclaration));

if (locationIsSymbolDeclaration) {
const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures();
if (!typeChecker.isImplementationOfOverload(functionDeclaration)) {
signature = typeChecker.getSignatureFromDeclaration(functionDeclaration);
}
else {
signature = allSignatures[0];
}

if (functionDeclaration.kind === SyntaxKind.Constructor) {
// show (constructor) Type(...) signature
symbolKind = ScriptElementKind.constructorImplementationElement;
addPrefixForAnyFunctionOrVar(type.symbol, symbolKind);
}
else {
// (function/method) symbol(..signature)
addPrefixForAnyFunctionOrVar(functionDeclaration.kind === SyntaxKind.CallSignature &&
!(type.symbol.flags & SymbolFlags.TypeLiteral || type.symbol.flags & SymbolFlags.ObjectLiteral) ? type.symbol : symbol, symbolKind);
}
}
else if ((isNameOfFunctionDeclaration(location) && !(symbolFlags & SymbolFlags.Accessor)) || // name of function declaration
(location.kind === SyntaxKind.ConstructorKeyword && location.parent.kind === SyntaxKind.Constructor)) { // At constructor keyword of constructor declaration
// get the signature from the declaration and write it
const functionDeclaration = <FunctionLike>location.parent;
// Use function declaration to write the signatures only if the symbol corresponding to this declaration
const locationIsSymbolDeclaration = find(symbol.declarations, declaration =>
declaration === (location.kind === SyntaxKind.ConstructorKeyword ? functionDeclaration.parent : functionDeclaration));

if (locationIsSymbolDeclaration) {
const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures();
if (!typeChecker.isImplementationOfOverload(functionDeclaration)) {
signature = typeChecker.getSignatureFromDeclaration(functionDeclaration);
}
else {
signature = allSignatures[0];
}

addSignatureDisplayParts(signature, allSignatures);
hasAddedSymbolInfo = true;
if (functionDeclaration.kind === SyntaxKind.Constructor) {
// show (constructor) Type(...) signature
symbolKind = ScriptElementKind.constructorImplementationElement;
addPrefixForAnyFunctionOrVar(type.symbol, symbolKind);
}
else {
// (function/method) symbol(..signature)
addPrefixForAnyFunctionOrVar(functionDeclaration.kind === SyntaxKind.CallSignature &&
!(type.symbol.flags & SymbolFlags.TypeLiteral || type.symbol.flags & SymbolFlags.ObjectLiteral) ? type.symbol : symbol, symbolKind);
}

addSignatureDisplayParts(signature, allSignatures);
hasAddedSymbolInfo = true;
}
}
}
Expand Down Expand Up @@ -417,7 +416,9 @@ namespace ts.SymbolDisplay {
symbolFlags & SymbolFlags.Accessor ||
symbolKind === ScriptElementKind.memberFunctionElement) {
const allSignatures = type.getNonNullableType().getCallSignatures();
addSignatureDisplayParts(allSignatures[0], allSignatures);
if (allSignatures.length) {
addSignatureDisplayParts(allSignatures[0], allSignatures);
}
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions tests/cases/fourslash/quickInfoTypeError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/// <reference path='fourslash.ts' />

////foo({
//// /**/f: function() {},
//// f() {}
////});

// The symbol indicates that this is a funciton, but the type is `any`.
// Regression test that we don't crash (by trying to get signatures from `any`).
verify.quickInfoAt("", "(method) f");

0 comments on commit 5e8e735

Please sign in to comment.