Skip to content

Commit cad98b9

Browse files
committed
Ugghhghgh
1 parent 3b9bd27 commit cad98b9

File tree

6 files changed

+68
-66
lines changed

6 files changed

+68
-66
lines changed

src/compiler/checker.ts

+41-45
Original file line numberDiff line numberDiff line change
@@ -532,26 +532,13 @@ namespace ts {
532532
if (!node) {
533533
return undefined;
534534
}
535-
const containingCall = findAncestor(node, isCallLikeExpression);
536-
const containingCallResolvedSignature = containingCall && getNodeLinks(containingCall).resolvedSignature;
537-
if (contextFlags! & ContextFlags.Completions && containingCall) {
538-
let toMarkSkip = node as Node;
539-
do {
540-
getNodeLinks(toMarkSkip).skipDirectInference = true;
541-
toMarkSkip = toMarkSkip.parent;
542-
} while (toMarkSkip && toMarkSkip !== containingCall);
543-
getNodeLinks(containingCall).resolvedSignature = undefined;
544-
}
545-
const result = getContextualType(node, contextFlags);
546-
if (contextFlags! & ContextFlags.Completions && containingCall) {
547-
let toMarkSkip = node as Node;
548-
do {
549-
getNodeLinks(toMarkSkip).skipDirectInference = undefined;
550-
toMarkSkip = toMarkSkip.parent;
551-
} while (toMarkSkip && toMarkSkip !== containingCall);
552-
getNodeLinks(containingCall).resolvedSignature = containingCallResolvedSignature;
535+
if (contextFlags! & ContextFlags.Completions) {
536+
return runWithInferenceBlockedFromSourceNode(node, /*stop*/ undefined, () => getContextualType(node, contextFlags));
553537
}
554-
return result;
538+
return getContextualType(node, contextFlags);
539+
},
540+
getContextualTypeForCompletions: (nodeIn: Expression, editingNodeIn: Node) => {
541+
return runWithInferenceBlockedFromSourceNode(editingNodeIn, editingNodeIn, () => getContextualType(nodeIn, ContextFlags.Completions));
555542
},
556543
getContextualTypeForObjectLiteralElement: nodeIn => {
557544
const node = getParseTreeNode(nodeIn, isObjectLiteralElementLike);
@@ -741,33 +728,42 @@ namespace ts {
741728
getMemberOverrideModifierStatus,
742729
};
743730

731+
function runWithInferenceBlockedFromSourceNode<T>(node: Node | undefined, stop: Node | undefined, fn: () => T): T {
732+
const containingCall = findAncestor(stop || node, isCallLikeExpression);
733+
const containingCallResolvedSignature = containingCall && getNodeLinks(containingCall).resolvedSignature;
734+
if (containingCall) {
735+
let toMarkSkip = node!;
736+
do {
737+
getNodeLinks(toMarkSkip).skipDirectInference = true;
738+
toMarkSkip = toMarkSkip.parent;
739+
} while (toMarkSkip && toMarkSkip !== (stop || containingCall));
740+
getNodeLinks(containingCall).resolvedSignature = undefined;
741+
}
742+
const result = fn();
743+
if (containingCall) {
744+
let toMarkSkip = node!;
745+
do {
746+
getNodeLinks(toMarkSkip).skipDirectInference = undefined;
747+
toMarkSkip = toMarkSkip.parent;
748+
} while (toMarkSkip && toMarkSkip !== (stop || containingCall));
749+
getNodeLinks(containingCall).resolvedSignature = containingCallResolvedSignature;
750+
}
751+
return result;
752+
}
753+
744754
function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode, argumentIndex?: number): Signature | undefined {
745-
let node = getParseTreeNode(nodeIn, isCallLikeExpression);
755+
const node = getParseTreeNode(nodeIn, isCallLikeExpression);
756+
let res;
757+
apparentArgumentCount = argumentCount;
746758
if (node && argumentIndex !== undefined) {
747-
const replacementArg = setParentRecursive(factory.createAsExpression(factory.createStringLiteral(""), factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword)), /*incremental*/ false);
748-
switch (node.kind) {
749-
case SyntaxKind.CallExpression:
750-
node = factory.updateCallExpression(node, node.expression, node.typeArguments, [
751-
...node.arguments.slice(0, argumentIndex),
752-
replacementArg,
753-
...node.arguments.slice(argumentIndex + 1),
754-
]);
755-
break;
756-
case SyntaxKind.NewExpression:
757-
node = factory.updateNewExpression(node, node.expression, node.typeArguments, [
758-
...node.arguments?.slice(0, argumentIndex) || emptyArray,
759-
replacementArg,
760-
...node.arguments?.slice(argumentIndex + 1) || emptyArray,
761-
]);
762-
break;
763-
default:
764-
Debug.failBadSyntaxKind(node);
765-
}
766-
setParent(replacementArg, node);
767-
setParent(node, nodeIn.parent);
759+
const editingArgument =
760+
tryCast(node, isCallOrNewExpression)?.arguments?.[argumentIndex] ||
761+
tryCast(node, isJsxOpeningLikeElement)?.attributes?.properties[argumentIndex];
762+
res = runWithInferenceBlockedFromSourceNode(editingArgument, /*stop*/ undefined, () => getResolvedSignature(node, candidatesOutArray, checkMode));
763+
}
764+
else {
765+
res = node ? getResolvedSignature(node, candidatesOutArray, checkMode) : undefined;
768766
}
769-
apparentArgumentCount = argumentCount;
770-
const res = node ? getResolvedSignature(node, candidatesOutArray, checkMode) : undefined;
771767
apparentArgumentCount = undefined;
772768
return res;
773769
}
@@ -22687,7 +22683,7 @@ namespace ts {
2268722683
const properties = getPropertiesOfObjectType(target);
2268822684
for (const targetProp of properties) {
2268922685
const sourceProp = getPropertyOfType(source, targetProp.escapedName);
22690-
if (sourceProp) {
22686+
if (sourceProp && !some(sourceProp.declarations, d => !!getNodeLinks(d).skipDirectInference)) {
2269122687
inferFromTypes(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp));
2269222688
}
2269322689
}
@@ -29785,7 +29781,7 @@ namespace ts {
2978529781

2978629782
for (let i = 0; i < argCount; i++) {
2978729783
const arg = args[i];
29788-
if (arg.kind !== SyntaxKind.OmittedExpression) {
29784+
if (arg.kind !== SyntaxKind.OmittedExpression && !getNodeLinks(arg).skipDirectInference) {
2978929785
const paramType = getTypeAtPosition(signature, i);
2979029786
const argType = checkExpressionWithContextualType(arg, paramType, context, checkMode);
2979129787
inferTypes(context.inferences, argType, paramType);

src/compiler/types.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -4269,6 +4269,7 @@ namespace ts {
42694269
/* @internal */ getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike): Type | undefined;
42704270
/* @internal */ getContextualTypeForArgumentAtIndex(call: CallLikeExpression, argIndex: number): Type | undefined;
42714271
/* @internal */ getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute): Type | undefined;
4272+
/* @internal */ getContextualTypeForCompletions(node: Expression, editingNode: Node): Type | undefined;
42724273
/* @internal */ isContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike): boolean;
42734274
/* @internal */ getTypeOfPropertyOfContextualType(type: Type, name: __String): Type | undefined;
42744275

@@ -4278,7 +4279,7 @@ namespace ts {
42784279
* @param argumentCount Apparent number of arguments, passed in case of a possibly incomplete call. This should come from an ArgumentListInfo. See `signatureHelp.ts`.
42794280
*/
42804281
getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[], argumentCount?: number): Signature | undefined;
4281-
/* @internal */ getResolvedSignatureForStringLiteralCompletions(node: CallExpression | NewExpression, candidatesOutArray: Signature[], argumentCount: number, argumentIndex: number): Signature | undefined;
4282+
/* @internal */ getResolvedSignatureForStringLiteralCompletions(node: CallLikeExpression, candidatesOutArray: Signature[], argumentCount: number, argumentIndex: number): Signature | undefined;
42824283
/* @internal */ getResolvedSignatureForSignatureHelp(node: CallLikeExpression, candidatesOutArray?: Signature[], argumentCount?: number): Signature | undefined;
42834284
/* @internal */ getExpandedParameters(sig: Signature): readonly (readonly Symbol[])[];
42844285
/* @internal */ hasEffectiveRestParameter(sig: Signature): boolean;

src/harness/fourslashImpl.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ namespace FourSlash {
168168
// The position of the end of the current selection, or -1 if nothing is selected
169169
public selectionEnd = -1;
170170

171-
public lastKnownMarker = "";
171+
public lastKnownMarker: string | undefined;
172172

173173
// The file that's currently 'opened'
174174
public activeFile!: FourSlashFile;
@@ -400,7 +400,7 @@ namespace FourSlash {
400400
continue;
401401
}
402402
const memo = Utils.memoize(
403-
(_version: number, _active: string, _caret: number, _selectEnd: number, _marker: string, ...args: any[]) => (ls[key] as Function)(...args),
403+
(_version: number, _active: string, _caret: number, _selectEnd: number, _marker: string | undefined, ...args: any[]) => (ls[key] as Function)(...args),
404404
(...args) => args.map(a => a && typeof a === "object" ? JSON.stringify(a) : a).join("|,|")
405405
);
406406
proxy[key] = (...args: any[]) => memo(
@@ -540,8 +540,8 @@ namespace FourSlash {
540540
}
541541

542542
private messageAtLastKnownMarker(message: string) {
543-
const locationDescription = this.lastKnownMarker ? this.lastKnownMarker : this.getLineColStringAtPosition(this.currentCaretPosition);
544-
return `At ${locationDescription}: ${message}`;
543+
const locationDescription = this.lastKnownMarker !== undefined ? this.lastKnownMarker : this.getLineColStringAtPosition(this.currentCaretPosition);
544+
return `At marker '${locationDescription}': ${message}`;
545545
}
546546

547547
private assertionMessageAtLastKnownMarker(msg: string) {
@@ -864,7 +864,7 @@ namespace FourSlash {
864864
else {
865865
for (const marker of toArray(options.marker)) {
866866
this.goToMarker(marker);
867-
this.verifyCompletionsWorker(options);
867+
this.verifyCompletionsWorker({ ...options, marker });
868868
}
869869
}
870870
}

src/services/stringCompletions.ts

+14-7
Original file line numberDiff line numberDiff line change
@@ -210,12 +210,13 @@ namespace ts.Completions.StringCompletions {
210210

211211
case SyntaxKind.CallExpression:
212212
case SyntaxKind.NewExpression:
213+
case SyntaxKind.JsxAttribute:
213214
if (!isRequireCallArgument(node) && !isImportCall(parent)) {
214-
const argumentInfo = SignatureHelp.getArgumentInfoForCompletions(node, position, sourceFile);
215+
const argumentInfo = SignatureHelp.getArgumentInfoForCompletions(parent.kind === SyntaxKind.JsxAttribute ? parent.parent : node, position, sourceFile);
215216
// Get string literal completions from specialized signatures of the target
216217
// i.e. declare function f(a: 'A');
217218
// f("/*completion position*/")
218-
return argumentInfo ? getStringLiteralCompletionsFromSignature(parent as CallExpression | NewExpression, argumentInfo, typeChecker) : fromContextualType();
219+
return argumentInfo ? getStringLiteralCompletionsFromSignature(argumentInfo.invocation, node, argumentInfo, typeChecker) : fromContextualType();
219220
}
220221
// falls through (is `require("")` or `require(""` or `import("")`)
221222

@@ -237,7 +238,7 @@ namespace ts.Completions.StringCompletions {
237238
function fromContextualType(): StringLiteralCompletion {
238239
// Get completion for string literal from string literal type
239240
// i.e. var x: "hi" | "hello" = "/*completion position*/"
240-
return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(getContextualTypeFromParent(node, typeChecker)), isNewIdentifier: false };
241+
return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(getContextualTypeFromParent(node, typeChecker, ContextFlags.Completions)), isNewIdentifier: false };
241242
}
242243
}
243244

@@ -257,15 +258,21 @@ namespace ts.Completions.StringCompletions {
257258
type !== current && isLiteralTypeNode(type) && isStringLiteral(type.literal) ? type.literal.text : undefined);
258259
}
259260

260-
function getStringLiteralCompletionsFromSignature(node: CallExpression | NewExpression, argumentInfo: SignatureHelp.ArgumentInfoForCompletions, checker: TypeChecker): StringLiteralCompletionsFromTypes {
261+
function getStringLiteralCompletionsFromSignature(call: CallLikeExpression, arg: StringLiteralLike, argumentInfo: SignatureHelp.ArgumentInfoForCompletions, checker: TypeChecker): StringLiteralCompletionsFromTypes {
261262
let isNewIdentifier = false;
262263

263264
const uniques = new Map<string, true>();
264265
const candidates: Signature[] = [];
265-
checker.getResolvedSignatureForStringLiteralCompletions(node, candidates, argumentInfo.argumentCount, argumentInfo.argumentIndex);
266+
let argumentCount = argumentInfo.argumentCount;
267+
let argumentIndex = argumentInfo.argumentIndex;
268+
if (isJsxOpeningLikeElement(call)) {
269+
argumentCount = call.attributes.properties.length;
270+
argumentIndex = findIndex(call.attributes.properties, p => tryCast(p, isJsxAttribute)?.initializer === arg);
271+
}
272+
checker.getResolvedSignatureForStringLiteralCompletions(call, candidates, argumentCount, argumentIndex);
266273
const types = flatMap(candidates, candidate => {
267-
if (!signatureHasRestParameter(candidate) && argumentInfo.argumentCount > candidate.parameters.length) return;
268-
const type = candidate.getTypeParameterAtPosition(argumentInfo.argumentIndex);
274+
if (!signatureHasRestParameter(candidate) && argumentCount > candidate.parameters.length) return;
275+
const type = candidate.getTypeParameterAtPosition(argumentIndex);
269276
isNewIdentifier = isNewIdentifier || !!(type.flags & TypeFlags.String);
270277
return getStringLiteralTypes(type, uniques);
271278
});

src/services/utilities.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -2657,21 +2657,21 @@ namespace ts {
26572657
|| isAsExpression(expression) && isObjectLiteralExpression(expression.expression);
26582658
}
26592659

2660-
export function getContextualTypeFromParent(node: Expression, checker: TypeChecker): Type | undefined {
2660+
export function getContextualTypeFromParent(node: Expression, checker: TypeChecker, contextFlags?: ContextFlags): Type | undefined {
26612661
const { parent } = node;
26622662
switch (parent.kind) {
26632663
case SyntaxKind.NewExpression:
2664-
return checker.getContextualType(parent as NewExpression);
2664+
return checker.getContextualType(parent as NewExpression, contextFlags);
26652665
case SyntaxKind.BinaryExpression: {
26662666
const { left, operatorToken, right } = parent as BinaryExpression;
26672667
return isEqualityOperatorKind(operatorToken.kind)
26682668
? checker.getTypeAtLocation(node === right ? left : right)
2669-
: checker.getContextualType(node);
2669+
: checker.getContextualType(node, contextFlags);
26702670
}
26712671
case SyntaxKind.CaseClause:
26722672
return (parent as CaseClause).expression === node ? getSwitchedType(parent as CaseClause, checker) : undefined;
26732673
default:
2674-
return checker.getContextualType(node);
2674+
return checker.getContextualType(node, contextFlags);
26752675
}
26762676
}
26772677

tests/cases/fourslash/completionsLiteralOverload.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
//// dragenter: any;
1010
//// }
1111
//// declare function addListener<K extends keyof Events>(type: K, listener: (ev: Events[K]) => any): void;
12-
//// declare function addListener(type: string, listener: (ev: any) => any): void;
1312
////
1413
//// declare function ListenerComponent<K extends keyof Events>(props: { type: K, onWhatever: (ev: Events[K]) => void }): JSX.Element;
1514
////
@@ -19,7 +18,6 @@
1918
// @Filename: /b.js
2019
//// addListener("/*js*/");
2120

22-
verify.completions({ marker: ["ts", "js"], isNewIdentifierLocation: true, exact: ["", "drag", "dragenter"] });
23-
verify.completions({ marker: "tsx", exact: ["", "drag", "dragenter"] });
21+
verify.completions({ marker: ["ts", "tsx", "js"], exact: ["", "drag", "dragenter"] });
2422
edit.insert("drag");
25-
verify.completions({ isNewIdentifierLocation: true, exact: ["", "drag", "dragenter"] });
23+
verify.completions({ marker: ["ts", "tsx", "js"], exact: ["", "drag", "dragenter"] });

0 commit comments

Comments
 (0)