Compare commits

...

1 Commits

Author SHA1 Message Date
Joe Savona
2283eb9183 [compiler] Improve more error messages
This PR uses the new diagnostic type for most of the error messages produced in our explicit validation passes (`Validation/` directory). One of the validations produced multiple errors as a hack to showing multiple related locations, which we can now consolidate into a single diagnostic.
2025-07-24 15:38:22 -07:00
41 changed files with 324 additions and 262 deletions

View File

@@ -59,7 +59,7 @@ export type CompilerDiagnosticDetail =
*/ */
{ {
kind: 'error'; kind: 'error';
loc: SourceLocation; loc: SourceLocation | null;
message: string; message: string;
}; };
@@ -100,6 +100,12 @@ export class CompilerDiagnostic {
this.options = options; this.options = options;
} }
static create(
options: Omit<CompilerDiagnosticOptions, 'details'>,
): CompilerDiagnostic {
return new CompilerDiagnostic({...options, details: []});
}
get category(): CompilerDiagnosticOptions['category'] { get category(): CompilerDiagnosticOptions['category'] {
return this.options.category; return this.options.category;
} }
@@ -113,6 +119,11 @@ export class CompilerDiagnostic {
return this.options.suggestions; return this.options.suggestions;
} }
withDetail(detail: CompilerDiagnosticDetail): CompilerDiagnostic {
this.options.details.push(detail);
return this;
}
primaryLocation(): SourceLocation | null { primaryLocation(): SourceLocation | null {
return this.options.details.filter(d => d.kind === 'error')[0]?.loc ?? null; return this.options.details.filter(d => d.kind === 'error')[0]?.loc ?? null;
} }
@@ -127,7 +138,7 @@ export class CompilerDiagnostic {
switch (detail.kind) { switch (detail.kind) {
case 'error': { case 'error': {
const loc = detail.loc; const loc = detail.loc;
if (typeof loc === 'symbol') { if (loc == null || typeof loc === 'symbol') {
continue; continue;
} }
let codeFrame: string; let codeFrame: string;

View File

@@ -9,6 +9,7 @@ import {NodePath, Scope} from '@babel/traverse';
import * as t from '@babel/types'; import * as t from '@babel/types';
import invariant from 'invariant'; import invariant from 'invariant';
import { import {
CompilerDiagnostic,
CompilerError, CompilerError,
CompilerSuggestionOperation, CompilerSuggestionOperation,
ErrorSeverity, ErrorSeverity,
@@ -104,12 +105,18 @@ export function lower(
if (param.isIdentifier()) { if (param.isIdentifier()) {
const binding = builder.resolveIdentifier(param); const binding = builder.resolveIdentifier(param);
if (binding.kind !== 'Identifier') { if (binding.kind !== 'Identifier') {
builder.errors.push({ builder.errors.pushDiagnostic(
reason: `(BuildHIR::lower) Could not find binding for param \`${param.node.name}\``, CompilerDiagnostic.create({
severity: ErrorSeverity.Invariant, category: 'Could not find binding',
loc: param.node.loc ?? null, description: `[BuildHIR] Could not find binding for param \`${param.node.name}\``,
suggestions: null, severity: ErrorSeverity.Invariant,
}); suggestions: null,
}).withDetail({
kind: 'error',
loc: param.node.loc ?? null,
message: 'Could not find binding',
}),
);
return; return;
} }
const place: Place = { const place: Place = {
@@ -163,12 +170,18 @@ export function lower(
'Assignment', 'Assignment',
); );
} else { } else {
builder.errors.push({ builder.errors.pushDiagnostic(
reason: `(BuildHIR::lower) Handle ${param.node.type} params`, CompilerDiagnostic.create({
severity: ErrorSeverity.Todo, category: `Handle ${param.node.type} parameters`,
loc: param.node.loc ?? null, description: `[BuildHIR] Add support for ${param.node.type} parameters`,
suggestions: null, severity: ErrorSeverity.Todo,
}); suggestions: null,
}).withDetail({
kind: 'error',
loc: param.node.loc ?? null,
message: 'Unsupported parameter type',
}),
);
} }
}); });
@@ -188,13 +201,18 @@ export function lower(
lowerStatement(builder, body); lowerStatement(builder, body);
directives = body.get('directives').map(d => d.node.value.value); directives = body.get('directives').map(d => d.node.value.value);
} else { } else {
builder.errors.push({ builder.errors.pushDiagnostic(
severity: ErrorSeverity.InvalidJS, CompilerDiagnostic.create({
reason: `Unexpected function body kind`, severity: ErrorSeverity.InvalidJS,
description: `Expected function body to be an expression or a block statement, got \`${body.type}\``, category: `Unexpected function body kind`,
loc: body.node.loc ?? null, description: `Expected function body to be an expression or a block statement, got \`${body.type}\``,
suggestions: null, suggestions: null,
}); }).withDetail({
kind: 'error',
loc: body.node.loc ?? null,
message: 'Expected a block statement or expression',
}),
);
} }
if (builder.errors.hasErrors()) { if (builder.errors.hasErrors()) {

View File

@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import {CompilerError, Effect} from '..'; import {CompilerDiagnostic, CompilerError, Effect, ErrorSeverity} from '..';
import {HIRFunction, IdentifierId, Place} from '../HIR'; import {HIRFunction, IdentifierId, Place} from '../HIR';
import { import {
eachInstructionLValue, eachInstructionLValue,
@@ -28,16 +28,19 @@ export function validateLocalsNotReassignedAfterRender(fn: HIRFunction): void {
false, false,
); );
if (reassignment !== null) { if (reassignment !== null) {
CompilerError.throwInvalidReact({ const errors = new CompilerError();
reason: errors.pushDiagnostic(
'Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead', CompilerDiagnostic.create({
description: severity: ErrorSeverity.InvalidReact,
reassignment.identifier.name !== null && category: 'Cannot reassign a variable after render completes',
reassignment.identifier.name.kind === 'named' description: `Reassigning ${reassignment.identifier.name != null && reassignment.identifier.name.kind === 'named' ? `variable \`${reassignment.identifier.name.value}\`` : 'a variable'} after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead`,
? `Variable \`${reassignment.identifier.name.value}\` cannot be reassigned after render` }).withDetail({
: '', kind: 'error',
loc: reassignment.loc, loc: reassignment.loc,
}); message: 'Cannot reassign variable after render completes',
}),
);
throw errors;
} }
} }

View File

@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import {CompilerError, Effect, ErrorSeverity} from '..'; import {CompilerDiagnostic, CompilerError, Effect, ErrorSeverity} from '..';
import { import {
FunctionEffect, FunctionEffect,
HIRFunction, HIRFunction,
@@ -57,16 +57,24 @@ export function validateNoFreezingKnownMutableFunctions(
if (operand.effect === Effect.Freeze) { if (operand.effect === Effect.Freeze) {
const effect = contextMutationEffects.get(operand.identifier.id); const effect = contextMutationEffects.get(operand.identifier.id);
if (effect != null) { if (effect != null) {
errors.push({ errors.pushDiagnostic(
reason: `This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead`, CompilerDiagnostic.create({
loc: operand.loc, severity: ErrorSeverity.InvalidReact,
severity: ErrorSeverity.InvalidReact, category: 'Cannot modify local variables after render completes',
}); description: `This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead`,
errors.push({ })
reason: `The function modifies a local variable here`, .withDetail({
loc: effect.loc, kind: 'error',
severity: ErrorSeverity.InvalidReact, loc: operand.loc,
}); message:
'This function may (indirectly) reassign or modify local variables after render',
})
.withDetail({
kind: 'error',
loc: effect.loc,
message: 'This modifies a local variable',
}),
);
} }
} }
} }

View File

@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import {CompilerError, ErrorSeverity} from '..'; import {CompilerDiagnostic, CompilerError, ErrorSeverity} from '..';
import {HIRFunction} from '../HIR'; import {HIRFunction} from '../HIR';
import {getFunctionCallSignature} from '../Inference/InferReferenceEffects'; import {getFunctionCallSignature} from '../Inference/InferReferenceEffects';
import {Result} from '../Utils/Result'; import {Result} from '../Utils/Result';
@@ -34,17 +34,22 @@ export function validateNoImpureFunctionsInRender(
callee.identifier.type, callee.identifier.type,
); );
if (signature != null && signature.impure === true) { if (signature != null && signature.impure === true) {
errors.push({ errors.pushDiagnostic(
reason: CompilerDiagnostic.create({
'Calling an impure function can produce unstable results. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent)', category: 'Cannot call impure function during render',
description: description:
signature.canonicalName != null (signature.canonicalName != null
? `\`${signature.canonicalName}\` is an impure function whose results may change on every call` ? `\`${signature.canonicalName}\` is an impure function. `
: null, : '') +
severity: ErrorSeverity.InvalidReact, 'Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent)',
loc: callee.loc, severity: ErrorSeverity.InvalidReact,
suggestions: null, suggestions: null,
}); }).withDetail({
kind: 'error',
loc: callee.loc,
message: 'Cannot call impure function',
}),
);
} }
} }
} }

