Skip to content

Commit

Permalink
Don't add completion for method if the this parameter doesn't match (
Browse files Browse the repository at this point in the history
  • Loading branch information
Andy authored Dec 18, 2017
1 parent 2efc92d commit 1562a27
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 9 deletions.
25 changes: 20 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,11 @@ namespace ts {
},
isValidPropertyAccess: (node, propertyName) => {
node = getParseTreeNode(node, isPropertyAccessOrQualifiedName);
return node ? isValidPropertyAccess(node, escapeLeadingUnderscores(propertyName)) : false;
return !!node && isValidPropertyAccess(node, escapeLeadingUnderscores(propertyName));
},
isValidPropertyAccessForCompletions: (node, type, property) => {
node = getParseTreeNode(node, isPropertyAccessExpression);
return !!node && isValidPropertyAccessForCompletions(node, type, property);
},
getSignatureFromDeclaration: declaration => {
declaration = getParseTreeNode(declaration, isFunctionLike);
Expand Down Expand Up @@ -16042,13 +16046,24 @@ namespace ts {
}

function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: __String): boolean {
const left = node.kind === SyntaxKind.PropertyAccessExpression
? (<PropertyAccessExpression>node).expression
: (<QualifiedName>node).left;

const left = node.kind === SyntaxKind.PropertyAccessExpression ? node.expression : node.left;
return isValidPropertyAccessWithType(node, left, propertyName, getWidenedType(checkExpression(left)));
}

function isValidPropertyAccessForCompletions(node: PropertyAccessExpression, type: Type, property: Symbol): boolean {
return isValidPropertyAccessWithType(node, node.expression, property.escapedName, type)
&& (!(property.flags & ts.SymbolFlags.Method) || isValidMethodAccess(property, type));
}
function isValidMethodAccess(method: Symbol, type: Type) {
const propType = getTypeOfFuncClassEnumModule(method);
const signatures = getSignaturesOfType(propType, SignatureKind.Call);
Debug.assert(signatures.length !== 0);
return signatures.some(sig => {
const thisType = getThisTypeOfSignature(sig);
return !thisType || isTypeAssignableTo(type, thisType);
});
}

function isValidPropertyAccessWithType(
node: PropertyAccessExpression | QualifiedName,
left: LeftHandSideExpression | QualifiedName,
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2785,6 +2785,8 @@ namespace ts {

getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number | undefined;
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
/** Exclude accesses to private properties or methods with a `this` parameter that `type` doesn't satisfy. */
/* @internal */ isValidPropertyAccessForCompletions(node: PropertyAccessExpression, type: Type, property: Symbol): boolean;
/** Follow all aliases to get the original symbol. */
getAliasedSymbol(symbol: Symbol): Symbol;
/** Follow a *single* alias to get the immediately aliased symbol. */
Expand Down
6 changes: 2 additions & 4 deletions src/services/completions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -918,9 +918,8 @@ namespace ts.Completions {
symbols.push(...getPropertiesForCompletion(type, typeChecker, /*isForAccess*/ true));
}
else {
// Filter private properties
for (const symbol of type.getApparentProperties()) {
if (typeChecker.isValidPropertyAccess(<PropertyAccessExpression>(node.parent), symbol.name)) {
if (typeChecker.isValidPropertyAccessForCompletions(<PropertyAccessExpression>(node.parent), type, symbol)) {
symbols.push(symbol);
}
}
Expand Down Expand Up @@ -2119,8 +2118,7 @@ namespace ts.Completions {

/**
* Gets all properties on a type, but if that type is a union of several types,
* tries to only include those types which declare properties, not methods.
* This ensures that we don't try providing completions for all the methods on e.g. Array.
* excludes array-like types or callable/constructable types.
*/
function getPropertiesForCompletion(type: Type, checker: TypeChecker, isForAccess: boolean): Symbol[] {
if (!(type.flags & TypeFlags.Union)) {
Expand Down
15 changes: 15 additions & 0 deletions tests/cases/fourslash/completionsMethodWithThisParameter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/// <reference path='fourslash.ts'/>

////class A<T> {
//// value: T; // Make the type parameter actually matter
//// ms(this: A<string>) {}
//// mo(this: A<{}>) {}
////}
////
////const s = new A<string>();
////const n = new A<number>();
////s./*s*/;
////n./*n*/;

verify.completionsAt("s", ["value", "ms", "mo"]);
verify.completionsAt("n", ["value", "mo"]);

0 comments on commit 1562a27

Please sign in to comment.