Skip to content

Commit 82389a3

Browse files
authored
fix: crash in @typescript-eslint/no-misused-promises rule (#236)
* fix: crash in `@typescript-eslint/no-misused-promises` rule * Create ninety-cheetahs-remain.md
1 parent cf78036 commit 82389a3

File tree

6 files changed

+102
-35
lines changed

6 files changed

+102
-35
lines changed

Diff for: .changeset/ninety-cheetahs-remain.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte-eslint-parser": patch
3+
---
4+
5+
fix: crash in `@typescript-eslint/no-misused-promises` rule

Diff for: docs/internal-mechanism.md

+14-6
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,22 @@ Parse the following virtual script code as a script:
157157

158158
export let foo: { bar: number } | null = null
159159

160-
$: function $_reactiveStatementScopeFunction1(){console.log(foo && foo.bar);}
160+
$: function $_reactiveStatementScopeFunction1(){
161+
console.log(foo && foo.bar);
162+
}
161163

162-
$: let r = $_reactiveVariableScopeFunction2();
163-
function $_reactiveVariableScopeFunction2(){return foo && foo.bar;}
164+
$: let r =$_reactiveVariableScopeFunction2();
165+
function $_reactiveVariableScopeFunction2(){
166+
let $_tmpVar3;
167+
return ($_tmpVar3 = foo && foo.bar);
168+
}
164169

165-
$: let { bar: n } = $_reactiveVariableScopeFunction3();
166-
function $_reactiveVariableScopeFunction3(){return foo || { bar: 42 };}
167-
;function $_render4(){
170+
$: let { bar: n } =$_reactiveVariableScopeFunction4();
171+
function $_reactiveVariableScopeFunction4(){
172+
let $_tmpVar5;
173+
return ($_tmpVar5 = foo || { bar: 42 });
174+
}
175+
;function $_render6(){
168176

169177
(foo && foo.bar);
170178
}

Diff for: src/parser/typescript/analyze/index.ts

+53-29
Original file line numberDiff line numberDiff line change
@@ -231,15 +231,15 @@ function transformForDeclareReactiveVar(
231231
//
232232
// To:
233233
// $: let id = fn()
234-
// function fn () { return x + y; }
234+
// function fn () { let tmp; return (tmp = x + y); }
235235
//
236236
//
237237
// From:
238238
// $: ({id} = foo);
239239
//
240240
// To:
241241
// $: let {id} = fn()
242-
// function fn () { return foo; }
242+
// function fn () { let tmp; return (tmp = foo); }
243243

244244
/**
245245
* The opening paren tokens for
@@ -297,6 +297,7 @@ function transformForDeclareReactiveVar(
297297
}
298298

299299
const functionId = ctx.generateUniqueId("reactiveVariableScopeFunction");
300+
const tmpVarId = ctx.generateUniqueId("tmpVar");
300301
for (const token of openParens) {
301302
ctx.appendOriginal(token.range[0]);
302303
ctx.skipOriginalOffset(token.range[1] - token.range[0]);
@@ -306,7 +307,7 @@ function transformForDeclareReactiveVar(
306307
ctx.appendVirtualScript("let ");
307308
ctx.appendOriginal(eq ? eq.range[1] : expression.right.range[0]);
308309
ctx.appendVirtualScript(
309-
`${functionId}();\nfunction ${functionId}(){return (`
310+
`${functionId}();\nfunction ${functionId}(){let ${tmpVarId};return (${tmpVarId} = `
310311
);
311312
ctx.appendOriginal(expression.right.range[1]);
312313
ctx.appendVirtualScript(`)`);
@@ -347,46 +348,61 @@ function transformForDeclareReactiveVar(
347348
!fnDecl ||
348349
fnDecl.type !== "FunctionDeclaration" ||
349350
fnDecl.id.name !== functionId ||
350-
fnDecl.body.body.length !== 1 ||
351-
fnDecl.body.body[0].type !== "ReturnStatement"
351+
fnDecl.body.body.length !== 2 ||
352+
fnDecl.body.body[0].type !== "VariableDeclaration" ||
353+
fnDecl.body.body[1].type !== "ReturnStatement"
352354
) {
353355
return false;
354356
}
355-
const returnStatement = fnDecl.body.body[0];
356-
if (returnStatement.argument?.type !== expression.right.type) {
357+
const tmpVarDeclaration = fnDecl.body.body[0];
358+
if (
359+
tmpVarDeclaration.declarations.length !== 1 ||
360+
tmpVarDeclaration.declarations[0].type !== "VariableDeclarator"
361+
) {
357362
return false;
358363
}
364+
const tempVarDeclId = tmpVarDeclaration.declarations[0].id;
365+
if (
366+
tempVarDeclId.type !== "Identifier" ||
367+
tempVarDeclId.name !== tmpVarId
368+
) {
369+
return false;
370+
}
371+
const returnStatement = fnDecl.body.body[1];
372+
const assignment = returnStatement.argument;
373+
if (
374+
assignment?.type !== "AssignmentExpression" ||
375+
assignment.left.type !== "Identifier" ||
376+
assignment.right.type !== expression.right.type
377+
) {
378+
return false;
379+
}
380+
const tempLeft = assignment.left;
359381
// Remove function declaration
360382
program.body.splice(nextIndex, 1);
361383
// Restore expression statement
362-
const newExpression: TSESTree.AssignmentExpression = {
363-
type: "AssignmentExpression" as TSESTree.AssignmentExpression["type"],
364-
operator: "=",
365-
left: idDecl.id,
366-
right: returnStatement.argument,
367-
loc: {
368-
start: idDecl.id.loc.start,
369-
end: expressionCloseParen
370-
? expressionCloseParen.loc.end
371-
: returnStatement.argument.loc.end,
372-
},
373-
range: [
374-
idDecl.id.range[0],
375-
expressionCloseParen
376-
? expressionCloseParen.range[1]
377-
: returnStatement.argument.range[1],
378-
],
384+
assignment.left = idDecl.id;
385+
assignment.loc = {
386+
start: idDecl.id.loc.start,
387+
end: expressionCloseParen
388+
? expressionCloseParen.loc.end
389+
: assignment.right.loc.end,
379390
};
380-
idDecl.id.parent = newExpression;
381-
returnStatement.argument.parent = newExpression;
391+
assignment.range = [
392+
idDecl.id.range[0],
393+
expressionCloseParen
394+
? expressionCloseParen.range[1]
395+
: assignment.right.range[1],
396+
];
397+
idDecl.id.parent = assignment;
382398
const newBody: TSESTree.ExpressionStatement = {
383399
type: "ExpressionStatement" as TSESTree.ExpressionStatement["type"],
384-
expression: newExpression,
400+
expression: assignment,
385401
loc: statement.body.loc,
386402
range: statement.body.range,
387403
parent: reactiveStatement,
388404
};
389-
newExpression.parent = newBody;
405+
assignment.parent = newBody;
390406
reactiveStatement.body = newBody;
391407
// Restore statement end location
392408
reactiveStatement.range[1] = returnStatement.range[1];
@@ -401,12 +417,20 @@ function transformForDeclareReactiveVar(
401417
);
402418

403419
const scopeManager = result.scopeManager as ScopeManager;
420+
removeAllScopeAndVariableAndReference(tmpVarDeclaration, {
421+
visitorKeys: result.visitorKeys,
422+
scopeManager,
423+
});
404424
removeFunctionScope(fnDecl, scopeManager);
425+
405426
const scope = getProgramScope(scopeManager);
406427
for (const reference of getAllReferences(idDecl.id, scope)) {
407-
reference.writeExpr = newExpression.right as ESTree.Expression;
428+
reference.writeExpr = assignment.right as ESTree.Expression;
408429
}
409430

431+
removeIdentifierReference(tempLeft, scope);
432+
removeIdentifierVariable(tempVarDeclId, scope);
433+
410434
removeIdentifierReference(idDecl.init.callee, scope);
411435
removeIdentifierVariable(idDecl.id, scope);
412436
return true;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script lang="ts">
2+
$: noMisusedPromisesvar = 42;
3+
</script>
4+
5+
{noMisusedPromisesvar}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/* eslint eslint-comments/require-description: 0, @typescript-eslint/explicit-module-boundary-types: 0 */
2+
import type { Linter } from "eslint";
3+
import { BASIC_PARSER_OPTIONS } from "../../../src/parser/test-utils";
4+
import { rules } from "@typescript-eslint/eslint-plugin";
5+
export function setupLinter(linter: Linter) {
6+
linter.defineRule(
7+
"@typescript-eslint/no-misused-promises",
8+
rules["no-misused-promises"] as never
9+
);
10+
}
11+
12+
export function getConfig() {
13+
return {
14+
parser: "svelte-eslint-parser",
15+
parserOptions: BASIC_PARSER_OPTIONS,
16+
rules: {
17+
"@typescript-eslint/no-misused-promises": "error",
18+
},
19+
env: {
20+
browser: true,
21+
es2021: true,
22+
},
23+
};
24+
}

0 commit comments

Comments
 (0)