View File

@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import {CompilerError, ErrorSeverity} from '..'; import {CompilerDiagnostic, CompilerError, ErrorSeverity} from '..';
import {BlockId, HIRFunction} from '../HIR'; import {BlockId, HIRFunction} from '../HIR';
import {Result} from '../Utils/Result'; import {Result} from '../Utils/Result';
import {retainWhere} from '../Utils/utils'; import {retainWhere} from '../Utils/utils';
@@ -34,11 +34,17 @@ export function validateNoJSXInTryStatement(
switch (value.kind) { switch (value.kind) {
case 'JsxExpression': case 'JsxExpression':
case 'JsxFragment': { case 'JsxFragment': {
errors.push({ errors.pushDiagnostic(
severity: ErrorSeverity.InvalidReact, CompilerDiagnostic.create({
reason: `Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)`, severity: ErrorSeverity.InvalidReact,
loc: value.loc, category: 'Avoid constructing JSX within try/catch',
}); description: `React does not immediately render components when JSX is rendered, so any errors from this component will not be caught by the try/catch. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)`,
}).withDetail({
kind: 'error',
loc: value.loc,
message: 'Avoid constructing JSX within try/catch',
}),
);
break; break;
} }
} }

View File

@@ -5,7 +5,11 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import {CompilerError, ErrorSeverity} from '../CompilerError'; import {
CompilerDiagnostic,
CompilerError,
ErrorSeverity,
} from '../CompilerError';
import { import {
HIRFunction, HIRFunction,
IdentifierId, IdentifierId,
@@ -90,14 +94,21 @@ export function validateNoSetStateInEffects(
if (arg !== undefined && arg.kind === 'Identifier') { if (arg !== undefined && arg.kind === 'Identifier') {
const setState = setStateFunctions.get(arg.identifier.id); const setState = setStateFunctions.get(arg.identifier.id);
if (setState !== undefined) { if (setState !== undefined) {
errors.push({ errors.pushDiagnostic(
reason: CompilerDiagnostic.create({
'Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)', category:
description: null, 'Calling setState within an effect can trigger cascading renders',
severity: ErrorSeverity.InvalidReact, description:
loc: setState.loc, 'Calling setState directly within a useEffect causes cascading renders that can hurt performance, and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)',
suggestions: null, severity: ErrorSeverity.InvalidReact,
}); suggestions: null,
}).withDetail({
kind: 'error',
loc: setState.loc,
message:
'Avoid calling setState() in the top-level of an effect',
}),
);
} }
} }
} }

View File

@@ -5,7 +5,11 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import {CompilerError, ErrorSeverity} from '../CompilerError'; import {
CompilerDiagnostic,
CompilerError,
ErrorSeverity,
} from '../CompilerError';
import {HIRFunction, IdentifierId, isSetStateType} from '../HIR'; import {HIRFunction, IdentifierId, isSetStateType} from '../HIR';
import {computeUnconditionalBlocks} from '../HIR/ComputeUnconditionalBlocks'; import {computeUnconditionalBlocks} from '../HIR/ComputeUnconditionalBlocks';
import {eachInstructionValueOperand} from '../HIR/visitors'; import {eachInstructionValueOperand} from '../HIR/visitors';
@@ -122,23 +126,35 @@ function validateNoSetStateInRenderImpl(
unconditionalSetStateFunctions.has(callee.identifier.id) unconditionalSetStateFunctions.has(callee.identifier.id)
) { ) {
if (activeManualMemoId !== null) { if (activeManualMemoId !== null) {
errors.push({ errors.pushDiagnostic(
reason: CompilerDiagnostic.create({
'Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState)', category:
description: null, 'Calling setState from useMemo may trigger an infinite loop',
severity: ErrorSeverity.InvalidReact, description:
loc: callee.loc, 'Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState)',
suggestions: null, severity: ErrorSeverity.InvalidReact,
}); suggestions: null,
}).withDetail({
kind: 'error',
loc: callee.loc,
message: 'Found setState() within useMemo()',
}),
);
} else if (unconditionalBlocks.has(block.id)) { } else if (unconditionalBlocks.has(block.id)) {
errors.push({ errors.pushDiagnostic(
reason: CompilerDiagnostic.create({
'This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState)', category:
description: null, 'Calling setState during render may trigger an infinite loop',
severity: ErrorSeverity.InvalidReact, description:
loc: callee.loc, 'Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState)',
suggestions: null, severity: ErrorSeverity.InvalidReact,
}); suggestions: null,
}).withDetail({
kind: 'error',
loc: callee.loc,
message: 'Found setState() within useMemo()',
}),
);
} }
} }
break; break;

View File

@@ -5,7 +5,11 @@
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/ */
import {CompilerError, ErrorSeverity} from '..'; import {
CompilerDiagnostic,
CompilerError,
ErrorSeverity,
} from '../CompilerError';
import {FunctionExpression, HIRFunction, IdentifierId} from '../HIR'; import {FunctionExpression, HIRFunction, IdentifierId} from '../HIR';
import {Result} from '../Utils/Result'; import {Result} from '../Utils/Result';
@@ -63,24 +67,41 @@ export function validateUseMemo(fn: HIRFunction): Result<void, CompilerError> {
} }
if (body.loweredFunc.func.params.length > 0) { if (body.loweredFunc.func.params.length > 0) {
errors.push({ const firstParam = body.loweredFunc.func.params[0];
severity: ErrorSeverity.InvalidReact, const loc =
reason: 'useMemo callbacks may not accept any arguments', firstParam.kind === 'Identifier'
description: null, ? firstParam.loc
loc: body.loc, : firstParam.place.loc;
suggestions: null, errors.pushDiagnostic(
}); CompilerDiagnostic.create({
severity: ErrorSeverity.InvalidReact,
category: 'useMemo() callbacks may not accept parameters',
description:
'useMemo() callbacks are called by React to cache calculations across re-renders. They should not take parameters. Instead, directly reference the props, state, or local variables needed for the computation.',
suggestions: null,
}).withDetail({
kind: 'error',
loc,
message: '',
}),
);
} }
if (body.loweredFunc.func.async || body.loweredFunc.func.generator) { if (body.loweredFunc.func.async || body.loweredFunc.func.generator) {
errors.push({ errors.pushDiagnostic(
severity: ErrorSeverity.InvalidReact, CompilerDiagnostic.create({
reason: severity: ErrorSeverity.InvalidReact,
'useMemo callbacks may not be async or generator functions', category:
description: null, 'useMemo callbacks may not be async or generator functions',
loc: body.loc, description:
suggestions: null, 'useMemo() callbacks are called once and must synchronously return a value',
}); suggestions: null,
}).withDetail({
kind: 'error',
loc: body.loc,
message: 'Async and generator functions are not supported',
}),
);
} }
break; break;

