Skip to content

Commit 2c1982f

Browse files
committed
Better way to get error node
Plus add a check that errorNode is an argument to the call, not the call's expression.
1 parent e9607f1 commit 2c1982f

File tree

4 files changed

+55
-38
lines changed

4 files changed

+55
-38
lines changed

src/services/codefixes/addMissingAwait.ts

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ namespace ts.codefix {
3232
errorCodes,
3333
getCodeActions: context => {
3434
const { sourceFile, errorCode, span, cancellationToken, program } = context;
35-
const expression = getFixableErrorSpanExpression(sourceFile, errorCode, span, cancellationToken, program);
35+
const expression = getAwaitErrorSpanExpression(sourceFile, errorCode, span, cancellationToken, program);
3636
if (!expression) {
3737
return;
3838
}
@@ -48,7 +48,7 @@ namespace ts.codefix {
4848
const checker = context.program.getTypeChecker();
4949
const fixedDeclarations = new Set<number>();
5050
return codeFixAll(context, errorCodes, (t, diagnostic) => {
51-
const expression = getFixableErrorSpanExpression(sourceFile, diagnostic.code, diagnostic, cancellationToken, program);
51+
const expression = getAwaitErrorSpanExpression(sourceFile, diagnostic.code, diagnostic, cancellationToken, program);
5252
if (!expression) {
5353
return;
5454
}
@@ -59,6 +59,13 @@ namespace ts.codefix {
5959
},
6060
});
6161

62+
function getAwaitErrorSpanExpression(sourceFile: SourceFile, errorCode: number, span: TextSpan, cancellationToken: CancellationToken, program: Program) {
63+
const expression = getFixableErrorSpanExpression(sourceFile, span)
64+
return expression
65+
&& isMissingAwaitError(sourceFile, errorCode, span, cancellationToken, program)
66+
&& isInsideAwaitableBody(expression) ? expression : undefined;
67+
}
68+
6269
function getDeclarationSiteFix(context: CodeFixContext | CodeFixAllContext, expression: Expression, errorCode: number, checker: TypeChecker, trackChanges: ContextualTrackChangesFunction, fixedDeclarations?: Set<number>) {
6370
const { sourceFile, program, cancellationToken } = context;
6471
const awaitableInitializers = findAwaitableInitializers(expression, sourceFile, cancellationToken, program, checker);
@@ -95,23 +102,6 @@ namespace ts.codefix {
95102
some(relatedInformation, related => related.code === Diagnostics.Did_you_forget_to_use_await.code));
96103
}
97104

98-
function getFixableErrorSpanExpression(sourceFile: SourceFile, errorCode: number, span: TextSpan, cancellationToken: CancellationToken, program: Program): Expression | undefined {
99-
const token = getTokenAtPosition(sourceFile, span.start);
100-
// Checker has already done work to determine that await might be possible, and has attached
101-
// related info to the node, so start by finding the expression that exactly matches up
102-
// with the diagnostic range.
103-
const expression = findAncestor(token, node => {
104-
if (node.getStart(sourceFile) < span.start || node.getEnd() > textSpanEnd(span)) {
105-
return "quit";
106-
}
107-
return isExpression(node) && textSpansEqual(span, createTextSpanFromNode(node, sourceFile));
108-
}) as Expression | undefined;
109-
110-
return expression
111-
&& isMissingAwaitError(sourceFile, errorCode, span, cancellationToken, program)
112-
&& isInsideAwaitableBody(expression) ? expression : undefined;
113-
}
114-
115105
interface AwaitableInitializer {
116106
expression: Expression;
117107
declarationSymbol: Symbol;

src/services/codefixes/addOptionalPropertyUndefined.ts

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace ts.codefix {
1212
errorCodes,
1313
getCodeActions(context) {
1414
const typeChecker = context.program.getTypeChecker();
15-
const toAdd = getPropertiesToAdd(context.sourceFile, context.span.start, typeChecker);
15+
const toAdd = getPropertiesToAdd(context.sourceFile, context.span, typeChecker);
1616
if (!toAdd.length) {
1717
return undefined;
1818
}
@@ -26,7 +26,7 @@ namespace ts.codefix {
2626
const seen = new Map<number, true>();
2727
return createCombinedCodeActions(textChanges.ChangeTracker.with(context, changes => {
2828
eachDiagnostic(context, errorCodes, diag => {
29-
const toAdd = getPropertiesToAdd(diag.file, diag.start, checker);
29+
const toAdd = getPropertiesToAdd(diag.file, diag, checker);
3030
if (!toAdd.length) {
3131
return;
3232
}
@@ -44,8 +44,8 @@ namespace ts.codefix {
4444
},
4545
});
4646

47-
function getPropertiesToAdd(file: SourceFile, pos: number, checker: TypeChecker): Symbol[] {
48-
const sourceTarget = getSourceTarget(getErrorNode(file, pos), checker);
47+
function getPropertiesToAdd(file: SourceFile, span: TextSpan, checker: TypeChecker): Symbol[] {
48+
const sourceTarget = getSourceTarget(getFixableErrorSpanExpression(file, span), checker);
4949
if (!sourceTarget) {
5050
return emptyArray;
5151
}
@@ -72,20 +72,6 @@ namespace ts.codefix {
7272
}
7373
return undefined;
7474
}
75-
/**
76-
* Get the part of the incorrect assignment that is useful for type-checking
77-
* eg
78-
* this.definite = 1; ---> `this.definite`
79-
* ^^^^
80-
* definite = source ----> `definite`
81-
* ^^^^^^^^
82-
*/
83-
function getErrorNode(file: SourceFile, pos: number): MemberName | PropertyAccessExpression | undefined {
84-
const start = getTokenAtPosition(file, pos);
85-
return isPropertyAccessExpression(start.parent) && start.parent.expression === start ? start.parent
86-
: isIdentifier(start) || isPrivateIdentifier(start) ? start
87-
: undefined;
88-
}
8975

9076
/**
9177
* Find the source and target of the incorrect assignment.
@@ -101,7 +87,7 @@ namespace ts.codefix {
10187
else if (isVariableDeclaration(errorNode.parent) && errorNode.parent.initializer) {
10288
return { source: errorNode.parent.initializer, target: errorNode.parent.name };
10389
}
104-
else if (isCallExpression(errorNode.parent)) {
90+
else if (isCallExpression(errorNode.parent) && errorNode.parent.arguments.indexOf(errorNode as Expression) > -1) {
10591
const n = checker.getSymbolAtLocation(errorNode.parent.expression);
10692
if (!n?.valueDeclaration) return undefined;
10793
if (!isExpression(errorNode)) return undefined;

src/services/utilities.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3085,6 +3085,22 @@ namespace ts {
30853085
return createTextSpanFromBounds(startPosition, endPosition === undefined ? startPosition : endPosition);
30863086
}
30873087

3088+
/* @internal */
3089+
export function getFixableErrorSpanExpression(sourceFile: SourceFile, span: TextSpan): Expression | undefined {
3090+
const token = getTokenAtPosition(sourceFile, span.start);
3091+
// Checker has already done work to determine that await might be possible, and has attached
3092+
// related info to the node, so start by finding the expression that exactly matches up
3093+
// with the diagnostic range.
3094+
const expression = findAncestor(token, node => {
3095+
if (node.getStart(sourceFile) < span.start || node.getEnd() > textSpanEnd(span)) {
3096+
return "quit";
3097+
}
3098+
return isExpression(node) && textSpansEqual(span, createTextSpanFromNode(node, sourceFile));
3099+
}) as Expression | undefined;
3100+
3101+
return expression
3102+
}
3103+
30883104
/**
30893105
* If the provided value is an array, the mapping function is applied to each element; otherwise, the mapping function is applied
30903106
* to the provided value itself.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
// @strictNullChecks: true
4+
// @exactOptionalPropertyTypes: true
5+
// @Filename: fixExactOptionalUnassignableProperties6.ts
6+
// based on snapshotterInjected.ts in microsoft/playwright
7+
//// type Data = {
8+
//// p?: boolean,
9+
//// };
10+
//// declare function e(o: any): Data;
11+
//// e(101).p = undefined
12+
verify.codeFixAvailable([
13+
{ description: ts.Diagnostics.Add_undefined_to_optional_property_type.message }
14+
]);
15+
16+
verify.codeFix({
17+
description: ts.Diagnostics.Add_undefined_to_optional_property_type.message,
18+
index: 0,
19+
newFileContent:
20+
`type Data = {
21+
p?: boolean | undefined,
22+
};
23+
declare function e(o: any): Data;
24+
e(101).p = undefined`,
25+
});

0 commit comments

Comments
 (0)