View File

@@ -36,8 +36,10 @@ function Component() {
## Error ## Error
``` ```
Found 2 errors: Found 1 error:
Error: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead Error: Cannot modify local variables after render completes
This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead
error.bug-old-inference-false-positive-ref-validation-in-use-effect.ts:20:12 error.bug-old-inference-false-positive-ref-validation-in-use-effect.ts:20:12
18 | ); 18 | );
@@ -51,24 +53,19 @@ error.bug-old-inference-false-positive-ref-validation-in-use-effect.ts:20:12
> 23 | } > 23 | }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 24 | }, [update]); > 24 | }, [update]);
| ^^^^ This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead | ^^^^ This function may (indirectly) reassign or modify local variables after render
25 | 25 |
26 | return 'ok'; 26 | return 'ok';
27 | } 27 | }
Error: The function modifies a local variable here
error.bug-old-inference-false-positive-ref-validation-in-use-effect.ts:14:6 error.bug-old-inference-false-positive-ref-validation-in-use-effect.ts:14:6
12 | ...partialParams, 12 | ...partialParams,
13 | }; 13 | };
> 14 | nextParams.param = 'value'; > 14 | nextParams.param = 'value';
| ^^^^^^^^^^ The function modifies a local variable here | ^^^^^^^^^^ This modifies a local variable
15 | console.log(nextParams); 15 | console.log(nextParams);
16 | }, 16 | },
17 | [params] 17 | [params]
``` ```

View File

@@ -29,20 +29,18 @@ export const FIXTURE_ENTRYPOINT = {
``` ```
Found 1 error: Found 1 error:
Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead Error: Cannot reassign a variable after render completes
Variable `x` cannot be reassigned after render. Reassigning variable `x` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead
error.context-variable-only-chained-assign.ts:10:19 error.context-variable-only-chained-assign.ts:10:19
8 | }; 8 | };
9 | const fn2 = () => { 9 | const fn2 = () => {
> 10 | const copy2 = (x = 4); > 10 | const copy2 = (x = 4);
| ^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead | ^ Cannot reassign variable after render completes
11 | return [invoke(fn1), copy2, identity(copy2)]; 11 | return [invoke(fn1), copy2, identity(copy2)];
12 | }; 12 | };
13 | return invoke(fn2); 13 | return invoke(fn2);
``` ```

View File

@@ -18,20 +18,18 @@ function Component() {
``` ```
Found 1 error: Found 1 error:
Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead Error: Cannot reassign a variable after render completes
Variable `x` cannot be reassigned after render. Reassigning variable `x` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead
error.declare-reassign-variable-in-function-declaration.ts:4:4 error.declare-reassign-variable-in-function-declaration.ts:4:4
2 | let x = null; 2 | let x = null;
3 | function foo() { 3 | function foo() {
> 4 | x = 9; > 4 | x = 9;
| ^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead | ^ Cannot reassign variable after render completes
5 | } 5 | }
6 | const y = bar(foo); 6 | const y = bar(foo);
7 | return <Child y={y} />; 7 | return <Child y={y} />;
``` ```

View File

@@ -16,20 +16,18 @@ function Component() {
``` ```
Found 1 error: Found 1 error:
Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead Error: Cannot reassign a variable after render completes
Variable `callback` cannot be reassigned after render. Reassigning variable `callback` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead
error.function-expression-references-variable-its-assigned-to.ts:3:4 error.function-expression-references-variable-its-assigned-to.ts:3:4
1 | function Component() { 1 | function Component() {
2 | let callback = () => { 2 | let callback = () => {
> 3 | callback = null; > 3 | callback = null;
| ^^^^^^^^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead | ^^^^^^^^ Cannot reassign variable after render completes
4 | }; 4 | };
5 | return <div onClick={callback} />; 5 | return <div onClick={callback} />;
6 | } 6 | }
``` ```

View File

@@ -18,6 +18,8 @@ function component(a, b) {
Found 1 error: Found 1 error:
Error: useMemo callbacks may not be async or generator functions Error: useMemo callbacks may not be async or generator functions
useMemo() callbacks are called once and must synchronously return a value
error.invalid-ReactUseMemo-async-callback.ts:2:24 error.invalid-ReactUseMemo-async-callback.ts:2:24
1 | function component(a, b) { 1 | function component(a, b) {
> 2 | let x = React.useMemo(async () => { > 2 | let x = React.useMemo(async () => {
@@ -25,12 +27,10 @@ error.invalid-ReactUseMemo-async-callback.ts:2:24
> 3 | await a; > 3 | await a;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
> 4 | }, []); > 4 | }, []);
| ^^^^ useMemo callbacks may not be async or generator functions | ^^^^ Async and generator functions are not supported
5 | return x; 5 | return x;
6 | } 6 | }
7 | 7 |
``` ```

View File

@@ -23,30 +23,30 @@ function Component({item, cond}) {
``` ```
Found 2 errors: Found 2 errors:
Error: Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) Error: Calling setState from useMemo may trigger an infinite loop
Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState)
error.invalid-conditional-setState-in-useMemo.ts:7:6 error.invalid-conditional-setState-in-useMemo.ts:7:6
5 | useMemo(() => { 5 | useMemo(() => {
6 | if (cond) { 6 | if (cond) {
> 7 | setPrevItem(item); > 7 | setPrevItem(item);
| ^^^^^^^^^^^ Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) | ^^^^^^^^^^^ Found setState() within useMemo()
8 | setState(0); 8 | setState(0);
9 | } 9 | }
10 | }, [cond, key, init]); 10 | }, [cond, key, init]);
Error: Calling setState from useMemo may trigger an infinite loop
Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState)
Error: Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState)
error.invalid-conditional-setState-in-useMemo.ts:8:6 error.invalid-conditional-setState-in-useMemo.ts:8:6
6 | if (cond) { 6 | if (cond) {
7 | setPrevItem(item); 7 | setPrevItem(item);
> 8 | setState(0); > 8 | setState(0);
| ^^^^^^^^ Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) | ^^^^^^^^ Found setState() within useMemo()
9 | } 9 | }
10 | }, [cond, key, init]); 10 | }, [cond, key, init]);
11 | 11 |
``` ```

View File

@@ -17,8 +17,10 @@ function useFoo() {
## Error ## Error
``` ```
Found 2 errors: Found 1 error:
Error: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead Error: Cannot modify local variables after render completes
This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead
error.invalid-hook-function-argument-mutates-local-variable.ts:5:10 error.invalid-hook-function-argument-mutates-local-variable.ts:5:10
3 | function useFoo() { 3 | function useFoo() {
@@ -28,23 +30,18 @@ error.invalid-hook-function-argument-mutates-local-variable.ts:5:10
> 6 | cache.set('key', 'value'); > 6 | cache.set('key', 'value');
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 7 | }); > 7 | });
| ^^^^ This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead | ^^^^ This function may (indirectly) reassign or modify local variables after render
8 | } 8 | }
9 | 9 |
Error: The function modifies a local variable here
error.invalid-hook-function-argument-mutates-local-variable.ts:6:4 error.invalid-hook-function-argument-mutates-local-variable.ts:6:4
4 | const cache = new Map(); 4 | const cache = new Map();
5 | useHook(() => { 5 | useHook(() => {
> 6 | cache.set('key', 'value'); > 6 | cache.set('key', 'value');
| ^^^^^ The function modifies a local variable here | ^^^^^ This modifies a local variable
7 | }); 7 | });
8 | } 8 | }
9 | 9 |
``` ```

View File

@@ -47,20 +47,18 @@ function Component() {
``` ```
Found 1 error: Found 1 error:
Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead Error: Cannot reassign a variable after render completes
Variable `local` cannot be reassigned after render. Reassigning variable `local` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead
error.invalid-nested-function-reassign-local-variable-in-effect.ts:7:6 error.invalid-nested-function-reassign-local-variable-in-effect.ts:7:6
5 | // Create the reassignment function inside another function, then return it 5 | // Create the reassignment function inside another function, then return it
6 | const reassignLocal = newValue => { 6 | const reassignLocal = newValue => {
> 7 | local = newValue; > 7 | local = newValue;
| ^^^^^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead | ^^^^^ Cannot reassign variable after render completes
8 | }; 8 | };
9 | return reassignLocal; 9 | return reassignLocal;
10 | }; 10 | };
``` ```

View File

@@ -17,30 +17,27 @@ function Component() {
## Error ## Error
``` ```
Found 2 errors: Found 1 error:
Error: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead Error: Cannot modify local variables after render completes
This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead
error.invalid-pass-mutable-function-as-prop.ts:7:18 error.invalid-pass-mutable-function-as-prop.ts:7:18
5 | cache.set('key', 'value'); 5 | cache.set('key', 'value');
6 | }; 6 | };
> 7 | return <Foo fn={fn} />; > 7 | return <Foo fn={fn} />;
| ^^ This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead | ^^ This function may (indirectly) reassign or modify local variables after render
8 | } 8 | }
9 | 9 |
Error: The function modifies a local variable here
error.invalid-pass-mutable-function-as-prop.ts:5:4 error.invalid-pass-mutable-function-as-prop.ts:5:4
3 | const cache = new Map(); 3 | const cache = new Map();
4 | const fn = () => { 4 | const fn = () => {
> 5 | cache.set('key', 'value'); > 5 | cache.set('key', 'value');
| ^^^^^ The function modifies a local variable here | ^^^^^ This modifies a local variable
6 | }; 6 | };
7 | return <Foo fn={fn} />; 7 | return <Foo fn={fn} />;
8 | } 8 | }
``` ```

View File

@@ -16,20 +16,18 @@ function useFoo() {
``` ```
Found 1 error: Found 1 error:
Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead Error: Cannot reassign a variable after render completes
Variable `x` cannot be reassigned after render. Reassigning variable `x` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead
error.invalid-reassign-local-in-hook-return-value.ts:4:4 error.invalid-reassign-local-in-hook-return-value.ts:4:4
2 | let x = 0; 2 | let x = 0;
3 | return value => { 3 | return value => {
> 4 | x = value; > 4 | x = value;
| ^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead | ^ Cannot reassign variable after render completes
5 | }; 5 | };
6 | } 6 | }
7 | 7 |
``` ```

View File

@@ -48,20 +48,18 @@ function Component() {
``` ```
Found 1 error: Found 1 error:
Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead Error: Cannot reassign a variable after render completes
Variable `local` cannot be reassigned after render. Reassigning variable `local` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead
error.invalid-reassign-local-variable-in-effect.ts:7:4 error.invalid-reassign-local-variable-in-effect.ts:7:4
5 | 5 |
6 | const reassignLocal = newValue => { 6 | const reassignLocal = newValue => {
> 7 | local = newValue; > 7 | local = newValue;
| ^^^^^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead | ^^^^^ Cannot reassign variable after render completes
8 | }; 8 | };
9 | 9 |
10 | const onMount = newValue => { 10 | const onMount = newValue => {
``` ```

View File

@@ -49,20 +49,18 @@ function Component() {
``` ```
Found 1 error: Found 1 error:
Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead Error: Cannot reassign a variable after render completes
Variable `local` cannot be reassigned after render. Reassigning variable `local` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead
error.invalid-reassign-local-variable-in-hook-argument.ts:8:4 error.invalid-reassign-local-variable-in-hook-argument.ts:8:4
6 | 6 |
7 | const reassignLocal = newValue => { 7 | const reassignLocal = newValue => {
> 8 | local = newValue; > 8 | local = newValue;
| ^^^^^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead | ^^^^^ Cannot reassign variable after render completes
9 | }; 9 | };
10 | 10 |
11 | const callback = newValue => { 11 | const callback = newValue => {
``` ```

View File

@@ -42,20 +42,18 @@ function Component() {
``` ```
Found 1 error: Found 1 error:
Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead Error: Cannot reassign a variable after render completes
Variable `local` cannot be reassigned after render. Reassigning variable `local` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead
error.invalid-reassign-local-variable-in-jsx-callback.ts:5:4 error.invalid-reassign-local-variable-in-jsx-callback.ts:5:4
3 | 3 |
4 | const reassignLocal = newValue => { 4 | const reassignLocal = newValue => {
> 5 | local = newValue; > 5 | local = newValue;
| ^^^^^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead | ^^^^^ Cannot reassign variable after render completes
6 | }; 6 | };
7 | 7 |
8 | const onClick = newValue => { 8 | const onClick = newValue => {
``` ```

View File

@@ -19,8 +19,10 @@ function useFoo() {
## Error ## Error
``` ```
Found 2 errors: Found 1 error:
Error: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead Error: Cannot modify local variables after render completes
This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead
error.invalid-return-mutable-function-from-hook.ts:7:9 error.invalid-return-mutable-function-from-hook.ts:7:9
5 | useHook(); // for inference to kick in 5 | useHook(); // for inference to kick in
@@ -30,23 +32,18 @@ error.invalid-return-mutable-function-from-hook.ts:7:9
> 8 | cache.set('key', 'value'); > 8 | cache.set('key', 'value');
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 9 | }; > 9 | };
| ^^^^ This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead | ^^^^ This function may (indirectly) reassign or modify local variables after render
10 | } 10 | }
11 | 11 |
Error: The function modifies a local variable here
error.invalid-return-mutable-function-from-hook.ts:8:4 error.invalid-return-mutable-function-from-hook.ts:8:4
6 | const cache = new Map(); 6 | const cache = new Map();
7 | return () => { 7 | return () => {
> 8 | cache.set('key', 'value'); > 8 | cache.set('key', 'value');
| ^^^^^ The function modifies a local variable here | ^^^^^ This modifies a local variable
9 | }; 9 | };
10 | } 10 | }
11 | 11 |
``` ```

View File

@@ -27,18 +27,18 @@ function useKeyedState({key, init}) {
``` ```
Found 1 error: Found 1 error:
Error: Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) Error: Calling setState from useMemo may trigger an infinite loop
Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState)
error.invalid-setState-in-useMemo-indirect-useCallback.ts:13:4 error.invalid-setState-in-useMemo-indirect-useCallback.ts:13:4
11 | 11 |
12 | useMemo(() => { 12 | useMemo(() => {
> 13 | fn(); > 13 | fn();
| ^^ Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) | ^^ Found setState() within useMemo()
14 | }, [key, init]); 14 | }, [key, init]);
15 | 15 |
16 | return state; 16 | return state;
``` ```

View File

@@ -21,30 +21,30 @@ function useKeyedState({key, init}) {
``` ```
Found 2 errors: Found 2 errors:
Error: Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) Error: Calling setState from useMemo may trigger an infinite loop
Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState)
error.invalid-setState-in-useMemo.ts:6:4 error.invalid-setState-in-useMemo.ts:6:4
4 | 4 |
5 | useMemo(() => { 5 | useMemo(() => {
> 6 | setPrevKey(key); > 6 | setPrevKey(key);
| ^^^^^^^^^^ Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) | ^^^^^^^^^^ Found setState() within useMemo()
7 | setState(init); 7 | setState(init);
8 | }, [key, init]); 8 | }, [key, init]);
9 | 9 |
Error: Calling setState from useMemo may trigger an infinite loop
Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState)
Error: Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState)
error.invalid-setState-in-useMemo.ts:7:4 error.invalid-setState-in-useMemo.ts:7:4
5 | useMemo(() => { 5 | useMemo(() => {
6 | setPrevKey(key); 6 | setPrevKey(key);
> 7 | setState(init); > 7 | setState(init);
| ^^^^^^^^ Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) | ^^^^^^^^ Found setState() within useMemo()
8 | }, [key, init]); 8 | }, [key, init]);
9 | 9 |
10 | return state; 10 | return state;
``` ```

View File

@@ -47,8 +47,10 @@ hook useMemoMap<TInput: interface {}, TOutput>(
## Error ## Error
``` ```
Found 2 errors: Found 1 error:
Error: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead Error: Cannot modify local variables after render completes
This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead
undefined:21:9 undefined:21:9
19 | map: TInput => TOutput 19 | map: TInput => TOutput
@@ -86,23 +88,18 @@ undefined:21:9
> 36 | }; > 36 | };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 37 | }, [map]); > 37 | }, [map]);
| ^^^^^^^^^^^^ This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead | ^^^^^^^^^^^^ This function may (indirectly) reassign or modify local variables after render
38 | } 38 | }
39 | 39 |
Error: The function modifies a local variable here
undefined:33:8 undefined:33:8
31 | if (output == null) { 31 | if (output == null) {
32 | output = map(input); 32 | output = map(input);
> 33 | cache.set(input, output); > 33 | cache.set(input, output);
| ^^^^^ The function modifies a local variable here | ^^^^^ This modifies a local variable
34 | } 34 | }
35 | return output; 35 | return output;
36 | }; 36 | };
``` ```

View File

@@ -20,30 +20,30 @@ function Component(props) {
``` ```
Found 2 errors: Found 2 errors:
Error: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) Error: Calling setState during render may trigger an infinite loop
Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState)
error.invalid-unconditional-set-state-in-render.ts:6:2 error.invalid-unconditional-set-state-in-render.ts:6:2
4 | const aliased = setX; 4 | const aliased = setX;
5 | 5 |
> 6 | setX(1); > 6 | setX(1);
| ^^^^ This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) | ^^^^ Found setState() within useMemo()
7 | aliased(2); 7 | aliased(2);
8 | 8 |
9 | return x; 9 | return x;
Error: Calling setState during render may trigger an infinite loop
Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState)
Error: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState)
error.invalid-unconditional-set-state-in-render.ts:7:2 error.invalid-unconditional-set-state-in-render.ts:7:2
5 | 5 |
6 | setX(1); 6 | setX(1);
> 7 | aliased(2); > 7 | aliased(2);
| ^^^^^^^ This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) | ^^^^^^^ Found setState() within useMemo()
8 | 8 |
9 | return x; 9 | return x;
10 | } 10 | }
``` ```

View File

@@ -18,6 +18,8 @@ function component(a, b) {
Found 1 error: Found 1 error:
Error: useMemo callbacks may not be async or generator functions Error: useMemo callbacks may not be async or generator functions
useMemo() callbacks are called once and must synchronously return a value
error.invalid-useMemo-async-callback.ts:2:18 error.invalid-useMemo-async-callback.ts:2:18
1 | function component(a, b) { 1 | function component(a, b) {
> 2 | let x = useMemo(async () => { > 2 | let x = useMemo(async () => {
@@ -25,12 +27,10 @@ error.invalid-useMemo-async-callback.ts:2:18
> 3 | await a; > 3 | await a;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
> 4 | }, []); > 4 | }, []);
| ^^^^ useMemo callbacks may not be async or generator functions | ^^^^ Async and generator functions are not supported
5 | return x; 5 | return x;
6 | } 6 | }
7 | 7 |
``` ```

View File

@@ -14,17 +14,17 @@ function component(a, b) {
``` ```
Found 1 error: Found 1 error:
Error: useMemo callbacks may not accept any arguments Error: useMemo() callbacks may not accept parameters
useMemo() callbacks are called by React to cache calculations across re-renders. They should not take parameters. Instead, directly reference the props, state, or local variables needed for the computation.
error.invalid-useMemo-callback-args.ts:2:18 error.invalid-useMemo-callback-args.ts:2:18
1 | function component(a, b) { 1 | function component(a, b) {
> 2 | let x = useMemo(c => a, []); > 2 | let x = useMemo(c => a, []);
| ^^^^^^ useMemo callbacks may not accept any arguments | ^
3 | return x; 3 | return x;
4 | } 4 | }
5 | 5 |
``` ```

View File

@@ -33,20 +33,18 @@ export const FIXTURE_ENTRYPOINT = {
``` ```
Found 1 error: Found 1 error:
Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead Error: Cannot reassign a variable after render completes
Variable `a` cannot be reassigned after render. Reassigning variable `a` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead
error.mutable-range-shared-inner-outer-function.ts:8:6 error.mutable-range-shared-inner-outer-function.ts:8:6
6 | const f = () => { 6 | const f = () => {
7 | if (cond) { 7 | if (cond) {
> 8 | a = {}; > 8 | a = {};
| ^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead | ^ Cannot reassign variable after render completes
9 | b = []; 9 | b = [];
10 | } else { 10 | } else {
11 | a = {}; 11 | a = {};
``` ```

View File

@@ -18,20 +18,18 @@ function Component() {
``` ```
Found 1 error: Found 1 error:
Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead Error: Cannot reassign a variable after render completes
Variable `onClick` cannot be reassigned after render. Reassigning variable `onClick` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead
error.todo-function-expression-references-later-variable-declaration.ts:3:4 error.todo-function-expression-references-later-variable-declaration.ts:3:4
1 | function Component() { 1 | function Component() {
2 | let callback = () => { 2 | let callback = () => {
> 3 | onClick = () => {}; > 3 | onClick = () => {};
| ^^^^^^^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead | ^^^^^^^ Cannot reassign variable after render completes
4 | }; 4 | };
5 | let onClick; 5 | let onClick;
6 | 6 |
``` ```

View File

@@ -23,18 +23,18 @@ function Component(props) {
``` ```
Found 1 error: Found 1 error:
Error: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) Error: Calling setState during render may trigger an infinite loop
Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState)
error.unconditional-set-state-in-render-after-loop-break.ts:11:2 error.unconditional-set-state-in-render-after-loop-break.ts:11:2
9 | } 9 | }
10 | } 10 | }
> 11 | setState(true); > 11 | setState(true);
| ^^^^^^^^ This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) | ^^^^^^^^ Found setState() within useMemo()
12 | return state; 12 | return state;
13 | } 13 | }
14 | 14 |
``` ```

View File

@@ -18,18 +18,18 @@ function Component(props) {
``` ```
Found 1 error: Found 1 error:
Error: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) Error: Calling setState during render may trigger an infinite loop
Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState)
error.unconditional-set-state-in-render-after-loop.ts:6:2 error.unconditional-set-state-in-render-after-loop.ts:6:2
4 | for (const _ of props) { 4 | for (const _ of props) {
5 | } 5 | }
> 6 | setState(true); > 6 | setState(true);
| ^^^^^^^^ This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) | ^^^^^^^^ Found setState() within useMemo()
7 | return state; 7 | return state;
8 | } 8 | }
9 | 9 |
``` ```

View File

@@ -23,18 +23,18 @@ function Component(props) {
``` ```
Found 1 error: Found 1 error:
Error: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) Error: Calling setState during render may trigger an infinite loop
Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState)
error.unconditional-set-state-in-render-with-loop-throw.ts:11:2 error.unconditional-set-state-in-render-with-loop-throw.ts:11:2
9 | } 9 | }
10 | } 10 | }
> 11 | setState(true); > 11 | setState(true);
| ^^^^^^^^ This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) | ^^^^^^^^ Found setState() within useMemo()
12 | return state; 12 | return state;
13 | } 13 | }
14 | 14 |
``` ```

View File

@@ -21,18 +21,18 @@ function Component(props) {
``` ```
Found 1 error: Found 1 error:
Error: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) Error: Calling setState during render may trigger an infinite loop
Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState)
error.unconditional-set-state-lambda.ts:8:2 error.unconditional-set-state-lambda.ts:8:2
6 | setX(1); 6 | setX(1);
7 | }; 7 | };
> 8 | foo(); > 8 | foo();
| ^^^ This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) | ^^^ Found setState() within useMemo()
9 | 9 |
10 | return [x]; 10 | return [x];
11 | } 11 | }
``` ```

View File

@@ -29,18 +29,18 @@ function Component(props) {
``` ```
Found 1 error: Found 1 error:
Error: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) Error: Calling setState during render may trigger an infinite loop
Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState)
error.unconditional-set-state-nested-function-expressions.ts:16:2 error.unconditional-set-state-nested-function-expressions.ts:16:2
14 | bar(); 14 | bar();
15 | }; 15 | };
> 16 | baz(); > 16 | baz();
| ^^^ This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) | ^^^ Found setState() within useMemo()
17 | 17 |
18 | return [x]; 18 | return [x];
19 | } 19 | }
``` ```

View File

@@ -65,7 +65,7 @@ function Component(props) {
## Logs ## Logs
``` ```
{"kind":"CompileError","detail":{"options":{"reason":"Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","description":null,"severity":"InvalidReact","loc":{"start":{"line":11,"column":11,"index":222},"end":{"line":11,"column":32,"index":243},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"}}},"fnLoc":null} {"kind":"CompileError","detail":{"options":{"severity":"InvalidReact","category":"Avoid constructing JSX within try/catch","description":"React does not immediately render components when JSX is rendered, so any errors from this component will not be caught by the try/catch. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","details":[{"kind":"error","loc":{"start":{"line":11,"column":11,"index":222},"end":{"line":11,"column":32,"index":243},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"},"message":"Avoid constructing JSX within try/catch"}]}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":91},"end":{"line":17,"column":1,"index":298},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} {"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":91},"end":{"line":17,"column":1,"index":298},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0}
``` ```

View File

@@ -42,7 +42,7 @@ function Component(props) {
## Logs ## Logs
``` ```
{"kind":"CompileError","detail":{"options":{"reason":"Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","description":null,"severity":"InvalidReact","loc":{"start":{"line":5,"column":9,"index":104},"end":{"line":5,"column":16,"index":111},"filename":"invalid-jsx-in-try-with-catch.ts"}}},"fnLoc":null} {"kind":"CompileError","detail":{"options":{"severity":"InvalidReact","category":"Avoid constructing JSX within try/catch","description":"React does not immediately render components when JSX is rendered, so any errors from this component will not be caught by the try/catch. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","details":[{"kind":"error","loc":{"start":{"line":5,"column":9,"index":104},"end":{"line":5,"column":16,"index":111},"filename":"invalid-jsx-in-try-with-catch.ts"},"message":"Avoid constructing JSX within try/catch"}]}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":49},"end":{"line":10,"column":1,"index":160},"filename":"invalid-jsx-in-try-with-catch.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} {"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":49},"end":{"line":10,"column":1,"index":160},"filename":"invalid-jsx-in-try-with-catch.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0}
``` ```

View File

@@ -65,7 +65,7 @@ function _temp(s) {
## Logs ## Logs
``` ```
{"kind":"CompileError","detail":{"options":{"reason":"Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":13,"column":4,"index":265},"end":{"line":13,"column":5,"index":266},"filename":"invalid-setState-in-useEffect-transitive.ts","identifierName":"g"}}},"fnLoc":null} {"kind":"CompileError","detail":{"options":{"category":"Calling setState within an effect can trigger cascading renders","description":"Calling setState directly within a useEffect causes cascading renders that can hurt performance, and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","severity":"InvalidReact","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":13,"column":4,"index":265},"end":{"line":13,"column":5,"index":266},"filename":"invalid-setState-in-useEffect-transitive.ts","identifierName":"g"},"message":"Avoid calling setState() in the top-level of an effect"}]}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":92},"end":{"line":16,"column":1,"index":293},"filename":"invalid-setState-in-useEffect-transitive.ts"},"fnName":"Component","memoSlots":2,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} {"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":92},"end":{"line":16,"column":1,"index":293},"filename":"invalid-setState-in-useEffect-transitive.ts"},"fnName":"Component","memoSlots":2,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0}
``` ```

View File

@@ -45,7 +45,7 @@ function _temp(s) {
## Logs ## Logs
``` ```
{"kind":"CompileError","detail":{"options":{"reason":"Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":7,"column":4,"index":180},"end":{"line":7,"column":12,"index":188},"filename":"invalid-setState-in-useEffect.ts","identifierName":"setState"}}},"fnLoc":null} {"kind":"CompileError","detail":{"options":{"category":"Calling setState within an effect can trigger cascading renders","description":"Calling setState directly within a useEffect causes cascading renders that can hurt performance, and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","severity":"InvalidReact","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":7,"column":4,"index":180},"end":{"line":7,"column":12,"index":188},"filename":"invalid-setState-in-useEffect.ts","identifierName":"setState"},"message":"Avoid calling setState() in the top-level of an effect"}]}},"fnLoc":null}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":92},"end":{"line":10,"column":1,"index":225},"filename":"invalid-setState-in-useEffect.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} {"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":92},"end":{"line":10,"column":1,"index":225},"filename":"invalid-setState-in-useEffect.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0}
``` ```

View File

@@ -43,20 +43,18 @@ function Component() {
``` ```
Found 1 error: Found 1 error:
Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead Error: Cannot reassign a variable after render completes
Variable `local` cannot be reassigned after render. Reassigning variable `local` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead
error.invalid-reassign-local-variable-in-jsx-callback.ts:6:4 error.invalid-reassign-local-variable-in-jsx-callback.ts:6:4
4 | 4 |
5 | const reassignLocal = newValue => { 5 | const reassignLocal = newValue => {
> 6 | local = newValue; > 6 | local = newValue;
| ^^^^^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead | ^^^^^ Cannot reassign variable after render completes
7 | }; 7 | };
8 | 8 |
9 | const onClick = newValue => { 9 | const onClick = newValue => {
``` ```