Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8419fbcda3 | ||
|
|
8c0829bda4 |
@@ -10,48 +10,22 @@ import {codeFrameColumns} from '@babel/code-frame';
|
||||
import type {SourceLocation} from './HIR';
|
||||
import {Err, Ok, Result} from './Utils/Result';
|
||||
import {assertExhaustive} from './Utils/utils';
|
||||
|
||||
export enum ErrorSeverity {
|
||||
/**
|
||||
* Invalid JS syntax, or valid syntax that is semantically invalid which may indicate some
|
||||
* misunderstanding on the user’s part.
|
||||
*/
|
||||
InvalidJS = 'InvalidJS',
|
||||
/**
|
||||
* JS syntax that is not supported and which we do not plan to support. Developers should
|
||||
* rewrite to use supported forms.
|
||||
*/
|
||||
UnsupportedJS = 'UnsupportedJS',
|
||||
/**
|
||||
* Code that breaks the rules of React.
|
||||
*/
|
||||
InvalidReact = 'InvalidReact',
|
||||
/**
|
||||
* Incorrect configuration of the compiler.
|
||||
*/
|
||||
InvalidConfig = 'InvalidConfig',
|
||||
/**
|
||||
* Code that can reasonably occur and that doesn't break any rules, but is unsafe to preserve
|
||||
* memoization.
|
||||
*/
|
||||
CannotPreserveMemoization = 'CannotPreserveMemoization',
|
||||
/**
|
||||
* Unhandled syntax that we don't support yet.
|
||||
*/
|
||||
Todo = 'Todo',
|
||||
/**
|
||||
* An unexpected internal error in the compiler that indicates critical issues that can panic
|
||||
* the compiler.
|
||||
*/
|
||||
Invariant = 'Invariant',
|
||||
}
|
||||
import {ErrorSeverity} from './Utils/CompilerErrorSeverity';
|
||||
import {
|
||||
ErrorCode,
|
||||
ErrorCodeDetails,
|
||||
LinterCategory,
|
||||
} from './Utils/CompilerErrorCodes';
|
||||
export {ErrorSeverity};
|
||||
export {ErrorCode, ErrorCodeDetails, LinterCategory};
|
||||
|
||||
export type CompilerDiagnosticOptions = {
|
||||
severity: ErrorSeverity;
|
||||
category: string;
|
||||
description: string;
|
||||
description?: string | null | undefined;
|
||||
details: Array<CompilerDiagnosticDetail>;
|
||||
suggestions?: Array<CompilerSuggestion> | null | undefined;
|
||||
linterCategory?: LinterCategory | null | undefined;
|
||||
};
|
||||
|
||||
export type CompilerDiagnosticDetail =
|
||||
@@ -86,13 +60,28 @@ export type CompilerSuggestion =
|
||||
description: string;
|
||||
};
|
||||
|
||||
export type CompilerErrorDetailOptions = {
|
||||
export type PlainCompilerErrorDetailOptions = {
|
||||
errorCode?: void;
|
||||
reason: string;
|
||||
description?: string | null | undefined;
|
||||
severity: ErrorSeverity;
|
||||
severity:
|
||||
| ErrorSeverity.Invariant
|
||||
| ErrorSeverity.Todo
|
||||
| ErrorSeverity.InvalidConfig;
|
||||
loc: SourceLocation | null;
|
||||
suggestions?: Array<CompilerSuggestion> | null | undefined;
|
||||
};
|
||||
export type CodedCompilerErrorDetailOptions = {
|
||||
errorCode: ErrorCode;
|
||||
description?: string | null | undefined;
|
||||
loc: SourceLocation | null;
|
||||
suggestions?: Array<CompilerSuggestion> | null | undefined;
|
||||
linterCategory?: LinterCategory | null | undefined;
|
||||
};
|
||||
|
||||
export type CompilerErrorDetailOptions =
|
||||
| PlainCompilerErrorDetailOptions
|
||||
| CodedCompilerErrorDetailOptions;
|
||||
|
||||
export type PrintErrorMessageOptions = {
|
||||
/**
|
||||
@@ -102,19 +91,72 @@ export type PrintErrorMessageOptions = {
|
||||
eslint: boolean;
|
||||
};
|
||||
|
||||
export function makeCompilerDiagnostic(
|
||||
code: ErrorCode,
|
||||
options?: {
|
||||
description?: string;
|
||||
suggestions?: Array<CompilerSuggestion> | null | undefined;
|
||||
},
|
||||
): CompilerDiagnostic {
|
||||
return makeCompilerDiagnostic(code, options);
|
||||
}
|
||||
|
||||
export class CompilerDiagnostic {
|
||||
options: CompilerDiagnosticOptions;
|
||||
|
||||
constructor(options: CompilerDiagnosticOptions) {
|
||||
/**
|
||||
* Constructor is private to enforce that we either only create invariant diagnostics
|
||||
* or use ErrorCodes
|
||||
*/
|
||||
private constructor(options: CompilerDiagnosticOptions) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
static create(
|
||||
options: Omit<CompilerDiagnosticOptions, 'details'>,
|
||||
): CompilerDiagnostic {
|
||||
static create<
|
||||
T extends CompilerDiagnosticOptions & {severity: ErrorSeverity.Invariant},
|
||||
>(options: Omit<T, 'details'>): CompilerDiagnostic {
|
||||
return new CompilerDiagnostic({...options, details: []});
|
||||
}
|
||||
|
||||
static fromCode(
|
||||
code: ErrorCode,
|
||||
options?: {
|
||||
description?: string;
|
||||
suggestions?: Array<CompilerSuggestion> | null | undefined;
|
||||
details?: Array<CompilerDiagnosticDetail> | null | undefined;
|
||||
},
|
||||
): CompilerDiagnostic {
|
||||
const errorEntry = ErrorCodeDetails[code];
|
||||
let description = undefined;
|
||||
if (errorEntry.description != null) {
|
||||
description = endWithPeriod(errorEntry.description);
|
||||
}
|
||||
if (options?.description != null && options.description.length > 0) {
|
||||
if (description != null && description.length > 0) {
|
||||
description += ' ';
|
||||
} else {
|
||||
description = '';
|
||||
}
|
||||
description += endWithPeriod(options.description);
|
||||
}
|
||||
|
||||
const diagnosticOptions: CompilerDiagnosticOptions = {
|
||||
severity: errorEntry.severity,
|
||||
category: errorEntry.reason,
|
||||
description,
|
||||
linterCategory: errorEntry.linterCategory,
|
||||
suggestions: options?.suggestions,
|
||||
details: options?.details ?? [],
|
||||
};
|
||||
|
||||
return new CompilerDiagnostic(diagnosticOptions);
|
||||
}
|
||||
|
||||
// TODO: remove after converting test fixtures to use printErrorMessage
|
||||
serialize(): unknown {
|
||||
return {options: {...this.options, linterCategory: undefined}};
|
||||
}
|
||||
|
||||
get category(): CompilerDiagnosticOptions['category'] {
|
||||
return this.options.category;
|
||||
}
|
||||
@@ -127,6 +169,9 @@ export class CompilerDiagnostic {
|
||||
get suggestions(): CompilerDiagnosticOptions['suggestions'] {
|
||||
return this.options.suggestions;
|
||||
}
|
||||
get linterCategory(): CompilerDiagnosticOptions['linterCategory'] {
|
||||
return this.options.linterCategory;
|
||||
}
|
||||
|
||||
withDetail(detail: CompilerDiagnosticDetail): CompilerDiagnostic {
|
||||
this.options.details.push(detail);
|
||||
@@ -138,11 +183,10 @@ export class CompilerDiagnostic {
|
||||
}
|
||||
|
||||
printErrorMessage(source: string, options: PrintErrorMessageOptions): string {
|
||||
const buffer = [
|
||||
printErrorSummary(this.severity, this.category),
|
||||
'\n\n',
|
||||
this.description,
|
||||
];
|
||||
const buffer = [printErrorSummary(this.severity, this.category)];
|
||||
if (this.description != null) {
|
||||
buffer.push(`\n\n${endWithPeriod(this.description)}`);
|
||||
}
|
||||
for (const detail of this.options.details) {
|
||||
switch (detail.kind) {
|
||||
case 'error': {
|
||||
@@ -181,7 +225,7 @@ export class CompilerDiagnostic {
|
||||
toString(): string {
|
||||
const buffer = [printErrorSummary(this.severity, this.category)];
|
||||
if (this.description != null) {
|
||||
buffer.push(`. ${this.description}.`);
|
||||
buffer.push(`. ${endWithPeriod(this.description)}`);
|
||||
}
|
||||
const loc = this.primaryLocation();
|
||||
if (loc != null && typeof loc !== 'symbol') {
|
||||
@@ -202,13 +246,68 @@ export class CompilerErrorDetail {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
get reason(): CompilerErrorDetailOptions['reason'] {
|
||||
return this.options.reason;
|
||||
static fromCode(
|
||||
code: ErrorCode,
|
||||
details?: {
|
||||
description?: string | null;
|
||||
loc?: SourceLocation | null;
|
||||
suggestions?: Array<CompilerSuggestion> | null | undefined;
|
||||
},
|
||||
): CompilerErrorDetail {
|
||||
return new CompilerErrorDetail({
|
||||
...details,
|
||||
errorCode: code,
|
||||
} as CodedCompilerErrorDetailOptions);
|
||||
}
|
||||
get description(): CompilerErrorDetailOptions['description'] {
|
||||
return this.options.description;
|
||||
|
||||
// TODO: remove after converting test fixtures to use printErrorMessage
|
||||
serialize(): unknown {
|
||||
return {
|
||||
options: {
|
||||
reason: this.reason,
|
||||
description: this.description,
|
||||
severity: this.severity,
|
||||
loc: this.loc,
|
||||
suggestions: this.suggestions,
|
||||
},
|
||||
};
|
||||
}
|
||||
get severity(): CompilerErrorDetailOptions['severity'] {
|
||||
|
||||
get reason(): string {
|
||||
if (this.options.errorCode != null) {
|
||||
return ErrorCodeDetails[this.options.errorCode].reason;
|
||||
} else {
|
||||
return this.options.reason;
|
||||
}
|
||||
}
|
||||
get description(): string | null | undefined {
|
||||
if (this.options.errorCode != null) {
|
||||
let description = undefined;
|
||||
const errorEntry = ErrorCodeDetails[this.options.errorCode];
|
||||
if (errorEntry.description != null) {
|
||||
description = endWithPeriod(errorEntry.description);
|
||||
}
|
||||
if (
|
||||
this.options.description != null &&
|
||||
this.options.description.length > 0
|
||||
) {
|
||||
if (description != null && description.length > 0) {
|
||||
description += ' ';
|
||||
} else {
|
||||
description = '';
|
||||
}
|
||||
description += endWithPeriod(this.options.description);
|
||||
}
|
||||
return description;
|
||||
}
|
||||
return this.options.description != null
|
||||
? endWithPeriod(this.options.description)
|
||||
: this.options.description;
|
||||
}
|
||||
get severity(): ErrorSeverity {
|
||||
if (this.options.errorCode != null) {
|
||||
return ErrorCodeDetails[this.options.errorCode].severity;
|
||||
}
|
||||
return this.options.severity;
|
||||
}
|
||||
get loc(): CompilerErrorDetailOptions['loc'] {
|
||||
@@ -217,6 +316,12 @@ export class CompilerErrorDetail {
|
||||
get suggestions(): CompilerErrorDetailOptions['suggestions'] {
|
||||
return this.options.suggestions;
|
||||
}
|
||||
get linterCategory(): LinterCategory | null | undefined {
|
||||
if (this.options.errorCode != null) {
|
||||
return ErrorCodeDetails[this.options.errorCode].linterCategory;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
primaryLocation(): SourceLocation | null {
|
||||
return this.loc;
|
||||
@@ -225,7 +330,7 @@ export class CompilerErrorDetail {
|
||||
printErrorMessage(source: string, options: PrintErrorMessageOptions): string {
|
||||
const buffer = [printErrorSummary(this.severity, this.reason)];
|
||||
if (this.description != null) {
|
||||
buffer.push(`\n\n${this.description}.`);
|
||||
buffer.push(`\n\n${endWithPeriod(this.description)}`);
|
||||
}
|
||||
const loc = this.loc;
|
||||
if (loc != null && typeof loc !== 'symbol') {
|
||||
@@ -250,7 +355,7 @@ export class CompilerErrorDetail {
|
||||
toString(): string {
|
||||
const buffer = [printErrorSummary(this.severity, this.reason)];
|
||||
if (this.description != null) {
|
||||
buffer.push(`. ${this.description}.`);
|
||||
buffer.push(`. ${endWithPeriod(this.description)}`);
|
||||
}
|
||||
const loc = this.loc;
|
||||
if (loc != null && typeof loc !== 'symbol') {
|
||||
@@ -266,7 +371,7 @@ export class CompilerError extends Error {
|
||||
|
||||
static invariant(
|
||||
condition: unknown,
|
||||
options: Omit<CompilerErrorDetailOptions, 'severity'>,
|
||||
options: Omit<PlainCompilerErrorDetailOptions, 'severity'>,
|
||||
): asserts condition {
|
||||
if (!condition) {
|
||||
const errors = new CompilerError();
|
||||
@@ -280,14 +385,14 @@ export class CompilerError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
static throwDiagnostic(options: CompilerDiagnosticOptions): never {
|
||||
static throwDiagnostic(diagnostic: CompilerDiagnostic): never {
|
||||
const errors = new CompilerError();
|
||||
errors.pushDiagnostic(new CompilerDiagnostic(options));
|
||||
errors.pushDiagnostic(diagnostic);
|
||||
throw errors;
|
||||
}
|
||||
|
||||
static throwTodo(
|
||||
options: Omit<CompilerErrorDetailOptions, 'severity'>,
|
||||
options: Omit<PlainCompilerErrorDetailOptions, 'severity'>,
|
||||
): never {
|
||||
const errors = new CompilerError();
|
||||
errors.pushErrorDetail(
|
||||
@@ -296,34 +401,21 @@ export class CompilerError extends Error {
|
||||
throw errors;
|
||||
}
|
||||
|
||||
static throwInvalidJS(
|
||||
options: Omit<CompilerErrorDetailOptions, 'severity'>,
|
||||
static throwFromCode(
|
||||
code: ErrorCode,
|
||||
options?: {
|
||||
description?: string | null;
|
||||
loc?: SourceLocation | null;
|
||||
suggestions?: Array<CompilerSuggestion> | null | undefined;
|
||||
},
|
||||
): never {
|
||||
const errors = new CompilerError();
|
||||
errors.pushErrorDetail(
|
||||
new CompilerErrorDetail({
|
||||
...options,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
}),
|
||||
);
|
||||
throw errors;
|
||||
}
|
||||
|
||||
static throwInvalidReact(
|
||||
options: Omit<CompilerErrorDetailOptions, 'severity'>,
|
||||
): never {
|
||||
const errors = new CompilerError();
|
||||
errors.pushErrorDetail(
|
||||
new CompilerErrorDetail({
|
||||
...options,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
}),
|
||||
);
|
||||
errors.pushErrorDetail(CompilerErrorDetail.fromCode(code, options));
|
||||
throw errors;
|
||||
}
|
||||
|
||||
static throwInvalidConfig(
|
||||
options: Omit<CompilerErrorDetailOptions, 'severity'>,
|
||||
options: Omit<PlainCompilerErrorDetailOptions, 'severity'>,
|
||||
): never {
|
||||
const errors = new CompilerError();
|
||||
errors.pushErrorDetail(
|
||||
@@ -392,13 +484,10 @@ export class CompilerError extends Error {
|
||||
}
|
||||
|
||||
push(options: CompilerErrorDetailOptions): CompilerErrorDetail {
|
||||
const detail = new CompilerErrorDetail({
|
||||
reason: options.reason,
|
||||
description: options.description ?? null,
|
||||
severity: options.severity,
|
||||
suggestions: options.suggestions,
|
||||
loc: typeof options.loc === 'symbol' ? null : options.loc,
|
||||
});
|
||||
if (options instanceof CompilerErrorDetail) {
|
||||
return this.pushErrorDetail(options);
|
||||
}
|
||||
const detail = new CompilerErrorDetail(options);
|
||||
return this.pushErrorDetail(detail);
|
||||
}
|
||||
|
||||
@@ -407,6 +496,19 @@ export class CompilerError extends Error {
|
||||
return detail;
|
||||
}
|
||||
|
||||
pushErrorCode(
|
||||
code: ErrorCode,
|
||||
details?: {
|
||||
description?: string | null;
|
||||
loc?: SourceLocation | null;
|
||||
suggestions?: Array<CompilerSuggestion> | null | undefined;
|
||||
},
|
||||
): CompilerErrorDetail {
|
||||
const detail = CompilerErrorDetail.fromCode(code, details);
|
||||
this.details.push(detail);
|
||||
return detail;
|
||||
}
|
||||
|
||||
hasErrors(): boolean {
|
||||
return this.details.length > 0;
|
||||
}
|
||||
@@ -493,3 +595,10 @@ function printErrorSummary(severity: ErrorSeverity, message: string): string {
|
||||
}
|
||||
return `${severityCategory}: ${message}`;
|
||||
}
|
||||
|
||||
function endWithPeriod(s: string): string {
|
||||
if (s === '' || s.endsWith('.')) {
|
||||
return s;
|
||||
}
|
||||
return `${s.replace(/\.+$/, '')}.`;
|
||||
}
|
||||
|
||||
@@ -38,10 +38,10 @@ export function validateRestrictedImports(
|
||||
ImportDeclaration(importDeclPath) {
|
||||
if (restrictedImports.has(importDeclPath.node.source.value)) {
|
||||
error.push({
|
||||
severity: ErrorSeverity.Todo,
|
||||
reason: 'Bailing out due to blocklisted import',
|
||||
description: `Import from module ${importDeclPath.node.source.value}`,
|
||||
loc: importDeclPath.node.loc ?? null,
|
||||
severity: ErrorSeverity.Todo,
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -205,10 +205,10 @@ export class ProgramContext {
|
||||
}
|
||||
const error = new CompilerError();
|
||||
error.push({
|
||||
severity: ErrorSeverity.Todo,
|
||||
reason: 'Encountered conflicting global in generated program',
|
||||
description: `Conflict from local binding ${name}`,
|
||||
loc: scope.getBinding(name)?.path.node.loc ?? null,
|
||||
severity: ErrorSeverity.Todo,
|
||||
suggestions: null,
|
||||
});
|
||||
return Err(error);
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
CompilerDiagnostic,
|
||||
CompilerError,
|
||||
CompilerErrorDetail,
|
||||
CompilerErrorDetailOptions,
|
||||
PlainCompilerErrorDetailOptions,
|
||||
} from '../CompilerError';
|
||||
import {
|
||||
EnvironmentConfig,
|
||||
@@ -107,6 +107,8 @@ export type PluginOptions = {
|
||||
* passes.
|
||||
*
|
||||
* Defaults to false
|
||||
*
|
||||
* TODO: rename this to lintOnly or something similar
|
||||
*/
|
||||
noEmit: boolean;
|
||||
|
||||
@@ -234,7 +236,10 @@ export type CompileErrorEvent = {
|
||||
export type CompileDiagnosticEvent = {
|
||||
kind: 'CompileDiagnostic';
|
||||
fnLoc: t.SourceLocation | null;
|
||||
detail: Omit<Omit<CompilerErrorDetailOptions, 'severity'>, 'suggestions'>;
|
||||
detail: Omit<
|
||||
Omit<PlainCompilerErrorDetailOptions, 'severity'>,
|
||||
'suggestions'
|
||||
>;
|
||||
};
|
||||
export type CompileSuccessEvent = {
|
||||
kind: 'CompileSuccess';
|
||||
|
||||
@@ -100,7 +100,6 @@ import {outlineJSX} from '../Optimization/OutlineJsx';
|
||||
import {optimizePropsMethodCalls} from '../Optimization/OptimizePropsMethodCalls';
|
||||
import {transformFire} from '../Transform';
|
||||
import {validateNoImpureFunctionsInRender} from '../Validation/ValidateNoImpureFunctionsInRender';
|
||||
import {CompilerError} from '..';
|
||||
import {validateStaticComponents} from '../Validation/ValidateStaticComponents';
|
||||
import {validateNoFreezingKnownMutableFunctions} from '../Validation/ValidateNoFreezingKnownMutableFunctions';
|
||||
import {inferMutationAliasingEffects} from '../Inference/InferMutationAliasingEffects';
|
||||
@@ -174,7 +173,7 @@ function runWithEnvironment(
|
||||
!env.config.disableMemoizationForDebugging &&
|
||||
!env.config.enableChangeDetectionForDebugging
|
||||
) {
|
||||
dropManualMemoization(hir).unwrap();
|
||||
env.logOrThrowErrors(dropManualMemoization(hir));
|
||||
log({kind: 'hir', name: 'DropManualMemoization', value: hir});
|
||||
}
|
||||
|
||||
@@ -207,10 +206,10 @@ function runWithEnvironment(
|
||||
|
||||
if (env.isInferredMemoEnabled) {
|
||||
if (env.config.validateHooksUsage) {
|
||||
validateHooksUsage(hir).unwrap();
|
||||
env.logOrThrowErrors(validateHooksUsage(hir));
|
||||
}
|
||||
if (env.config.validateNoCapitalizedCalls) {
|
||||
validateNoCapitalizedCalls(hir).unwrap();
|
||||
if (env.config.validateNoCapitalizedCalls != null) {
|
||||
env.logOrThrowErrors(validateNoCapitalizedCalls(hir));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,20 +229,17 @@ function runWithEnvironment(
|
||||
log({kind: 'hir', name: 'AnalyseFunctions', value: hir});
|
||||
|
||||
if (!env.config.enableNewMutationAliasingModel) {
|
||||
const fnEffectErrors = inferReferenceEffects(hir);
|
||||
const fnEffectResult = inferReferenceEffects(hir);
|
||||
|
||||
if (env.isInferredMemoEnabled) {
|
||||
if (fnEffectErrors.length > 0) {
|
||||
CompilerError.throw(fnEffectErrors[0]);
|
||||
}
|
||||
env.logOrThrowErrors(fnEffectResult);
|
||||
}
|
||||
log({kind: 'hir', name: 'InferReferenceEffects', value: hir});
|
||||
} else {
|
||||
const mutabilityAliasingErrors = inferMutationAliasingEffects(hir);
|
||||
log({kind: 'hir', name: 'InferMutationAliasingEffects', value: hir});
|
||||
if (env.isInferredMemoEnabled) {
|
||||
if (mutabilityAliasingErrors.isErr()) {
|
||||
throw mutabilityAliasingErrors.unwrapErr();
|
||||
}
|
||||
env.logOrThrowErrors(mutabilityAliasingErrors);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,10 +268,8 @@ function runWithEnvironment(
|
||||
});
|
||||
log({kind: 'hir', name: 'InferMutationAliasingRanges', value: hir});
|
||||
if (env.isInferredMemoEnabled) {
|
||||
if (mutabilityAliasingErrors.isErr()) {
|
||||
throw mutabilityAliasingErrors.unwrapErr();
|
||||
}
|
||||
validateLocalsNotReassignedAfterRender(hir);
|
||||
env.logOrThrowErrors(mutabilityAliasingErrors.map(() => undefined));
|
||||
env.logOrThrowErrors(validateLocalsNotReassignedAfterRender(hir));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,15 +279,15 @@ function runWithEnvironment(
|
||||
}
|
||||
|
||||
if (env.config.validateRefAccessDuringRender) {
|
||||
validateNoRefAccessInRender(hir).unwrap();
|
||||
env.logOrThrowErrors(validateNoRefAccessInRender(hir));
|
||||
}
|
||||
|
||||
if (env.config.validateNoSetStateInRender) {
|
||||
validateNoSetStateInRender(hir).unwrap();
|
||||
env.logOrThrowErrors(validateNoSetStateInRender(hir));
|
||||
}
|
||||
|
||||
if (env.config.validateNoDerivedComputationsInEffects) {
|
||||
validateNoDerivedComputationsInEffects(hir);
|
||||
env.logOrThrowErrors(validateNoDerivedComputationsInEffects(hir));
|
||||
}
|
||||
|
||||
if (env.config.validateNoSetStateInEffects) {
|
||||
@@ -303,16 +297,18 @@ function runWithEnvironment(
|
||||
if (env.config.validateNoJSXInTryStatements) {
|
||||
env.logErrors(validateNoJSXInTryStatement(hir));
|
||||
}
|
||||
|
||||
if (env.config.validateNoImpureFunctionsInRender) {
|
||||
validateNoImpureFunctionsInRender(hir).unwrap();
|
||||
if (
|
||||
env.config.validateNoImpureFunctionsInRender &&
|
||||
!env.config.enableNewMutationAliasingModel
|
||||
) {
|
||||
env.logOrThrowErrors(validateNoImpureFunctionsInRender(hir));
|
||||
}
|
||||
|
||||
if (
|
||||
env.config.validateNoFreezingKnownMutableFunctions ||
|
||||
env.config.enableNewMutationAliasingModel
|
||||
) {
|
||||
validateNoFreezingKnownMutableFunctions(hir).unwrap();
|
||||
env.logOrThrowErrors(validateNoFreezingKnownMutableFunctions(hir));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,7 @@
|
||||
|
||||
import {NodePath} from '@babel/core';
|
||||
import * as t from '@babel/types';
|
||||
import {
|
||||
CompilerError,
|
||||
CompilerErrorDetail,
|
||||
ErrorSeverity,
|
||||
} from '../CompilerError';
|
||||
import {CompilerError, ErrorSeverity} from '../CompilerError';
|
||||
import {ExternalFunction, ReactFunctionType} from '../HIR/Environment';
|
||||
import {CodegenFunction} from '../ReactiveScopes';
|
||||
import {isComponentDeclaration} from '../Utils/ComponentDeclaration';
|
||||
@@ -32,6 +28,7 @@ import {
|
||||
} from './Suppression';
|
||||
import {GeneratedSource} from '../HIR';
|
||||
import {Err, Ok, Result} from '../Utils/Result';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
|
||||
export type CompilerPass = {
|
||||
opts: PluginOptions;
|
||||
@@ -101,12 +98,9 @@ function findDirectivesDynamicGating(
|
||||
if (t.isValidIdentifier(maybeMatch[1])) {
|
||||
result.push({directive, match: maybeMatch[1]});
|
||||
} else {
|
||||
errors.push({
|
||||
reason: `Dynamic gating directive is not a valid JavaScript identifier`,
|
||||
errors.pushErrorCode(ErrorCode.DYNAMIC_GATING_IS_NOT_IDENTIFIER, {
|
||||
description: `Found '${directive.value.value}'`,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
loc: directive.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -115,14 +109,11 @@ function findDirectivesDynamicGating(
|
||||
return Err(errors);
|
||||
} else if (result.length > 1) {
|
||||
const error = new CompilerError();
|
||||
error.push({
|
||||
reason: `Multiple dynamic gating directives found`,
|
||||
error.pushErrorCode(ErrorCode.DYNAMIC_GATING_MULTIPLE_DIRECTIVES, {
|
||||
description: `Expected a single directive but found [${result
|
||||
.map(r => r.directive.value.value)
|
||||
.join(', ')}]`,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
loc: result[0].directive.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return Err(error);
|
||||
} else if (result.length === 1) {
|
||||
@@ -451,14 +442,12 @@ export function compileProgram(
|
||||
if (programContext.hasModuleScopeOptOut) {
|
||||
if (compiledFns.length > 0) {
|
||||
const error = new CompilerError();
|
||||
error.pushErrorDetail(
|
||||
new CompilerErrorDetail({
|
||||
reason:
|
||||
'Unexpected compiled functions when module scope opt-out is present',
|
||||
severity: ErrorSeverity.Invariant,
|
||||
loc: null,
|
||||
}),
|
||||
);
|
||||
error.push({
|
||||
reason:
|
||||
'Unexpected compiled functions when module scope opt-out is present',
|
||||
severity: ErrorSeverity.Invariant,
|
||||
loc: null,
|
||||
});
|
||||
handleError(error, programContext, null);
|
||||
}
|
||||
return null;
|
||||
@@ -591,9 +580,7 @@ function processFn(
|
||||
let compiledFn: CodegenFunction;
|
||||
const compileResult = tryCompileFunction(fn, fnType, programContext);
|
||||
if (compileResult.kind === 'error') {
|
||||
if (directives.optOut != null) {
|
||||
logError(compileResult.error, programContext, fn.node.loc ?? null);
|
||||
} else {
|
||||
if (directives.optOut == null) {
|
||||
handleError(compileResult.error, programContext, fn.node.loc ?? null);
|
||||
}
|
||||
const retryResult = retryCompileFunction(fn, fnType, programContext);
|
||||
@@ -692,7 +679,7 @@ function tryCompileFunction(
|
||||
fn,
|
||||
programContext.opts.environment,
|
||||
fnType,
|
||||
'all_features',
|
||||
programContext.opts.noEmit ? 'lint_only' : 'all_features',
|
||||
programContext,
|
||||
programContext.opts.logger,
|
||||
programContext.filename,
|
||||
@@ -805,15 +792,7 @@ function shouldSkipCompilation(
|
||||
if (pass.opts.sources) {
|
||||
if (pass.filename === null) {
|
||||
const error = new CompilerError();
|
||||
error.pushErrorDetail(
|
||||
new CompilerErrorDetail({
|
||||
reason: `Expected a filename but found none.`,
|
||||
description:
|
||||
"When the 'sources' config options is specified, the React compiler will only compile files with a name",
|
||||
severity: ErrorSeverity.InvalidConfig,
|
||||
loc: null,
|
||||
}),
|
||||
);
|
||||
error.pushErrorCode(ErrorCode.FILENAME_NOT_SET);
|
||||
handleError(error, pass, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
CompilerDiagnostic,
|
||||
CompilerError,
|
||||
CompilerSuggestionOperation,
|
||||
ErrorSeverity,
|
||||
ErrorCode,
|
||||
} from '../CompilerError';
|
||||
import {assertExhaustive} from '../Utils/utils';
|
||||
import {GeneratedSource} from '../HIR';
|
||||
@@ -165,14 +165,12 @@ export function suppressionsToCompilerError(
|
||||
let reason, suggestion;
|
||||
switch (suppressionRange.source) {
|
||||
case 'Eslint':
|
||||
reason =
|
||||
'React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled';
|
||||
reason = ErrorCode.BAILOUT_ESLINT_SUPPRESSION;
|
||||
suggestion =
|
||||
'Remove the ESLint suppression and address the React error';
|
||||
break;
|
||||
case 'Flow':
|
||||
reason =
|
||||
'React Compiler has skipped optimizing this component because one or more React rule violations were reported by Flow';
|
||||
reason = ErrorCode.BAILOUT_FLOW_SUPPRESSION;
|
||||
suggestion = 'Remove the Flow suppression and address the React error';
|
||||
break;
|
||||
default:
|
||||
@@ -182,10 +180,8 @@ export function suppressionsToCompilerError(
|
||||
);
|
||||
}
|
||||
error.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category: reason,
|
||||
description: `React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression \`${suppressionRange.disableComment.value.trim()}\``,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
CompilerDiagnostic.fromCode(reason, {
|
||||
description: `Found suppression \`${suppressionRange.disableComment.value.trim()}\``,
|
||||
suggestions: [
|
||||
{
|
||||
description: suggestion,
|
||||
|
||||
@@ -8,27 +8,24 @@
|
||||
import {NodePath} from '@babel/core';
|
||||
import * as t from '@babel/types';
|
||||
|
||||
import {CompilerError, EnvironmentConfig, ErrorSeverity, Logger} from '..';
|
||||
import {CompilerError, EnvironmentConfig, Logger} from '..';
|
||||
import {getOrInsertWith} from '../Utils/utils';
|
||||
import {Environment, GeneratedSource} from '../HIR';
|
||||
import {DEFAULT_EXPORT} from '../HIR/Environment';
|
||||
import {CompileProgramMetadata} from './Program';
|
||||
import {CompilerDiagnostic, CompilerDiagnosticOptions} from '../CompilerError';
|
||||
import {CompilerDiagnostic} from '../CompilerError';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
|
||||
function throwInvalidReact(
|
||||
options: Omit<CompilerDiagnosticOptions, 'severity'>,
|
||||
function logAndThrowDiagnostic(
|
||||
diagnostic: CompilerDiagnostic,
|
||||
{logger, filename}: TraversalState,
|
||||
): never {
|
||||
const detail: CompilerDiagnosticOptions = {
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
...options,
|
||||
};
|
||||
logger?.logEvent(filename, {
|
||||
kind: 'CompileError',
|
||||
fnLoc: null,
|
||||
detail: new CompilerDiagnostic(detail),
|
||||
detail: diagnostic,
|
||||
});
|
||||
CompilerError.throwDiagnostic(detail);
|
||||
CompilerError.throwDiagnostic(diagnostic);
|
||||
}
|
||||
|
||||
function isAutodepsSigil(
|
||||
@@ -90,13 +87,9 @@ function assertValidEffectImportReference(
|
||||
* as it may have already been transformed by the compiler (and not
|
||||
* memoized).
|
||||
*/
|
||||
throwInvalidReact(
|
||||
{
|
||||
category:
|
||||
'Cannot infer dependencies of this effect. This will break your build!',
|
||||
description:
|
||||
'To resolve, either pass a dependency array or fix reported compiler bailout diagnostics.' +
|
||||
(maybeErrorDiagnostic ? ` ${maybeErrorDiagnostic}` : ''),
|
||||
logAndThrowDiagnostic(
|
||||
CompilerDiagnostic.fromCode(ErrorCode.DID_NOT_INFER_DEPS, {
|
||||
description: maybeErrorDiagnostic ? `${maybeErrorDiagnostic}` : '',
|
||||
details: [
|
||||
{
|
||||
kind: 'error',
|
||||
@@ -104,7 +97,7 @@ function assertValidEffectImportReference(
|
||||
loc: parent.node.loc ?? GeneratedSource,
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
context,
|
||||
);
|
||||
}
|
||||
@@ -121,10 +114,8 @@ function assertValidFireImportReference(
|
||||
paths[0],
|
||||
context.transformErrors,
|
||||
);
|
||||
throwInvalidReact(
|
||||
{
|
||||
category:
|
||||
'[Fire] Untransformed reference to compiler-required feature.',
|
||||
logAndThrowDiagnostic(
|
||||
CompilerDiagnostic.fromCode(ErrorCode.CANNOT_COMPILE_FIRE, {
|
||||
description:
|
||||
'Either remove this `fire` call or ensure it is successfully transformed by the compiler' +
|
||||
maybeErrorDiagnostic
|
||||
@@ -137,7 +128,7 @@ function assertValidFireImportReference(
|
||||
loc: paths[0].node.loc ?? GeneratedSource,
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
context,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {CompilerError, SourceLocation} from '..';
|
||||
import {ErrorCode} from '../CompilerError';
|
||||
import {
|
||||
ConcreteType,
|
||||
printConcrete,
|
||||
@@ -12,8 +13,8 @@ export function unsupportedLanguageFeature(
|
||||
desc: string,
|
||||
loc: SourceLocation,
|
||||
): never {
|
||||
CompilerError.throwInvalidJS({
|
||||
reason: `Typedchecker does not currently support language feature: ${desc}`,
|
||||
CompilerError.throwFromCode(ErrorCode.INVALID_JAVASCRIPT_AST, {
|
||||
description: `Typedchecker does not currently support language feature: ${desc}`,
|
||||
loc,
|
||||
});
|
||||
}
|
||||
@@ -49,16 +50,16 @@ export function raiseUnificationErrors(
|
||||
loc,
|
||||
});
|
||||
} else if (errs.length === 1) {
|
||||
CompilerError.throwInvalidJS({
|
||||
reason: `Unable to unify types because ${printUnificationError(errs[0])}`,
|
||||
CompilerError.throwFromCode(ErrorCode.INVALID_JAVASCRIPT_AST, {
|
||||
description: `Unable to unify types because ${printUnificationError(errs[0])}`,
|
||||
loc,
|
||||
});
|
||||
} else {
|
||||
const messages = errs
|
||||
.map(err => `\t* ${printUnificationError(err)}`)
|
||||
.join('\n');
|
||||
CompilerError.throwInvalidJS({
|
||||
reason: `Unable to unify types because:\n${messages}`,
|
||||
CompilerError.throwFromCode(ErrorCode.INVALID_JAVASCRIPT_AST, {
|
||||
description: `Unable to unify types because:\n${messages}`,
|
||||
loc,
|
||||
});
|
||||
}
|
||||
@@ -69,21 +70,21 @@ export function unresolvableTypeVariable(
|
||||
id: VariableId,
|
||||
loc: SourceLocation,
|
||||
): never {
|
||||
CompilerError.throwInvalidJS({
|
||||
reason: `Unable to resolve free variable ${id} to a concrete type`,
|
||||
CompilerError.throwFromCode(ErrorCode.INVALID_JAVASCRIPT_AST, {
|
||||
description: `Unable to resolve free variable ${id} to a concrete type`,
|
||||
loc,
|
||||
});
|
||||
}
|
||||
|
||||
export function cannotAddVoid(explicit: boolean, loc: SourceLocation): never {
|
||||
if (explicit) {
|
||||
CompilerError.throwInvalidJS({
|
||||
reason: `Undefined is not a valid operand of \`+\``,
|
||||
CompilerError.throwFromCode(ErrorCode.INVALID_JAVASCRIPT_AST, {
|
||||
description: `Undefined is not a valid operand of \`+\``,
|
||||
loc,
|
||||
});
|
||||
} else {
|
||||
CompilerError.throwInvalidJS({
|
||||
reason: `Value may be undefined, which is not a valid operand of \`+\``,
|
||||
CompilerError.throwFromCode(ErrorCode.INVALID_JAVASCRIPT_AST, {
|
||||
description: `Value may be undefined, which is not a valid operand of \`+\``,
|
||||
loc,
|
||||
});
|
||||
}
|
||||
@@ -93,8 +94,8 @@ export function unsupportedTypeAnnotation(
|
||||
desc: string,
|
||||
loc: SourceLocation,
|
||||
): never {
|
||||
CompilerError.throwInvalidJS({
|
||||
reason: `Typedchecker does not currently support type annotation: ${desc}`,
|
||||
CompilerError.throwFromCode(ErrorCode.INVALID_JAVASCRIPT_AST, {
|
||||
description: `Typedchecker does not currently support type annotation: ${desc}`,
|
||||
loc,
|
||||
});
|
||||
}
|
||||
@@ -106,16 +107,16 @@ export function checkTypeArgumentArity(
|
||||
loc: SourceLocation,
|
||||
): void {
|
||||
if (expected !== actual) {
|
||||
CompilerError.throwInvalidJS({
|
||||
reason: `Expected ${desc} to have ${expected} type parameters, got ${actual}`,
|
||||
CompilerError.throwFromCode(ErrorCode.INVALID_JAVASCRIPT_AST, {
|
||||
description: `Expected ${desc} to have ${expected} type parameters, got ${actual}`,
|
||||
loc,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function notAFunction(desc: string, loc: SourceLocation): void {
|
||||
CompilerError.throwInvalidJS({
|
||||
reason: `Cannot call ${desc} because it is not a function`,
|
||||
CompilerError.throwFromCode(ErrorCode.INVALID_JAVASCRIPT_AST, {
|
||||
description: `Cannot call ${desc} because it is not a function`,
|
||||
loc,
|
||||
});
|
||||
}
|
||||
@@ -124,8 +125,8 @@ export function notAPolymorphicFunction(
|
||||
desc: string,
|
||||
loc: SourceLocation,
|
||||
): void {
|
||||
CompilerError.throwInvalidJS({
|
||||
reason: `Cannot call ${desc} with type arguments because it is not a polymorphic function`,
|
||||
CompilerError.throwFromCode(ErrorCode.INVALID_JAVASCRIPT_AST, {
|
||||
description: `Cannot call ${desc} with type arguments because it is not a polymorphic function`,
|
||||
loc,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
CompilerDiagnostic,
|
||||
CompilerError,
|
||||
CompilerSuggestionOperation,
|
||||
ErrorCode,
|
||||
ErrorSeverity,
|
||||
} from '../CompilerError';
|
||||
import {Err, Ok, Result} from '../Utils/Result';
|
||||
@@ -170,14 +171,12 @@ export function lower(
|
||||
);
|
||||
} else {
|
||||
builder.errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.Todo,
|
||||
category: `Handle ${param.node.type} parameters`,
|
||||
CompilerDiagnostic.fromCode(ErrorCode.UNKNOWN_FUNCTION_PARAMETERS, {
|
||||
description: `[BuildHIR] Add support for ${param.node.type} parameters.`,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: param.node.loc ?? null,
|
||||
message: 'Unsupported parameter type',
|
||||
message: `Unsupported parameter type: ${param.node.type}`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -201,14 +200,12 @@ export function lower(
|
||||
directives = body.get('directives').map(d => d.node.value.value);
|
||||
} else {
|
||||
builder.errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
category: `Unexpected function body kind`,
|
||||
CompilerDiagnostic.fromCode(ErrorCode.INVALID_JAVASCRIPT_AST, {
|
||||
description: `Expected function body to be an expression or a block statement, got \`${body.type}\`.`,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: body.node.loc ?? null,
|
||||
message: 'Expected a block statement or expression',
|
||||
message: 'Change this to a block statement or expression',
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -769,12 +766,12 @@ function lowerStatement(
|
||||
const testExpr = case_.get('test');
|
||||
if (testExpr.node == null) {
|
||||
if (hasDefault) {
|
||||
builder.errors.push({
|
||||
reason: `Expected at most one \`default\` branch in a switch statement, this code should have failed to parse`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
loc: case_.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
builder.errors.pushErrorCode(
|
||||
ErrorCode.INVALID_SYNTAX_MULTIPLE_DEFAULTS,
|
||||
{
|
||||
loc: case_.node.loc ?? null,
|
||||
},
|
||||
);
|
||||
break;
|
||||
}
|
||||
hasDefault = true;
|
||||
@@ -886,19 +883,20 @@ function lowerStatement(
|
||||
if (builder.isContextIdentifier(id)) {
|
||||
if (kind === InstructionKind.Const) {
|
||||
const declRangeStart = declaration.parentPath.node.start!;
|
||||
builder.errors.push({
|
||||
reason: `Expect \`const\` declaration not to be reassigned`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
loc: id.node.loc ?? null,
|
||||
suggestions: [
|
||||
{
|
||||
description: 'Change to a `let` declaration',
|
||||
op: CompilerSuggestionOperation.Replace,
|
||||
range: [declRangeStart, declRangeStart + 5], // "const".length
|
||||
text: 'let',
|
||||
},
|
||||
],
|
||||
});
|
||||
builder.errors.pushErrorCode(
|
||||
ErrorCode.INVALID_SYNTAX_REASSIGNED_CONST,
|
||||
{
|
||||
loc: id.node.loc ?? null,
|
||||
suggestions: [
|
||||
{
|
||||
description: 'Change to a `let` declaration',
|
||||
op: CompilerSuggestionOperation.Replace,
|
||||
range: [declRangeStart, declRangeStart + 5], // "const".length
|
||||
text: 'let',
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
}
|
||||
lowerValueToTemporary(builder, {
|
||||
kind: 'DeclareContext',
|
||||
@@ -932,13 +930,13 @@ function lowerStatement(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
builder.errors.push({
|
||||
reason: `Expected variable declaration to be an identifier if no initializer was provided`,
|
||||
description: `Got a \`${id.type}\``,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
loc: stmt.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
builder.errors.pushErrorCode(
|
||||
ErrorCode.INVALID_SYNTAX_BAD_VARIABLE_DECL,
|
||||
{
|
||||
description: `Got a \`${id.type}\``,
|
||||
loc: stmt.node.loc ?? null,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -1374,12 +1372,8 @@ function lowerStatement(
|
||||
return;
|
||||
}
|
||||
case 'WithStatement': {
|
||||
builder.errors.push({
|
||||
reason: `JavaScript 'with' syntax is not supported`,
|
||||
description: `'with' syntax is considered deprecated and removed from JavaScript standards, consider alternatives`,
|
||||
severity: ErrorSeverity.UnsupportedJS,
|
||||
builder.errors.pushErrorCode(ErrorCode.UNSUPPORTED_WITH, {
|
||||
loc: stmtPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
lowerValueToTemporary(builder, {
|
||||
kind: 'UnsupportedNode',
|
||||
@@ -1394,12 +1388,8 @@ function lowerStatement(
|
||||
* and complex enough to support that we don't anticipate supporting anytime soon. Developers
|
||||
* are encouraged to lift classes out of component/hook declarations.
|
||||
*/
|
||||
builder.errors.push({
|
||||
reason: 'Inline `class` declarations are not supported',
|
||||
description: `Move class declarations outside of components/hooks`,
|
||||
severity: ErrorSeverity.UnsupportedJS,
|
||||
builder.errors.pushErrorCode(ErrorCode.UNSUPPORTED_INNER_CLASS, {
|
||||
loc: stmtPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
lowerValueToTemporary(builder, {
|
||||
kind: 'UnsupportedNode',
|
||||
@@ -1423,12 +1413,8 @@ function lowerStatement(
|
||||
case 'ImportDeclaration':
|
||||
case 'TSExportAssignment':
|
||||
case 'TSImportEqualsDeclaration': {
|
||||
builder.errors.push({
|
||||
reason:
|
||||
'JavaScript `import` and `export` statements may only appear at the top level of a module',
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
builder.errors.pushErrorCode(ErrorCode.INVALID_IMPORT_EXPORT, {
|
||||
loc: stmtPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
lowerValueToTemporary(builder, {
|
||||
kind: 'UnsupportedNode',
|
||||
@@ -1438,12 +1424,8 @@ function lowerStatement(
|
||||
return;
|
||||
}
|
||||
case 'TSNamespaceExportDeclaration': {
|
||||
builder.errors.push({
|
||||
reason:
|
||||
'TypeScript `namespace` statements may only appear at the top level of a module',
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
builder.errors.pushErrorCode(ErrorCode.INVALID_TS_NAMESPACE, {
|
||||
loc: stmtPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
lowerValueToTemporary(builder, {
|
||||
kind: 'UnsupportedNode',
|
||||
@@ -1698,12 +1680,8 @@ function lowerExpression(
|
||||
const expr = exprPath as NodePath<t.NewExpression>;
|
||||
const calleePath = expr.get('callee');
|
||||
if (!calleePath.isExpression()) {
|
||||
builder.errors.push({
|
||||
reason: `Expected an expression as the \`new\` expression receiver (v8 intrinsics are not supported)`,
|
||||
description: `Got a \`${calleePath.node.type}\``,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
builder.errors.pushErrorCode(ErrorCode.UNSUPPORTED_NEW_EXPRESSION, {
|
||||
loc: calleePath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
|
||||
}
|
||||
@@ -1800,12 +1778,12 @@ function lowerExpression(
|
||||
last = lowerExpressionToTemporary(builder, item);
|
||||
}
|
||||
if (last === null) {
|
||||
builder.errors.push({
|
||||
reason: `Expected sequence expression to have at least one expression`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
loc: expr.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
builder.errors.pushErrorCode(
|
||||
ErrorCode.UNSUPPORTED_EMPTY_SEQUENCE_EXPRESSION,
|
||||
{
|
||||
loc: expr.node.loc ?? null,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
lowerValueToTemporary(builder, {
|
||||
kind: 'StoreLocal',
|
||||
@@ -2289,18 +2267,18 @@ function lowerExpression(
|
||||
});
|
||||
for (const [name, locations] of Object.entries(fbtLocations)) {
|
||||
if (locations.length > 1) {
|
||||
CompilerError.throwDiagnostic({
|
||||
severity: ErrorSeverity.Todo,
|
||||
category: 'Support duplicate fbt tags',
|
||||
description: `Support \`<${tagName}>\` tags with multiple \`<${tagName}:${name}>\` values`,
|
||||
details: locations.map(loc => {
|
||||
return {
|
||||
kind: 'error',
|
||||
message: `Multiple \`<${tagName}:${name}>\` tags found`,
|
||||
loc,
|
||||
};
|
||||
CompilerError.throwDiagnostic(
|
||||
CompilerDiagnostic.fromCode(ErrorCode.TODO_DUPLICATE_FBT_TAGS, {
|
||||
description: `Support \`<${tagName}>\` tags with multiple \`<${tagName}:${name}>\` values`,
|
||||
details: locations.map(loc => {
|
||||
return {
|
||||
kind: 'error',
|
||||
message: `Multiple \`<${tagName}:${name}>\` tags found`,
|
||||
loc,
|
||||
};
|
||||
}),
|
||||
}),
|
||||
});
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2389,11 +2367,8 @@ function lowerExpression(
|
||||
const quasis = expr.get('quasis');
|
||||
|
||||
if (subexprs.length !== quasis.length - 1) {
|
||||
builder.errors.push({
|
||||
reason: `Unexpected quasi and subexpression lengths in template literal`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
builder.errors.pushErrorCode(ErrorCode.INVALID_QUASI_LENGTHS, {
|
||||
loc: exprPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
return {kind: 'UnsupportedNode', node: exprNode, loc: exprLoc};
|
||||
}
|
||||
@@ -2441,24 +2416,23 @@ function lowerExpression(
|
||||
};
|
||||
}
|
||||
} else {
|
||||
builder.errors.push({
|
||||
reason: `Only object properties can be deleted`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
loc: expr.node.loc ?? null,
|
||||
suggestions: [
|
||||
{
|
||||
description: 'Remove this line',
|
||||
range: [expr.node.start!, expr.node.end!],
|
||||
op: CompilerSuggestionOperation.Remove,
|
||||
},
|
||||
],
|
||||
});
|
||||
builder.errors.pushErrorCode(
|
||||
ErrorCode.INVALID_SYNTAX_DELETE_EXPRESSION,
|
||||
{
|
||||
loc: expr.node.loc ?? null,
|
||||
suggestions: [
|
||||
{
|
||||
description: 'Remove this line',
|
||||
range: [expr.node.start!, expr.node.end!],
|
||||
op: CompilerSuggestionOperation.Remove,
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
return {kind: 'UnsupportedNode', node: expr.node, loc: exprLoc};
|
||||
}
|
||||
} else if (expr.node.operator === 'throw') {
|
||||
builder.errors.push({
|
||||
reason: `Throw expressions are not supported`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
builder.errors.pushErrorCode(ErrorCode.UNSUPPORTED_THROW_EXPRESSION, {
|
||||
loc: expr.node.loc ?? null,
|
||||
suggestions: [
|
||||
{
|
||||
@@ -3285,10 +3259,8 @@ function lowerJsxElementName(
|
||||
const name = exprPath.node.name.name;
|
||||
const tag = `${namespace}:${name}`;
|
||||
if (namespace.indexOf(':') !== -1 || name.indexOf(':') !== -1) {
|
||||
builder.errors.push({
|
||||
reason: `Expected JSXNamespacedName to have no colons in the namespace or name`,
|
||||
builder.errors.pushErrorCode(ErrorCode.INVALID_JSX_NAMESPACED_NAME, {
|
||||
description: `Got \`${namespace}\` : \`${name}\``,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
loc: exprPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
@@ -3583,11 +3555,7 @@ function lowerIdentifier(
|
||||
}
|
||||
default: {
|
||||
if (binding.kind === 'Global' && binding.name === 'eval') {
|
||||
builder.errors.push({
|
||||
reason: `The 'eval' function is not supported`,
|
||||
description:
|
||||
'Eval is an anti-pattern in JavaScript, and the code executed cannot be evaluated by React Compiler',
|
||||
severity: ErrorSeverity.UnsupportedJS,
|
||||
builder.errors.pushErrorCode(ErrorCode.UNSUPPORTED_EVAL, {
|
||||
loc: exprPath.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
@@ -3653,9 +3621,7 @@ function lowerIdentifierForAssignment(
|
||||
binding.bindingKind === 'const' &&
|
||||
kind === InstructionKind.Reassign
|
||||
) {
|
||||
builder.errors.push({
|
||||
reason: `Cannot reassign a \`const\` variable`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
builder.errors.pushErrorCode(ErrorCode.INVALID_SYNTAX_REASSIGNED_CONST, {
|
||||
loc: path.node.loc ?? null,
|
||||
description:
|
||||
binding.identifier.name != null
|
||||
@@ -3710,12 +3676,13 @@ function lowerAssignment(
|
||||
let temporary;
|
||||
if (builder.isContextIdentifier(lvalue)) {
|
||||
if (kind === InstructionKind.Const && !isHoistedIdentifier) {
|
||||
builder.errors.push({
|
||||
reason: `Expected \`const\` declaration not to be reassigned`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
loc: lvalue.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
builder.errors.pushErrorCode(
|
||||
ErrorCode.INVALID_SYNTAX_REASSIGNED_CONST,
|
||||
{
|
||||
loc: lvalue.node.loc ?? null,
|
||||
suggestions: null,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -3726,7 +3693,8 @@ function lowerAssignment(
|
||||
) {
|
||||
builder.errors.push({
|
||||
reason: `Unexpected context variable kind`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
description: `Expected one of Const, Reassign, Let, Function, got ${kind}`,
|
||||
severity: ErrorSeverity.Invariant,
|
||||
loc: lvalue.node.loc ?? null,
|
||||
suggestions: null,
|
||||
});
|
||||
|
||||
@@ -93,7 +93,7 @@ export const MacroSchema = z.union([
|
||||
z.tuple([z.string(), z.array(MacroMethodSchema)]),
|
||||
]);
|
||||
|
||||
export type CompilerMode = 'all_features' | 'no_inferred_memo';
|
||||
export type CompilerMode = 'all_features' | 'no_inferred_memo' | 'lint_only';
|
||||
|
||||
export type Macro = z.infer<typeof MacroSchema>;
|
||||
export type MacroMethod = z.infer<typeof MacroMethodSchema>;
|
||||
@@ -829,6 +829,14 @@ export class Environment {
|
||||
}
|
||||
}
|
||||
|
||||
logOrThrowErrors(errors: Result<void, CompilerError>): void {
|
||||
if (this.compilerMode === 'lint_only') {
|
||||
this.logErrors(errors);
|
||||
} else {
|
||||
errors.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
isContextIdentifier(node: t.Identifier): boolean {
|
||||
return this.#contextIdentifiers.has(node);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import {Type, makeType} from './Types';
|
||||
import {z} from 'zod';
|
||||
import type {AliasingEffect} from '../Inference/AliasingEffects';
|
||||
import {isReservedWord} from '../Utils/Keyword';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
|
||||
/*
|
||||
* *******************************************************************************************
|
||||
@@ -1322,18 +1323,18 @@ export function forkTemporaryIdentifier(
|
||||
*/
|
||||
export function makeIdentifierName(name: string): ValidatedIdentifier {
|
||||
if (isReservedWord(name)) {
|
||||
CompilerError.throwInvalidJS({
|
||||
reason: 'Expected a non-reserved identifier name',
|
||||
loc: GeneratedSource,
|
||||
description: `\`${name}\` is a reserved word in JavaScript and cannot be used as an identifier name`,
|
||||
suggestions: null,
|
||||
});
|
||||
CompilerError.throwFromCode(
|
||||
ErrorCode.INVALID_SYNTAX_RESERVED_VARIABLE_NAME,
|
||||
{
|
||||
loc: GeneratedSource,
|
||||
description: `\`${name}\` is a reserved word in JavaScript and cannot be used as an identifier name`,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
CompilerError.invariant(t.isValidIdentifier(name), {
|
||||
reason: `Expected a valid identifier name`,
|
||||
loc: GeneratedSource,
|
||||
description: `\`${name}\` is not a valid JavaScript identifier`,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import {Binding, NodePath} from '@babel/traverse';
|
||||
import * as t from '@babel/types';
|
||||
import {CompilerError, ErrorSeverity} from '../CompilerError';
|
||||
import {CompilerDiagnostic, CompilerError} from '../CompilerError';
|
||||
import {Environment} from './Environment';
|
||||
import {
|
||||
BasicBlock,
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
mapTerminalSuccessors,
|
||||
terminalFallthrough,
|
||||
} from './visitors';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
|
||||
/*
|
||||
* *******************************************************************************************
|
||||
@@ -308,19 +309,17 @@ export default class HIRBuilder {
|
||||
|
||||
resolveBinding(node: t.Identifier): Identifier {
|
||||
if (node.name === 'fbt') {
|
||||
CompilerError.throwDiagnostic({
|
||||
severity: ErrorSeverity.Todo,
|
||||
category: 'Support local variables named `fbt`',
|
||||
description:
|
||||
'Local variables named `fbt` may conflict with the fbt plugin and are not yet supported',
|
||||
details: [
|
||||
{
|
||||
kind: 'error',
|
||||
message: 'Rename to avoid conflict with fbt plugin',
|
||||
loc: node.loc ?? GeneratedSource,
|
||||
},
|
||||
],
|
||||
});
|
||||
CompilerError.throwDiagnostic(
|
||||
CompilerDiagnostic.fromCode(ErrorCode.TODO_CONFLICTING_FBT_IDENTIFIER, {
|
||||
details: [
|
||||
{
|
||||
kind: 'error',
|
||||
message: 'Rename to avoid conflict with fbt plugin',
|
||||
loc: node.loc ?? GeneratedSource,
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
}
|
||||
const originalName = node.name;
|
||||
let name = originalName;
|
||||
|
||||
@@ -5,12 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
CompilerDiagnostic,
|
||||
CompilerError,
|
||||
ErrorSeverity,
|
||||
SourceLocation,
|
||||
} from '..';
|
||||
import {CompilerDiagnostic, CompilerError, SourceLocation} from '..';
|
||||
import {
|
||||
CallExpression,
|
||||
Effect,
|
||||
@@ -35,6 +30,7 @@ import {
|
||||
makeInstructionId,
|
||||
} from '../HIR';
|
||||
import {createTemporaryPlace, markInstructionIds} from '../HIR/HIRBuilder';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
type ManualMemoCallee = {
|
||||
@@ -299,12 +295,11 @@ function extractManualMemoizationArgs(
|
||||
>;
|
||||
if (fnPlace == null) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: `Expected a callback function to be passed to ${kind}`,
|
||||
description: `Expected a callback function to be passed to ${kind}`,
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
CompilerDiagnostic.fromCode(
|
||||
kind === 'useMemo'
|
||||
? ErrorCode.INVALID_USE_MEMO_NO_ARG0
|
||||
: ErrorCode.INVALID_USE_CALLBACK_NO_ARG0,
|
||||
).withDetail({
|
||||
kind: 'error',
|
||||
loc: instr.value.loc,
|
||||
message: `Expected a callback function to be passed to ${kind}`,
|
||||
@@ -314,12 +309,11 @@ function extractManualMemoizationArgs(
|
||||
}
|
||||
if (fnPlace.kind === 'Spread' || depsListPlace?.kind === 'Spread') {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: `Unexpected spread argument to ${kind}`,
|
||||
description: `Unexpected spread argument to ${kind}`,
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
CompilerDiagnostic.fromCode(
|
||||
fnPlace.kind === 'Spread'
|
||||
? ErrorCode.DYNAMIC_USE_MEMO_SPREAD_ARGUMENT
|
||||
: ErrorCode.DYNAMIC_USE_CALLBACK_SPREAD_ARGUMENT,
|
||||
).withDetail({
|
||||
kind: 'error',
|
||||
loc: instr.value.loc,
|
||||
message: `Unexpected spread argument to ${kind}`,
|
||||
@@ -334,12 +328,9 @@ function extractManualMemoizationArgs(
|
||||
);
|
||||
if (maybeDepsList == null) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: `Expected the dependency list for ${kind} to be an array literal`,
|
||||
description: `Expected the dependency list for ${kind} to be an array literal`,
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
CompilerDiagnostic.fromCode(
|
||||
ErrorCode.DYNAMIC_MANUAL_MEMO_DEPENDENCY_LIST,
|
||||
).withDetail({
|
||||
kind: 'error',
|
||||
loc: depsListPlace.loc,
|
||||
message: `Expected the dependency list for ${kind} to be an array literal`,
|
||||
@@ -352,12 +343,9 @@ function extractManualMemoizationArgs(
|
||||
const maybeDep = sidemap.maybeDeps.get(dep.identifier.id);
|
||||
if (maybeDep == null) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
||||
description: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
CompilerDiagnostic.fromCode(
|
||||
ErrorCode.COMPLEX_MANUAL_MEMO_DEPENDENCY_LIST_ENTRY,
|
||||
).withDetail({
|
||||
kind: 'error',
|
||||
loc: dep.loc,
|
||||
message: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
||||
@@ -457,16 +445,16 @@ export function dropManualMemoization(
|
||||
if (funcToCheck !== undefined && funcToCheck.loweredFunc.func) {
|
||||
if (!hasNonVoidReturn(funcToCheck.loweredFunc.func)) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'useMemo() callbacks must return a value',
|
||||
description: `This ${
|
||||
manualMemo.loadInstr.value.kind === 'PropertyLoad'
|
||||
? 'React.useMemo'
|
||||
: 'useMemo'
|
||||
} callback doesn't return a value. useMemo is for computing and caching values, not for arbitrary side effects.`,
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
CompilerDiagnostic.fromCode(
|
||||
ErrorCode.INVALID_USE_MEMO_CALLBACK_RETURN,
|
||||
{
|
||||
description: `This ${
|
||||
manualMemo.loadInstr.value.kind === 'PropertyLoad'
|
||||
? 'React.useMemo'
|
||||
: 'useMemo'
|
||||
} callback doesn't return a value. useMemo is for computing and caching values, not for arbitrary side effects.`,
|
||||
},
|
||||
).withDetail({
|
||||
kind: 'error',
|
||||
loc: instr.value.loc,
|
||||
message: 'useMemo() callbacks must return a value',
|
||||
@@ -497,12 +485,9 @@ export function dropManualMemoization(
|
||||
*/
|
||||
if (!sidemap.functions.has(fnPlace.identifier.id)) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: `Expected the first argument to be an inline function expression`,
|
||||
description: `Expected the first argument to be an inline function expression`,
|
||||
suggestions: [],
|
||||
}).withDetail({
|
||||
CompilerDiagnostic.fromCode(
|
||||
ErrorCode.DYNAMIC_MANUAL_MEMO_CALLBACK,
|
||||
).withDetail({
|
||||
kind: 'error',
|
||||
loc: fnPlace.loc,
|
||||
message: `Expected the first argument to be an inline function expression`,
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
isRefOrRefValue,
|
||||
} from '../HIR';
|
||||
import {eachInstructionOperand, eachTerminalOperand} from '../HIR/visitors';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
import {assertExhaustive} from '../Utils/utils';
|
||||
|
||||
interface State {
|
||||
@@ -62,22 +63,20 @@ function inferOperandEffect(state: State, place: Place): null | FunctionEffect {
|
||||
// We ignore mutations of primitives since this is not a React-specific problem
|
||||
value.kind !== ValueKind.Primitive
|
||||
) {
|
||||
let reason = getWriteErrorReason(value);
|
||||
let errorCode = getWriteErrorReason(value);
|
||||
return {
|
||||
kind:
|
||||
value.reason.size === 1 && value.reason.has(ValueReason.Global)
|
||||
? 'GlobalMutation'
|
||||
: 'ReactMutation',
|
||||
error: {
|
||||
reason,
|
||||
errorCode,
|
||||
description:
|
||||
place.identifier.name !== null &&
|
||||
place.identifier.name.kind === 'named'
|
||||
? `Found mutation of \`${place.identifier.name.value}\``
|
||||
: null,
|
||||
loc: place.loc,
|
||||
suggestions: null,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -266,11 +265,8 @@ export function inferInstructionFunctionEffects(
|
||||
functionEffects.push({
|
||||
kind: 'GlobalMutation',
|
||||
error: {
|
||||
reason:
|
||||
'Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)',
|
||||
errorCode: ErrorCode.INVALID_WRITE_GLOBAL,
|
||||
loc: instr.loc,
|
||||
suggestions: null,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
},
|
||||
});
|
||||
break;
|
||||
@@ -324,28 +320,28 @@ function isEffectSafeOutsideRender(effect: FunctionEffect): boolean {
|
||||
return effect.kind === 'GlobalMutation';
|
||||
}
|
||||
|
||||
export function getWriteErrorReason(abstractValue: AbstractValue): string {
|
||||
export function getWriteErrorReason(abstractValue: AbstractValue): ErrorCode {
|
||||
if (abstractValue.reason.has(ValueReason.Global)) {
|
||||
return 'Modifying a variable defined outside a component or hook is not allowed. Consider using an effect';
|
||||
return ErrorCode.INVALID_WRITE_GLOBAL;
|
||||
} else if (abstractValue.reason.has(ValueReason.JsxCaptured)) {
|
||||
return 'Modifying a value used previously in JSX is not allowed. Consider moving the modification before the JSX';
|
||||
return ErrorCode.INVALID_WRITE_FROZEN_VALUE_JSX;
|
||||
} else if (abstractValue.reason.has(ValueReason.Context)) {
|
||||
return `Modifying a value returned from 'useContext()' is not allowed.`;
|
||||
return ErrorCode.INVALID_WRITE_IMMUTABLE_VALUE_USE_CONTEXT;
|
||||
} else if (abstractValue.reason.has(ValueReason.KnownReturnSignature)) {
|
||||
return 'Modifying a value returned from a function whose return value should not be mutated';
|
||||
return ErrorCode.INVALID_WRITE_IMMUTABLE_VALUE_KNOWN_SIGNATURE;
|
||||
} else if (abstractValue.reason.has(ValueReason.ReactiveFunctionArgument)) {
|
||||
return 'Modifying component props or hook arguments is not allowed. Consider using a local variable instead';
|
||||
return ErrorCode.INVALID_WRITE_IMMUTABLE_ARGS;
|
||||
} else if (abstractValue.reason.has(ValueReason.State)) {
|
||||
return "Modifying a value returned from 'useState()', which should not be modified directly. Use the setter function to update instead";
|
||||
return ErrorCode.INVALID_WRITE_STATE;
|
||||
} else if (abstractValue.reason.has(ValueReason.ReducerState)) {
|
||||
return "Modifying a value returned from 'useReducer()', which should not be modified directly. Use the dispatch function to update instead";
|
||||
return ErrorCode.INVALID_WRITE_REDUCER_STATE;
|
||||
} else if (abstractValue.reason.has(ValueReason.Effect)) {
|
||||
return 'Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect()';
|
||||
return ErrorCode.INVALID_WRITE_EFFECT_DEPENDENCY;
|
||||
} else if (abstractValue.reason.has(ValueReason.HookCaptured)) {
|
||||
return 'Modifying a value previously passed as an argument to a hook is not allowed. Consider moving the modification before calling the hook';
|
||||
return ErrorCode.INVALID_WRITE_HOOK_CAPTURED;
|
||||
} else if (abstractValue.reason.has(ValueReason.HookReturn)) {
|
||||
return 'Modifying a value returned from a hook is not allowed. Consider moving the modification into the hook where the value is constructed';
|
||||
return ErrorCode.INVALID_WRITE_HOOK_RETURN;
|
||||
} else {
|
||||
return 'This modifies a variable that React considers immutable';
|
||||
return ErrorCode.INVALID_WRITE_GENERIC;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
CompilerDiagnostic,
|
||||
CompilerError,
|
||||
Effect,
|
||||
ErrorSeverity,
|
||||
SourceLocation,
|
||||
ValueKind,
|
||||
} from '..';
|
||||
@@ -69,6 +68,7 @@ import {getWriteErrorReason} from './InferFunctionEffects';
|
||||
import prettyFormat from 'pretty-format';
|
||||
import {createTemporaryPlace} from '../HIR/HIRBuilder';
|
||||
import {AliasingEffect, AliasingSignature, hashEffect} from './AliasingEffects';
|
||||
import {ErrorCode, ErrorCodeDetails} from '../Utils/CompilerErrorCodes';
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
@@ -442,7 +442,7 @@ function applySignature(
|
||||
const value = state.kind(effect.value);
|
||||
switch (value.kind) {
|
||||
case ValueKind.Frozen: {
|
||||
const reason = getWriteErrorReason({
|
||||
const errorCode = getWriteErrorReason({
|
||||
kind: value.kind,
|
||||
reason: value.reason,
|
||||
context: new Set(),
|
||||
@@ -455,10 +455,9 @@ function applySignature(
|
||||
effects.push({
|
||||
kind: 'MutateFrozen',
|
||||
place: effect.value,
|
||||
error: CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'This value cannot be modified',
|
||||
description: `${reason}.`,
|
||||
// TODO: remove ERROR_CODE.INVALID_WRITE and update test fixtures
|
||||
error: CompilerDiagnostic.fromCode(ErrorCode.INVALID_WRITE, {
|
||||
description: ErrorCodeDetails[errorCode].reason,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: effect.value.loc,
|
||||
@@ -1026,22 +1025,20 @@ function applyEffect(
|
||||
const hoistedAccess = context.hoistedContextDeclarations.get(
|
||||
effect.value.identifier.declarationId,
|
||||
);
|
||||
const diagnostic = CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot access variable before it is declared',
|
||||
description: `${variable ?? 'This variable'} is accessed before it is declared, which prevents the earlier access from updating when this value changes over time.`,
|
||||
});
|
||||
const diagnostic = CompilerDiagnostic.fromCode(
|
||||
ErrorCode.INVALID_ACCESS_BEFORE_INIT,
|
||||
);
|
||||
if (hoistedAccess != null && hoistedAccess.loc != effect.value.loc) {
|
||||
diagnostic.withDetail({
|
||||
kind: 'error',
|
||||
loc: hoistedAccess.loc,
|
||||
message: `${variable ?? 'variable'} accessed before it is declared`,
|
||||
message: `${variable ?? 'This variable'} is accessed before it is declared`,
|
||||
});
|
||||
}
|
||||
diagnostic.withDetail({
|
||||
kind: 'error',
|
||||
loc: effect.value.loc,
|
||||
message: `${variable ?? 'variable'} is declared here`,
|
||||
message: `${variable ?? 'This variable'} is declared here`,
|
||||
});
|
||||
|
||||
applyEffect(
|
||||
@@ -1056,7 +1053,7 @@ function applyEffect(
|
||||
effects,
|
||||
);
|
||||
} else {
|
||||
const reason = getWriteErrorReason({
|
||||
const errorCode = getWriteErrorReason({
|
||||
kind: value.kind,
|
||||
reason: value.reason,
|
||||
context: new Set(),
|
||||
@@ -1075,10 +1072,9 @@ function applyEffect(
|
||||
? 'MutateFrozen'
|
||||
: 'MutateGlobal',
|
||||
place: effect.value,
|
||||
error: CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'This value cannot be modified',
|
||||
description: `${reason}.`,
|
||||
// TODO: remove ERROR_CODE.INVALID_WRITE and update test fixtures
|
||||
error: CompilerDiagnostic.fromCode(ErrorCode.INVALID_WRITE, {
|
||||
description: ErrorCodeDetails[errorCode].reason,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: effect.value.loc,
|
||||
@@ -2006,15 +2002,12 @@ function computeSignatureForInstruction(
|
||||
effects.push({
|
||||
kind: 'MutateGlobal',
|
||||
place: value.value,
|
||||
error: CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category:
|
||||
'Cannot reassign variables declared outside of the component/hook',
|
||||
description: `Variable ${variable} is declared outside of the component/hook. Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)`,
|
||||
}).withDetail({
|
||||
error: CompilerDiagnostic.fromCode(
|
||||
ErrorCode.INVALID_WRITE_GLOBAL,
|
||||
).withDetail({
|
||||
kind: 'error',
|
||||
loc: instr.loc,
|
||||
message: `${variable} cannot be reassigned`,
|
||||
message: `${variable} should not be reassigned`,
|
||||
}),
|
||||
});
|
||||
effects.push({kind: 'Assign', from: value.value, into: lvalue});
|
||||
@@ -2105,19 +2098,16 @@ function computeEffectsForLegacySignature(
|
||||
effects.push({
|
||||
kind: 'Impure',
|
||||
place: receiver,
|
||||
error: CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot call impure function during render',
|
||||
description:
|
||||
(signature.canonicalName != null
|
||||
? `\`${signature.canonicalName}\` is an impure function. `
|
||||
: '') +
|
||||
'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)',
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc,
|
||||
message: 'Cannot call impure function',
|
||||
}),
|
||||
error: CompilerDiagnostic.fromCode(ErrorCode.IMPURE_FUNCTIONS).withDetail(
|
||||
{
|
||||
kind: 'error',
|
||||
loc,
|
||||
message:
|
||||
signature.canonicalName != null
|
||||
? `\`${signature.canonicalName}\` is an impure function. `
|
||||
: 'This is an impure function.',
|
||||
},
|
||||
),
|
||||
});
|
||||
}
|
||||
const stores: Array<Place> = [];
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {CompilerError, CompilerErrorDetailOptions} from '../CompilerError';
|
||||
import {CompilerError} from '../CompilerError';
|
||||
import {Environment} from '../HIR';
|
||||
import {
|
||||
AbstractValue,
|
||||
@@ -48,6 +48,7 @@ import {
|
||||
eachTerminalOperand,
|
||||
eachTerminalSuccessor,
|
||||
} from '../HIR/visitors';
|
||||
import {Err, Ok, Result} from '../Utils/Result';
|
||||
import {assertExhaustive, Set_isSuperset} from '../Utils/utils';
|
||||
import {
|
||||
inferTerminalFunctionEffects,
|
||||
@@ -106,7 +107,7 @@ const UndefinedValue: InstructionValue = {
|
||||
export default function inferReferenceEffects(
|
||||
fn: HIRFunction,
|
||||
options: {isFunctionExpression: boolean} = {isFunctionExpression: false},
|
||||
): Array<CompilerErrorDetailOptions> {
|
||||
): Result<void, CompilerError> {
|
||||
/*
|
||||
* Initial state contains function params
|
||||
* TODO: include module declarations here as well
|
||||
@@ -247,10 +248,17 @@ export default function inferReferenceEffects(
|
||||
|
||||
if (options.isFunctionExpression) {
|
||||
fn.effects = functionEffects;
|
||||
return [];
|
||||
} else {
|
||||
return transformFunctionEffectErrors(functionEffects);
|
||||
const errors = transformFunctionEffectErrors(functionEffects);
|
||||
if (errors.length > 0) {
|
||||
const compilerError = new CompilerError();
|
||||
for (const detail of errors) {
|
||||
compilerError.push(detail);
|
||||
}
|
||||
return Err(compilerError);
|
||||
}
|
||||
}
|
||||
return Ok(void 0);
|
||||
}
|
||||
|
||||
type FreezeAction = {values: Set<InstructionValue>; reason: Set<ValueReason>};
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
import {eachInstructionOperand} from '../HIR/visitors';
|
||||
import {printSourceLocationLine} from '../HIR/PrintHIR';
|
||||
import {USE_FIRE_FUNCTION_NAME} from '../HIR/Environment';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
|
||||
/*
|
||||
* TODO(jmbrown):
|
||||
@@ -50,8 +51,6 @@ import {USE_FIRE_FUNCTION_NAME} from '../HIR/Environment';
|
||||
* - React.useEffect calls
|
||||
*/
|
||||
|
||||
const CANNOT_COMPILE_FIRE = 'Cannot compile `fire`';
|
||||
|
||||
export function transformFire(fn: HIRFunction): void {
|
||||
const context = new Context(fn.env);
|
||||
replaceFireFunctions(fn, context);
|
||||
@@ -178,9 +177,7 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void {
|
||||
loc: value.args[1].loc,
|
||||
description:
|
||||
'You must use an array literal for an effect dependency array when that effect uses `fire()`',
|
||||
severity: ErrorSeverity.Invariant,
|
||||
reason: CANNOT_COMPILE_FIRE,
|
||||
suggestions: null,
|
||||
errorCode: ErrorCode.CANNOT_COMPILE_FIRE,
|
||||
});
|
||||
}
|
||||
} else if (value.args.length > 1 && value.args[1].kind === 'Spread') {
|
||||
@@ -188,9 +185,7 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void {
|
||||
loc: value.args[1].place.loc,
|
||||
description:
|
||||
'You must use an array literal for an effect dependency array when that effect uses `fire()`',
|
||||
severity: ErrorSeverity.Invariant,
|
||||
reason: CANNOT_COMPILE_FIRE,
|
||||
suggestions: null,
|
||||
errorCode: ErrorCode.CANNOT_COMPILE_FIRE,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -243,11 +238,9 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void {
|
||||
} else {
|
||||
context.pushError({
|
||||
loc: value.loc,
|
||||
errorCode: ErrorCode.CANNOT_COMPILE_FIRE,
|
||||
description:
|
||||
'`fire()` can only receive a function call such as `fire(fn(a,b)). Method calls and other expressions are not allowed',
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: CANNOT_COMPILE_FIRE,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -262,10 +255,8 @@ function replaceFireFunctions(fn: HIRFunction, context: Context): void {
|
||||
}
|
||||
context.pushError({
|
||||
loc: value.loc,
|
||||
errorCode: ErrorCode.CANNOT_COMPILE_FIRE,
|
||||
description,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: CANNOT_COMPILE_FIRE,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
} else if (value.kind === 'CallExpression') {
|
||||
@@ -394,9 +385,7 @@ function ensureNoRemainingCalleeCaptures(
|
||||
description: `All uses of ${calleeName} must be either used with a fire() call in \
|
||||
this effect or not used with a fire() call at all. ${calleeName} was used with fire() on line \
|
||||
${printSourceLocationLine(calleeInfo.fireLoc)} in this effect`,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: CANNOT_COMPILE_FIRE,
|
||||
suggestions: null,
|
||||
errorCode: ErrorCode.CANNOT_COMPILE_FIRE,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -411,9 +400,7 @@ function ensureNoMoreFireUses(fn: HIRFunction, context: Context): void {
|
||||
context.pushError({
|
||||
loc: place.identifier.loc,
|
||||
description: 'Cannot use `fire` outside of a useEffect function',
|
||||
severity: ErrorSeverity.Invariant,
|
||||
reason: CANNOT_COMPILE_FIRE,
|
||||
suggestions: null,
|
||||
errorCode: ErrorCode.CANNOT_COMPILE_FIRE,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,624 @@
|
||||
import {ErrorSeverity} from './CompilerErrorSeverity';
|
||||
|
||||
export enum LinterCategory {
|
||||
RULES_OF_HOOKS = 'rules-of-hooks',
|
||||
IMPURE_FUNCTIONS = 'impure-functions',
|
||||
JSX_IN_TRY = 'jsx-in-try',
|
||||
NO_REF_ACCESS_IN_RENDER = 'no-ref-access-in-render',
|
||||
VALIDATE_MANUAL_MEMO = 'validate-manual-memo',
|
||||
DYNAMIC_MANUAL_MEMO = 'dynamic-manual-memo',
|
||||
|
||||
EXHAUSTIVE_DEPS = 'exhaustive-deps',
|
||||
MEMOIZED_DEPENDENCIES = 'memoized-dependencies', // not real!
|
||||
INVALID_WRITE = 'invalid-write',
|
||||
CAPITALIZED_CALLS = 'capitalized-calls',
|
||||
STATIC_COMPONENTS = 'static-components',
|
||||
NO_SET_STATE_IN_RENDER = 'no-set-state-in-render',
|
||||
NO_SET_STATE_IN_EFFECTS = 'no-set-state-in-effects',
|
||||
UNNECESSARY_EFFECTS = 'unnecessary-effects',
|
||||
|
||||
UNSUPPORTED_SYNTAX = 'unsupported-syntax',
|
||||
|
||||
TODO_SYNTAX = 'todo-syntax',
|
||||
|
||||
COMPILER_CONFIG = 'compiler-config',
|
||||
}
|
||||
|
||||
export enum ErrorCode {
|
||||
HOOK_CALL_STATIC,
|
||||
HOOK_INVALID_REFERENCE,
|
||||
HOOK_CALL_REACTIVE,
|
||||
HOOK_CALL_NOT_TOP_LEVEL,
|
||||
IMPURE_FUNCTIONS,
|
||||
JSX_IN_TRY,
|
||||
NO_REF_ACCESS_IN_RENDER,
|
||||
WRITE_AFTER_RENDER,
|
||||
REASSIGN_AFTER_RENDER,
|
||||
REASSIGN_IN_ASYNC,
|
||||
INVALID_WRITE,
|
||||
CAPITALIZED_CALLS,
|
||||
STATIC_COMPONENTS,
|
||||
INVALID_USE_MEMO_CALLBACK_RETURN,
|
||||
INVALID_USE_MEMO_CALLBACK_PARAMETERS,
|
||||
INVALID_USE_MEMO_CALLBACK_ASYNC,
|
||||
INVALID_USE_MEMO_NO_ARG0,
|
||||
INVALID_USE_CALLBACK_NO_ARG0,
|
||||
DYNAMIC_MANUAL_MEMO_CALLBACK,
|
||||
DYNAMIC_USE_MEMO_SPREAD_ARGUMENT,
|
||||
DYNAMIC_USE_CALLBACK_SPREAD_ARGUMENT,
|
||||
DYNAMIC_MANUAL_MEMO_DEPENDENCY_LIST,
|
||||
COMPLEX_MANUAL_MEMO_DEPENDENCY_LIST_ENTRY,
|
||||
|
||||
INVALID_SET_STATE_IN_RENDER,
|
||||
INVALID_SET_STATE_IN_MEMO,
|
||||
INVALID_SET_STATE_IN_EFFECTS,
|
||||
|
||||
NO_DERIVED_COMPUTATIONS_IN_EFFECTS,
|
||||
|
||||
DYNAMIC_GATING_IS_NOT_IDENTIFIER,
|
||||
DYNAMIC_GATING_MULTIPLE_DIRECTIVES,
|
||||
FILENAME_NOT_SET,
|
||||
|
||||
INVALID_WRITE_GLOBAL,
|
||||
INVALID_WRITE_FROZEN_VALUE_JSX,
|
||||
INVALID_WRITE_IMMUTABLE_VALUE_USE_CONTEXT,
|
||||
INVALID_WRITE_IMMUTABLE_VALUE_KNOWN_SIGNATURE,
|
||||
INVALID_WRITE_IMMUTABLE_ARGS,
|
||||
INVALID_WRITE_STATE,
|
||||
INVALID_WRITE_REDUCER_STATE,
|
||||
INVALID_WRITE_EFFECT_DEPENDENCY,
|
||||
INVALID_WRITE_HOOK_CAPTURED,
|
||||
INVALID_WRITE_HOOK_RETURN,
|
||||
INVALID_ACCESS_BEFORE_INIT,
|
||||
INVALID_WRITE_GENERIC,
|
||||
|
||||
MEMOIZED_EFFECT_DEPENDENCIES,
|
||||
|
||||
INVALID_SYNTAX_MULTIPLE_DEFAULTS,
|
||||
INVALID_SYNTAX_REASSIGNED_CONST,
|
||||
INVALID_SYNTAX_BAD_VARIABLE_DECL,
|
||||
UNSUPPORTED_WITH,
|
||||
UNSUPPORTED_INNER_CLASS,
|
||||
INVALID_IMPORT_EXPORT,
|
||||
INVALID_TS_NAMESPACE,
|
||||
UNSUPPORTED_NEW_EXPRESSION,
|
||||
UNSUPPORTED_EMPTY_SEQUENCE_EXPRESSION,
|
||||
INVALID_QUASI_LENGTHS,
|
||||
INVALID_SYNTAX_DELETE_EXPRESSION,
|
||||
UNSUPPORTED_THROW_EXPRESSION,
|
||||
INVALID_JSX_NAMESPACED_NAME,
|
||||
UNSUPPORTED_EVAL,
|
||||
INVALID_SYNTAX_RESERVED_VARIABLE_NAME,
|
||||
INVALID_JAVASCRIPT_AST,
|
||||
BAILOUT_ESLINT_SUPPRESSION,
|
||||
BAILOUT_FLOW_SUPPRESSION,
|
||||
MANUAL_MEMO_MUTATED_LATER,
|
||||
MANUAL_MEMO_REMOVED,
|
||||
MANUAL_MEMO_DEPENDENCIES_CONFLICT,
|
||||
|
||||
CANNOT_COMPILE_FIRE,
|
||||
DID_NOT_INFER_DEPS,
|
||||
|
||||
/** Todo syntax */
|
||||
TODO_CONFLICTING_FBT_IDENTIFIER,
|
||||
TODO_DUPLICATE_FBT_TAGS,
|
||||
UNKNOWN_FUNCTION_PARAMETERS,
|
||||
}
|
||||
|
||||
type ErrorCodeType = {
|
||||
code: ErrorCode;
|
||||
description?: string;
|
||||
severity: ErrorSeverity;
|
||||
reason: string;
|
||||
linterCategory: LinterCategory | null;
|
||||
};
|
||||
|
||||
export const ErrorCodeDetails: Record<ErrorCode, ErrorCodeType> = {
|
||||
[ErrorCode.HOOK_CALL_STATIC]: {
|
||||
code: ErrorCode.HOOK_CALL_STATIC,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)',
|
||||
linterCategory: LinterCategory.RULES_OF_HOOKS,
|
||||
},
|
||||
[ErrorCode.HOOK_INVALID_REFERENCE]: {
|
||||
code: ErrorCode.HOOK_INVALID_REFERENCE,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values',
|
||||
linterCategory: LinterCategory.RULES_OF_HOOKS,
|
||||
},
|
||||
[ErrorCode.HOOK_CALL_REACTIVE]: {
|
||||
code: ErrorCode.HOOK_CALL_REACTIVE,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks',
|
||||
linterCategory: LinterCategory.RULES_OF_HOOKS,
|
||||
},
|
||||
[ErrorCode.HOOK_CALL_NOT_TOP_LEVEL]: {
|
||||
code: ErrorCode.HOOK_CALL_NOT_TOP_LEVEL,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)',
|
||||
linterCategory: LinterCategory.RULES_OF_HOOKS,
|
||||
},
|
||||
[ErrorCode.IMPURE_FUNCTIONS]: {
|
||||
code: ErrorCode.IMPURE_FUNCTIONS,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Cannot call impure functions during render',
|
||||
description:
|
||||
'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)',
|
||||
linterCategory: LinterCategory.IMPURE_FUNCTIONS,
|
||||
},
|
||||
[ErrorCode.JSX_IN_TRY]: {
|
||||
code: ErrorCode.JSX_IN_TRY,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: '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)`,
|
||||
linterCategory: LinterCategory.JSX_IN_TRY,
|
||||
},
|
||||
[ErrorCode.NO_REF_ACCESS_IN_RENDER]: {
|
||||
code: ErrorCode.NO_REF_ACCESS_IN_RENDER,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Cannot access refs during render',
|
||||
description:
|
||||
'React refs are values that are not needed for rendering. Refs should only be accessed ' +
|
||||
'outside of render, such as in event handlers or effects. ' +
|
||||
'Accessing a ref value (the `current` property) during render can cause your component ' +
|
||||
'not to update as expected (https://react.dev/reference/react/useRef)',
|
||||
linterCategory: LinterCategory.NO_REF_ACCESS_IN_RENDER,
|
||||
},
|
||||
[ErrorCode.INVALID_SET_STATE_IN_EFFECTS]: {
|
||||
code: ErrorCode.INVALID_SET_STATE_IN_EFFECTS,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Calling setState synchronously within an effect can trigger cascading renders',
|
||||
description:
|
||||
'Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. ' +
|
||||
'In general, the body of an effect should do one or both of the following:\n' +
|
||||
'* Update external systems with the latest state from React.\n' +
|
||||
'* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\n' +
|
||||
'Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. ' +
|
||||
'(https://react.dev/learn/you-might-not-need-an-effect)',
|
||||
linterCategory: LinterCategory.NO_SET_STATE_IN_EFFECTS,
|
||||
},
|
||||
[ErrorCode.WRITE_AFTER_RENDER]: {
|
||||
code: ErrorCode.WRITE_AFTER_RENDER,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Cannot modify local variables after render completes',
|
||||
description: `This argument is a function which may reassign or mutate a variable after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead`,
|
||||
linterCategory: LinterCategory.INVALID_WRITE,
|
||||
},
|
||||
[ErrorCode.REASSIGN_AFTER_RENDER]: {
|
||||
code: ErrorCode.REASSIGN_AFTER_RENDER,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Cannot reassign local variables after render completes',
|
||||
description: `Reassigning local variables after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead`,
|
||||
linterCategory: LinterCategory.INVALID_WRITE,
|
||||
},
|
||||
[ErrorCode.REASSIGN_IN_ASYNC]: {
|
||||
code: ErrorCode.REASSIGN_IN_ASYNC,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Cannot reassign variable in async function',
|
||||
description:
|
||||
'Reassigning a variable in an async function can cause inconsistent behavior on subsequent renders. Consider using state instead',
|
||||
linterCategory: LinterCategory.INVALID_WRITE,
|
||||
},
|
||||
[ErrorCode.INVALID_WRITE]: {
|
||||
code: ErrorCode.INVALID_WRITE,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'This value cannot be modified',
|
||||
linterCategory: LinterCategory.INVALID_WRITE,
|
||||
},
|
||||
[ErrorCode.CAPITALIZED_CALLS]: {
|
||||
code: ErrorCode.CAPITALIZED_CALLS,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Capitalized functions are reserved for components, which must be invoked with JSX. If this is a component, render it with JSX. Otherwise, ensure that it has no hook calls and rename it to begin with a lowercase letter. Alternatively, if you know for a fact that this function is not a component, you can allowlist it via the compiler config',
|
||||
linterCategory: LinterCategory.CAPITALIZED_CALLS,
|
||||
},
|
||||
[ErrorCode.STATIC_COMPONENTS]: {
|
||||
code: ErrorCode.STATIC_COMPONENTS,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Cannot create components during render',
|
||||
linterCategory: LinterCategory.STATIC_COMPONENTS,
|
||||
},
|
||||
[ErrorCode.INVALID_USE_MEMO_NO_ARG0]: {
|
||||
code: ErrorCode.INVALID_USE_MEMO_NO_ARG0,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: `Expected a callback function to be passed to useMemo`,
|
||||
linterCategory: LinterCategory.VALIDATE_MANUAL_MEMO,
|
||||
},
|
||||
[ErrorCode.INVALID_USE_CALLBACK_NO_ARG0]: {
|
||||
code: ErrorCode.INVALID_USE_CALLBACK_NO_ARG0,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: `Expected a callback function to be passed to useCallback`,
|
||||
linterCategory: LinterCategory.VALIDATE_MANUAL_MEMO,
|
||||
},
|
||||
[ErrorCode.DYNAMIC_USE_MEMO_SPREAD_ARGUMENT]: {
|
||||
code: ErrorCode.DYNAMIC_USE_MEMO_SPREAD_ARGUMENT,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Unexpected spread argument to useMemo',
|
||||
linterCategory: LinterCategory.DYNAMIC_MANUAL_MEMO,
|
||||
},
|
||||
|
||||
[ErrorCode.DYNAMIC_USE_CALLBACK_SPREAD_ARGUMENT]: {
|
||||
code: ErrorCode.DYNAMIC_USE_CALLBACK_SPREAD_ARGUMENT,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Unexpected spread argument to useCallback',
|
||||
linterCategory: LinterCategory.DYNAMIC_MANUAL_MEMO,
|
||||
},
|
||||
[ErrorCode.INVALID_USE_MEMO_CALLBACK_PARAMETERS]: {
|
||||
code: ErrorCode.INVALID_USE_MEMO_CALLBACK_PARAMETERS,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: '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',
|
||||
linterCategory: LinterCategory.VALIDATE_MANUAL_MEMO,
|
||||
},
|
||||
[ErrorCode.INVALID_USE_MEMO_CALLBACK_ASYNC]: {
|
||||
code: ErrorCode.INVALID_USE_MEMO_CALLBACK_ASYNC,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'useMemo() callbacks may not be async or generator functions',
|
||||
description:
|
||||
'useMemo() callbacks are called once and must synchronously return a value',
|
||||
linterCategory: LinterCategory.VALIDATE_MANUAL_MEMO,
|
||||
},
|
||||
[ErrorCode.INVALID_USE_MEMO_CALLBACK_RETURN]: {
|
||||
code: ErrorCode.INVALID_USE_MEMO_CALLBACK_RETURN,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'useMemo() callbacks must return a value',
|
||||
linterCategory: LinterCategory.VALIDATE_MANUAL_MEMO,
|
||||
},
|
||||
[ErrorCode.DYNAMIC_MANUAL_MEMO_CALLBACK]: {
|
||||
code: ErrorCode.DYNAMIC_MANUAL_MEMO_CALLBACK,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: `Expected the first argument to be an inline function expression`,
|
||||
linterCategory: LinterCategory.DYNAMIC_MANUAL_MEMO,
|
||||
},
|
||||
[ErrorCode.DYNAMIC_MANUAL_MEMO_DEPENDENCY_LIST]: {
|
||||
code: ErrorCode.DYNAMIC_MANUAL_MEMO_DEPENDENCY_LIST,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: `Expected the dependency list of useMemo or useCallback to be an array literal`,
|
||||
linterCategory: LinterCategory.DYNAMIC_MANUAL_MEMO,
|
||||
},
|
||||
[ErrorCode.COMPLEX_MANUAL_MEMO_DEPENDENCY_LIST_ENTRY]: {
|
||||
code: ErrorCode.COMPLEX_MANUAL_MEMO_DEPENDENCY_LIST_ENTRY,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
||||
linterCategory: LinterCategory.DYNAMIC_MANUAL_MEMO,
|
||||
},
|
||||
[ErrorCode.INVALID_SET_STATE_IN_RENDER]: {
|
||||
code: ErrorCode.INVALID_SET_STATE_IN_RENDER,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Calling setState during render may trigger an infinite loop',
|
||||
description:
|
||||
'Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState)',
|
||||
linterCategory: LinterCategory.NO_SET_STATE_IN_RENDER,
|
||||
},
|
||||
[ErrorCode.INVALID_SET_STATE_IN_MEMO]: {
|
||||
code: ErrorCode.INVALID_SET_STATE_IN_MEMO,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Calling setState from useMemo may trigger an infinite loop',
|
||||
description:
|
||||
'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)',
|
||||
linterCategory: LinterCategory.NO_SET_STATE_IN_RENDER,
|
||||
},
|
||||
[ErrorCode.NO_DERIVED_COMPUTATIONS_IN_EFFECTS]: {
|
||||
code: ErrorCode.NO_DERIVED_COMPUTATIONS_IN_EFFECTS,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)',
|
||||
linterCategory: LinterCategory.UNNECESSARY_EFFECTS,
|
||||
},
|
||||
|
||||
/** Invalid writes */
|
||||
[ErrorCode.INVALID_WRITE_GLOBAL]: {
|
||||
code: ErrorCode.INVALID_WRITE_GLOBAL,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Cannot reassign variables declared outside of the component/hook',
|
||||
description:
|
||||
'Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)',
|
||||
linterCategory: LinterCategory.INVALID_WRITE,
|
||||
},
|
||||
[ErrorCode.INVALID_WRITE_FROZEN_VALUE_JSX]: {
|
||||
code: ErrorCode.INVALID_WRITE_FROZEN_VALUE_JSX,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Modifying a value used previously in JSX is not allowed. Consider moving the modification before the JSX',
|
||||
linterCategory: LinterCategory.INVALID_WRITE,
|
||||
},
|
||||
[ErrorCode.INVALID_WRITE_IMMUTABLE_VALUE_USE_CONTEXT]: {
|
||||
code: ErrorCode.INVALID_WRITE_IMMUTABLE_VALUE_USE_CONTEXT,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: `Modifying a value returned from 'useContext()' is not allowed`,
|
||||
linterCategory: LinterCategory.INVALID_WRITE,
|
||||
},
|
||||
[ErrorCode.INVALID_WRITE_IMMUTABLE_VALUE_KNOWN_SIGNATURE]: {
|
||||
code: ErrorCode.INVALID_WRITE_IMMUTABLE_VALUE_KNOWN_SIGNATURE,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Modifying a value returned from a function whose return value should not be mutated',
|
||||
linterCategory: LinterCategory.INVALID_WRITE,
|
||||
},
|
||||
[ErrorCode.INVALID_WRITE_IMMUTABLE_ARGS]: {
|
||||
code: ErrorCode.INVALID_WRITE_IMMUTABLE_ARGS,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Modifying component props or hook arguments is not allowed. Consider using a local variable instead',
|
||||
linterCategory: LinterCategory.INVALID_WRITE,
|
||||
},
|
||||
[ErrorCode.INVALID_WRITE_STATE]: {
|
||||
code: ErrorCode.INVALID_WRITE_STATE,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
"Modifying a value returned from 'useState()', which should not be modified directly. Use the setter function to update instead",
|
||||
linterCategory: LinterCategory.INVALID_WRITE,
|
||||
},
|
||||
[ErrorCode.INVALID_WRITE_REDUCER_STATE]: {
|
||||
code: ErrorCode.INVALID_WRITE_REDUCER_STATE,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
"Modifying a value returned from 'useReducer()', which should not be modified directly. Use the dispatch function to update instead",
|
||||
linterCategory: LinterCategory.INVALID_WRITE,
|
||||
},
|
||||
[ErrorCode.INVALID_WRITE_EFFECT_DEPENDENCY]: {
|
||||
code: ErrorCode.INVALID_WRITE_EFFECT_DEPENDENCY,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect()',
|
||||
linterCategory: LinterCategory.INVALID_WRITE,
|
||||
},
|
||||
[ErrorCode.INVALID_WRITE_HOOK_CAPTURED]: {
|
||||
code: ErrorCode.INVALID_WRITE_HOOK_CAPTURED,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Modifying a value previously passed as an argument to a hook is not allowed. Consider moving the modification before calling the hook',
|
||||
linterCategory: LinterCategory.INVALID_WRITE,
|
||||
},
|
||||
[ErrorCode.INVALID_WRITE_HOOK_RETURN]: {
|
||||
code: ErrorCode.INVALID_WRITE_HOOK_RETURN,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Modifying a value returned from a hook is not allowed. Consider moving the modification into the hook where the value is constructed',
|
||||
linterCategory: LinterCategory.INVALID_WRITE,
|
||||
},
|
||||
[ErrorCode.INVALID_ACCESS_BEFORE_INIT]: {
|
||||
code: ErrorCode.INVALID_ACCESS_BEFORE_INIT,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Cannot access variable before it is declared',
|
||||
description: `Reading a variable before it is initialized will prevent the earlier access from updating when this value changes over time. Instead, move the variable access to after it has been initialized`,
|
||||
linterCategory: LinterCategory.INVALID_WRITE,
|
||||
},
|
||||
[ErrorCode.INVALID_WRITE_GENERIC]: {
|
||||
code: ErrorCode.INVALID_WRITE_GENERIC,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'This modifies a variable that React considers immutable',
|
||||
linterCategory: LinterCategory.INVALID_WRITE,
|
||||
},
|
||||
|
||||
/** Compiler Config */
|
||||
[ErrorCode.DYNAMIC_GATING_IS_NOT_IDENTIFIER]: {
|
||||
code: ErrorCode.DYNAMIC_GATING_IS_NOT_IDENTIFIER,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Dynamic gating directive is not a valid JavaScript identifier',
|
||||
linterCategory: LinterCategory.COMPILER_CONFIG,
|
||||
},
|
||||
[ErrorCode.DYNAMIC_GATING_MULTIPLE_DIRECTIVES]: {
|
||||
code: ErrorCode.DYNAMIC_GATING_MULTIPLE_DIRECTIVES,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason: 'Expected a single dynamic gating directive',
|
||||
linterCategory: LinterCategory.COMPILER_CONFIG,
|
||||
},
|
||||
[ErrorCode.FILENAME_NOT_SET]: {
|
||||
code: ErrorCode.FILENAME_NOT_SET,
|
||||
severity: ErrorSeverity.InvalidConfig,
|
||||
reason: `Expected a filename but found none`,
|
||||
description:
|
||||
"When the 'sources' config options is specified, the React compiler will only compile files with a name",
|
||||
linterCategory: LinterCategory.COMPILER_CONFIG,
|
||||
},
|
||||
|
||||
/** Effect dependencies */
|
||||
[ErrorCode.MEMOIZED_EFFECT_DEPENDENCIES]: {
|
||||
code: ErrorCode.MEMOIZED_EFFECT_DEPENDENCIES,
|
||||
reason:
|
||||
'React Compiler has skipped optimizing this component because the effect dependencies could not be memoized. Unmemoized effect dependencies can trigger an infinite loop or other unexpected behavior',
|
||||
severity: ErrorSeverity.CannotPreserveMemoization,
|
||||
linterCategory: LinterCategory.MEMOIZED_DEPENDENCIES,
|
||||
},
|
||||
|
||||
/** Invalid / unsupported syntax */
|
||||
[ErrorCode.INVALID_SYNTAX_MULTIPLE_DEFAULTS]: {
|
||||
code: ErrorCode.INVALID_SYNTAX_MULTIPLE_DEFAULTS,
|
||||
reason: `Expected at most one \`default\` branch in a switch statement, this code should have failed to parse`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.INVALID_SYNTAX_REASSIGNED_CONST]: {
|
||||
code: ErrorCode.INVALID_SYNTAX_REASSIGNED_CONST,
|
||||
reason: `Expect \`const\` declaration not to be reassigned`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.INVALID_SYNTAX_BAD_VARIABLE_DECL]: {
|
||||
code: ErrorCode.INVALID_SYNTAX_BAD_VARIABLE_DECL,
|
||||
reason: `Expected variable declaration to be an identifier if no initializer was provided`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.UNSUPPORTED_WITH]: {
|
||||
code: ErrorCode.UNSUPPORTED_WITH,
|
||||
reason: `JavaScript 'with' syntax is not supported`,
|
||||
description: `'with' syntax is considered deprecated and removed from JavaScript standards, consider alternatives`,
|
||||
severity: ErrorSeverity.UnsupportedJS,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.UNSUPPORTED_INNER_CLASS]: {
|
||||
code: ErrorCode.UNSUPPORTED_INNER_CLASS,
|
||||
reason: 'Inline `class` declarations are not supported',
|
||||
description: `Move class declarations outside of components/hooks`,
|
||||
severity: ErrorSeverity.UnsupportedJS,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.INVALID_IMPORT_EXPORT]: {
|
||||
code: ErrorCode.INVALID_IMPORT_EXPORT,
|
||||
reason:
|
||||
'JavaScript `import` and `export` statements may only appear at the top level of a module',
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.INVALID_TS_NAMESPACE]: {
|
||||
code: ErrorCode.INVALID_TS_NAMESPACE,
|
||||
reason:
|
||||
'TypeScript `namespace` statements may only appear at the top level of a module',
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.UNSUPPORTED_NEW_EXPRESSION]: {
|
||||
code: ErrorCode.UNSUPPORTED_NEW_EXPRESSION,
|
||||
reason: `Expected an expression as the \`new\` expression receiver (v8 intrinsics are not supported)`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.UNSUPPORTED_EMPTY_SEQUENCE_EXPRESSION]: {
|
||||
code: ErrorCode.UNSUPPORTED_EMPTY_SEQUENCE_EXPRESSION,
|
||||
reason: `Expected sequence expression to have at least one expression`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.INVALID_QUASI_LENGTHS]: {
|
||||
code: ErrorCode.INVALID_QUASI_LENGTHS,
|
||||
reason: `Unexpected quasi and subexpression lengths in template literal`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.INVALID_SYNTAX_DELETE_EXPRESSION]: {
|
||||
code: ErrorCode.INVALID_SYNTAX_DELETE_EXPRESSION,
|
||||
reason: `Only object properties can be deleted`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.UNSUPPORTED_THROW_EXPRESSION]: {
|
||||
code: ErrorCode.UNSUPPORTED_THROW_EXPRESSION,
|
||||
reason: `Throw expressions are not supported`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.INVALID_JSX_NAMESPACED_NAME]: {
|
||||
code: ErrorCode.INVALID_JSX_NAMESPACED_NAME,
|
||||
reason: `Expected JSXNamespacedName to have no colons in the namespace or name`,
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.UNSUPPORTED_EVAL]: {
|
||||
code: ErrorCode.UNSUPPORTED_EVAL,
|
||||
reason: `The 'eval' function is not supported`,
|
||||
description:
|
||||
'Eval is an anti-pattern in JavaScript, and the code executed cannot be evaluated by React Compiler',
|
||||
severity: ErrorSeverity.UnsupportedJS,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.INVALID_SYNTAX_RESERVED_VARIABLE_NAME]: {
|
||||
code: ErrorCode.INVALID_SYNTAX_RESERVED_VARIABLE_NAME,
|
||||
reason: 'Expected a non-reserved identifier name',
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.INVALID_JAVASCRIPT_AST]: {
|
||||
code: ErrorCode.INVALID_JAVASCRIPT_AST,
|
||||
reason: 'Encountered invalid JavaScript',
|
||||
severity: ErrorSeverity.InvalidJS,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.BAILOUT_ESLINT_SUPPRESSION]: {
|
||||
code: ErrorCode.BAILOUT_ESLINT_SUPPRESSION,
|
||||
reason:
|
||||
'React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled',
|
||||
description:
|
||||
'React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior',
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.MANUAL_MEMO_REMOVED]: {
|
||||
code: ErrorCode.MANUAL_MEMO_REMOVED,
|
||||
reason:
|
||||
'Compilation skipped because existing memoization could not be preserved',
|
||||
description:
|
||||
'React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output',
|
||||
severity: ErrorSeverity.CannotPreserveMemoization,
|
||||
linterCategory: LinterCategory.TODO_SYNTAX,
|
||||
},
|
||||
|
||||
/**
|
||||
* This is left vague as fire is very experimental
|
||||
*/
|
||||
[ErrorCode.CANNOT_COMPILE_FIRE]: {
|
||||
code: ErrorCode.CANNOT_COMPILE_FIRE,
|
||||
reason: 'Cannot compile `fire`',
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.DID_NOT_INFER_DEPS]: {
|
||||
code: ErrorCode.DID_NOT_INFER_DEPS,
|
||||
reason:
|
||||
'Cannot infer dependencies of this effect. This will break your build!',
|
||||
description:
|
||||
'To resolve, either pass a dependency array or fix reported compiler bailout diagnostics',
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
|
||||
[ErrorCode.BAILOUT_FLOW_SUPPRESSION]: {
|
||||
code: ErrorCode.BAILOUT_FLOW_SUPPRESSION,
|
||||
reason:
|
||||
'React Compiler has skipped optimizing this component because one or more React rule violations were reported by Flow',
|
||||
description:
|
||||
'React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior',
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
|
||||
/** Syntax React Compiler may eventually support */
|
||||
[ErrorCode.TODO_CONFLICTING_FBT_IDENTIFIER]: {
|
||||
code: ErrorCode.TODO_CONFLICTING_FBT_IDENTIFIER,
|
||||
reason: 'Support local variables named `fbt`',
|
||||
description:
|
||||
'Local variables named `fbt` may conflict with the fbt plugin and are not yet supported',
|
||||
severity: ErrorSeverity.Todo,
|
||||
linterCategory: LinterCategory.TODO_SYNTAX,
|
||||
},
|
||||
[ErrorCode.TODO_DUPLICATE_FBT_TAGS]: {
|
||||
code: ErrorCode.TODO_DUPLICATE_FBT_TAGS,
|
||||
reason: 'Support duplicate fbt tags',
|
||||
severity: ErrorSeverity.Todo,
|
||||
linterCategory: LinterCategory.TODO_SYNTAX,
|
||||
},
|
||||
[ErrorCode.UNKNOWN_FUNCTION_PARAMETERS]: {
|
||||
code: ErrorCode.UNKNOWN_FUNCTION_PARAMETERS,
|
||||
severity: ErrorSeverity.Todo,
|
||||
reason: 'Currently unsupported function parameter syntax',
|
||||
linterCategory: LinterCategory.TODO_SYNTAX,
|
||||
},
|
||||
[ErrorCode.MANUAL_MEMO_MUTATED_LATER]: {
|
||||
code: ErrorCode.MANUAL_MEMO_MUTATED_LATER,
|
||||
reason:
|
||||
'Compilation skipped because existing memoization could not be preserved',
|
||||
description: [
|
||||
'React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. ',
|
||||
'This dependency may be mutated later, which could cause the value to change unexpectedly',
|
||||
].join(''),
|
||||
severity: ErrorSeverity.CannotPreserveMemoization,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
[ErrorCode.MANUAL_MEMO_DEPENDENCIES_CONFLICT]: {
|
||||
code: ErrorCode.MANUAL_MEMO_DEPENDENCIES_CONFLICT,
|
||||
reason:
|
||||
'Compilation skipped because existing memoization could not be preserved',
|
||||
description:
|
||||
'React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. ' +
|
||||
'The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected',
|
||||
severity: ErrorSeverity.CannotPreserveMemoization,
|
||||
linterCategory: LinterCategory.UNSUPPORTED_SYNTAX,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
export enum ErrorSeverity {
|
||||
/**
|
||||
* Invalid JS syntax, or valid syntax that is semantically invalid which may indicate some
|
||||
* misunderstanding on the user’s part.
|
||||
*/
|
||||
InvalidJS = 'InvalidJS',
|
||||
/**
|
||||
* JS syntax that is not supported and which we do not plan to support. Developers should
|
||||
* rewrite to use supported forms.
|
||||
*/
|
||||
UnsupportedJS = 'UnsupportedJS',
|
||||
/**
|
||||
* Code that breaks the rules of React.
|
||||
*/
|
||||
InvalidReact = 'InvalidReact',
|
||||
/**
|
||||
* Incorrect configuration of the compiler.
|
||||
*/
|
||||
InvalidConfig = 'InvalidConfig',
|
||||
/**
|
||||
* Code that can reasonably occur and that doesn't break any rules, but is unsafe to preserve
|
||||
* memoization.
|
||||
*/
|
||||
CannotPreserveMemoization = 'CannotPreserveMemoization',
|
||||
/**
|
||||
* Unhandled syntax that we don't support yet.
|
||||
*/
|
||||
Todo = 'Todo',
|
||||
/**
|
||||
* An unexpected internal error in the compiler that indicates critical issues that can panic
|
||||
* the compiler.
|
||||
*/
|
||||
Invariant = 'Invariant',
|
||||
}
|
||||
@@ -6,11 +6,7 @@
|
||||
*/
|
||||
|
||||
import * as t from '@babel/types';
|
||||
import {
|
||||
CompilerError,
|
||||
CompilerErrorDetail,
|
||||
ErrorSeverity,
|
||||
} from '../CompilerError';
|
||||
import {CompilerError, CompilerErrorDetail} from '../CompilerError';
|
||||
import {computeUnconditionalBlocks} from '../HIR/ComputeUnconditionalBlocks';
|
||||
import {isHookName} from '../HIR/Environment';
|
||||
import {
|
||||
@@ -27,6 +23,7 @@ import {
|
||||
} from '../HIR/visitors';
|
||||
import {assertExhaustive} from '../Utils/utils';
|
||||
import {Result} from '../Utils/Result';
|
||||
import {ErrorCode, ErrorCodeDetails} from '../Utils/CompilerErrorCodes';
|
||||
|
||||
/**
|
||||
* Represents the possible kinds of value which may be stored at a given Place during
|
||||
@@ -111,8 +108,6 @@ export function validateHooksUsage(
|
||||
// Once a particular hook has a conditional call error, don't report any further issues for this hook
|
||||
setKind(place, Kind.Error);
|
||||
|
||||
const reason =
|
||||
'Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)';
|
||||
const previousError =
|
||||
typeof place.loc !== 'symbol' ? errorsByPlace.get(place.loc) : undefined;
|
||||
|
||||
@@ -120,15 +115,15 @@ export function validateHooksUsage(
|
||||
* In some circumstances such as optional calls, we may first encounter a "hook may not be referenced as normal values" error.
|
||||
* If that same place is also used as a conditional call, upgrade the error to a conditonal hook error
|
||||
*/
|
||||
if (previousError === undefined || previousError.reason !== reason) {
|
||||
if (
|
||||
previousError === undefined ||
|
||||
previousError.reason !==
|
||||
ErrorCodeDetails[ErrorCode.HOOK_CALL_STATIC].reason
|
||||
) {
|
||||
recordError(
|
||||
place.loc,
|
||||
new CompilerErrorDetail({
|
||||
description: null,
|
||||
reason,
|
||||
CompilerErrorDetail.fromCode(ErrorCode.HOOK_CALL_STATIC, {
|
||||
loc: place.loc,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -139,13 +134,8 @@ export function validateHooksUsage(
|
||||
if (previousError === undefined) {
|
||||
recordError(
|
||||
place.loc,
|
||||
new CompilerErrorDetail({
|
||||
description: null,
|
||||
reason:
|
||||
'Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values',
|
||||
CompilerErrorDetail.fromCode(ErrorCode.HOOK_INVALID_REFERENCE, {
|
||||
loc: place.loc,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -156,14 +146,12 @@ export function validateHooksUsage(
|
||||
if (previousError === undefined) {
|
||||
recordError(
|
||||
place.loc,
|
||||
new CompilerErrorDetail({
|
||||
description: null,
|
||||
reason:
|
||||
'Hooks must be the same function on every render, but this value may change over time to a different function. See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks',
|
||||
loc: place.loc,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
}),
|
||||
CompilerErrorDetail.fromCode(
|
||||
ErrorCodeDetails[ErrorCode.HOOK_CALL_REACTIVE].code,
|
||||
{
|
||||
loc: place.loc,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -424,7 +412,7 @@ export function validateHooksUsage(
|
||||
}
|
||||
|
||||
for (const [, error] of errorsByPlace) {
|
||||
errors.push(error);
|
||||
errors.pushErrorDetail(error);
|
||||
}
|
||||
return errors.asResult();
|
||||
}
|
||||
@@ -446,16 +434,10 @@ function visitFunctionExpression(errors: CompilerError, fn: HIRFunction): void {
|
||||
: instr.value.property;
|
||||
const hookKind = getHookKind(fn.env, callee.identifier);
|
||||
if (hookKind != null) {
|
||||
errors.pushErrorDetail(
|
||||
new CompilerErrorDetail({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason:
|
||||
'Hooks must be called at the top level in the body of a function component or custom hook, and may not be called within function expressions. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)',
|
||||
loc: callee.loc,
|
||||
description: `Cannot call ${hookKind === 'Custom' ? 'hook' : hookKind} within a function expression`,
|
||||
suggestions: null,
|
||||
}),
|
||||
);
|
||||
errors.pushErrorCode(ErrorCode.HOOK_CALL_NOT_TOP_LEVEL, {
|
||||
loc: callee.loc,
|
||||
description: `Cannot call ${hookKind === 'Custom' ? 'hook' : hookKind} within a function expression`,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {CompilerDiagnostic, CompilerError, Effect, ErrorSeverity} from '..';
|
||||
import {CompilerDiagnostic, CompilerError, Effect} from '..';
|
||||
import {HIRFunction, IdentifierId, Place} from '../HIR';
|
||||
import {
|
||||
eachInstructionLValue,
|
||||
@@ -13,13 +13,17 @@ import {
|
||||
eachTerminalOperand,
|
||||
} from '../HIR/visitors';
|
||||
import {getFunctionCallSignature} from '../Inference/InferReferenceEffects';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
import {Ok, Result} from '../Utils/Result';
|
||||
|
||||
/**
|
||||
* Validates that local variables cannot be reassigned after render.
|
||||
* This prevents a category of bugs in which a closure captures a
|
||||
* binding from one render but does not update
|
||||
*/
|
||||
export function validateLocalsNotReassignedAfterRender(fn: HIRFunction): void {
|
||||
export function validateLocalsNotReassignedAfterRender(
|
||||
fn: HIRFunction,
|
||||
): Result<void, CompilerError> {
|
||||
const contextVariables = new Set<IdentifierId>();
|
||||
const reassignment = getContextReassignment(
|
||||
fn,
|
||||
@@ -35,18 +39,15 @@ export function validateLocalsNotReassignedAfterRender(fn: HIRFunction): void {
|
||||
? `\`${reassignment.identifier.name.value}\``
|
||||
: 'variable';
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot reassign variable after render completes',
|
||||
description: `Reassigning ${variable} after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead.`,
|
||||
}).withDetail({
|
||||
CompilerDiagnostic.fromCode(ErrorCode.REASSIGN_AFTER_RENDER).withDetail({
|
||||
kind: 'error',
|
||||
loc: reassignment.loc,
|
||||
message: `Cannot reassign ${variable} after render completes`,
|
||||
}),
|
||||
);
|
||||
throw errors;
|
||||
return errors.asResult();
|
||||
}
|
||||
return Ok(undefined);
|
||||
}
|
||||
|
||||
function getContextReassignment(
|
||||
@@ -90,12 +91,9 @@ function getContextReassignment(
|
||||
? `\`${reassignment.identifier.name.value}\``
|
||||
: 'variable';
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot reassign variable in async function',
|
||||
description:
|
||||
'Reassigning a variable in an async function can cause inconsistent behavior on subsequent renders. Consider using state instead',
|
||||
}).withDetail({
|
||||
CompilerDiagnostic.fromCode(
|
||||
ErrorCode.REASSIGN_IN_ASYNC,
|
||||
).withDetail({
|
||||
kind: 'error',
|
||||
loc: reassignment.loc,
|
||||
message: `Cannot reassign ${variable}`,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {CompilerError, ErrorSeverity} from '..';
|
||||
import {CompilerError} from '..';
|
||||
import {
|
||||
Identifier,
|
||||
Instruction,
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
ReactiveFunctionVisitor,
|
||||
visitReactiveFunction,
|
||||
} from '../ReactiveScopes/visitors';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
/**
|
||||
@@ -108,12 +109,8 @@ class Visitor extends ReactiveFunctionVisitor<CompilerError> {
|
||||
isUnmemoized(deps.identifier, this.scopes))
|
||||
) {
|
||||
state.push({
|
||||
reason:
|
||||
'React Compiler has skipped optimizing this component because the effect dependencies could not be memoized. Unmemoized effect dependencies can trigger an infinite loop or other unexpected behavior',
|
||||
description: null,
|
||||
severity: ErrorSeverity.CannotPreserveMemoization,
|
||||
errorCode: ErrorCode.MEMOIZED_EFFECT_DEPENDENCIES,
|
||||
loc: typeof instruction.loc !== 'symbol' ? instruction.loc : null,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {CompilerError, EnvironmentConfig, ErrorSeverity} from '..';
|
||||
import {CompilerError, EnvironmentConfig} from '..';
|
||||
import {HIRFunction, IdentifierId} from '../HIR';
|
||||
import {DEFAULT_GLOBALS} from '../HIR/Globals';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
export function validateNoCapitalizedCalls(
|
||||
@@ -33,8 +34,6 @@ export function validateNoCapitalizedCalls(
|
||||
const errors = new CompilerError();
|
||||
const capitalLoadGlobals = new Map<IdentifierId, string>();
|
||||
const capitalizedProperties = new Map<IdentifierId, string>();
|
||||
const reason =
|
||||
'Capitalized functions are reserved for components, which must be invoked with JSX. If this is a component, render it with JSX. Otherwise, ensure that it has no hook calls and rename it to begin with a lowercase letter. Alternatively, if you know for a fact that this function is not a component, you can allowlist it via the compiler config';
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
for (const {lvalue, value} of block.instructions) {
|
||||
switch (value.kind) {
|
||||
@@ -55,11 +54,9 @@ export function validateNoCapitalizedCalls(
|
||||
const calleeIdentifier = value.callee.identifier.id;
|
||||
const calleeName = capitalLoadGlobals.get(calleeIdentifier);
|
||||
if (calleeName != null) {
|
||||
CompilerError.throwInvalidReact({
|
||||
reason,
|
||||
errors.pushErrorCode(ErrorCode.CAPITALIZED_CALLS, {
|
||||
description: `${calleeName} may be a component.`,
|
||||
loc: value.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
break;
|
||||
@@ -78,12 +75,9 @@ export function validateNoCapitalizedCalls(
|
||||
const propertyIdentifier = value.property.identifier.id;
|
||||
const propertyName = capitalizedProperties.get(propertyIdentifier);
|
||||
if (propertyName != null) {
|
||||
errors.push({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
reason,
|
||||
errors.pushErrorCode(ErrorCode.CAPITALIZED_CALLS, {
|
||||
description: `${propertyName} may be a component.`,
|
||||
loc: value.loc,
|
||||
suggestions: null,
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {CompilerError, ErrorSeverity, SourceLocation} from '..';
|
||||
import {CompilerError, SourceLocation} from '..';
|
||||
import {
|
||||
ArrayExpression,
|
||||
BlockId,
|
||||
@@ -19,6 +19,8 @@ import {
|
||||
eachInstructionValueOperand,
|
||||
eachTerminalOperand,
|
||||
} from '../HIR/visitors';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
/**
|
||||
* Validates that useEffect is not used for derived computations which could/should
|
||||
@@ -43,7 +45,9 @@ import {
|
||||
* const fullName = firstName + ' ' + lastName;
|
||||
* ```
|
||||
*/
|
||||
export function validateNoDerivedComputationsInEffects(fn: HIRFunction): void {
|
||||
export function validateNoDerivedComputationsInEffects(
|
||||
fn: HIRFunction,
|
||||
): Result<void, CompilerError> {
|
||||
const candidateDependencies: Map<IdentifierId, ArrayExpression> = new Map();
|
||||
const functions: Map<IdentifierId, FunctionExpression> = new Map();
|
||||
const locals: Map<IdentifierId, IdentifierId> = new Map();
|
||||
@@ -96,9 +100,7 @@ export function validateNoDerivedComputationsInEffects(fn: HIRFunction): void {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (errors.hasErrors()) {
|
||||
throw errors;
|
||||
}
|
||||
return errors.asResult();
|
||||
}
|
||||
|
||||
function validateEffect(
|
||||
@@ -218,13 +220,6 @@ function validateEffect(
|
||||
}
|
||||
|
||||
for (const loc of setStateLocations) {
|
||||
errors.push({
|
||||
reason:
|
||||
'Values derived from props and state should be calculated during render, not in an effect. (https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state)',
|
||||
description: null,
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
loc,
|
||||
suggestions: null,
|
||||
});
|
||||
errors.pushErrorCode(ErrorCode.NO_DERIVED_COMPUTATIONS_IN_EFFECTS, {loc});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {CompilerDiagnostic, CompilerError, Effect, ErrorSeverity} from '..';
|
||||
import {CompilerDiagnostic, CompilerError, Effect} from '..';
|
||||
import {ErrorCode} from '../CompilerError';
|
||||
import {
|
||||
FunctionEffect,
|
||||
HIRFunction,
|
||||
@@ -65,11 +66,7 @@ export function validateNoFreezingKnownMutableFunctions(
|
||||
? `\`${place.identifier.name.value}\``
|
||||
: 'a local variable';
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot modify local variables after render completes',
|
||||
description: `This argument is a function which may reassign or mutate ${variable} after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.`,
|
||||
})
|
||||
CompilerDiagnostic.fromCode(ErrorCode.WRITE_AFTER_RENDER)
|
||||
.withDetail({
|
||||
kind: 'error',
|
||||
loc: operand.loc,
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {CompilerDiagnostic, CompilerError, ErrorSeverity} from '..';
|
||||
import {CompilerDiagnostic, CompilerError} from '..';
|
||||
import {HIRFunction} from '../HIR';
|
||||
import {getFunctionCallSignature} from '../Inference/InferReferenceEffects';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
/**
|
||||
@@ -35,19 +36,13 @@ export function validateNoImpureFunctionsInRender(
|
||||
);
|
||||
if (signature != null && signature.impure === true) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category: 'Cannot call impure function during render',
|
||||
description:
|
||||
(signature.canonicalName != null
|
||||
? `\`${signature.canonicalName}\` is an impure function. `
|
||||
: '') +
|
||||
'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)',
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
CompilerDiagnostic.fromCode(ErrorCode.IMPURE_FUNCTIONS).withDetail({
|
||||
kind: 'error',
|
||||
loc: callee.loc,
|
||||
message: 'Cannot call impure function',
|
||||
message:
|
||||
signature.canonicalName != null
|
||||
? `\`${signature.canonicalName}\` is an impure function. `
|
||||
: 'This is an impure function.',
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {CompilerDiagnostic, CompilerError, ErrorSeverity} from '..';
|
||||
import {CompilerDiagnostic, CompilerError} from '..';
|
||||
import {BlockId, HIRFunction} from '../HIR';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
import {Result} from '../Utils/Result';
|
||||
import {retainWhere} from '../Utils/utils';
|
||||
|
||||
@@ -35,11 +36,7 @@ export function validateNoJSXInTryStatement(
|
||||
case 'JsxExpression':
|
||||
case 'JsxFragment': {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.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)`,
|
||||
}).withDetail({
|
||||
CompilerDiagnostic.fromCode(ErrorCode.JSX_IN_TRY).withDetail({
|
||||
kind: 'error',
|
||||
loc: value.loc,
|
||||
message: 'Avoid constructing JSX within try/catch',
|
||||
|
||||
@@ -5,11 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
CompilerDiagnostic,
|
||||
CompilerError,
|
||||
ErrorSeverity,
|
||||
} from '../CompilerError';
|
||||
import {CompilerDiagnostic, CompilerError} from '../CompilerError';
|
||||
import {
|
||||
BlockId,
|
||||
HIRFunction,
|
||||
@@ -26,6 +22,7 @@ import {
|
||||
eachPatternOperand,
|
||||
eachTerminalOperand,
|
||||
} from '../HIR/visitors';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
import {Err, Ok, Result} from '../Utils/Result';
|
||||
import {retainWhere} from '../Utils/utils';
|
||||
|
||||
@@ -467,11 +464,9 @@ function validateNoRefAccessInRenderImpl(
|
||||
if (fnType.fn.readRefEffect) {
|
||||
didError = true;
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot access refs during render',
|
||||
description: ERROR_DESCRIPTION,
|
||||
}).withDetail({
|
||||
CompilerDiagnostic.fromCode(
|
||||
ErrorCode.NO_REF_ACCESS_IN_RENDER,
|
||||
).withDetail({
|
||||
kind: 'error',
|
||||
loc: callee.loc,
|
||||
message: `This function accesses a ref value`,
|
||||
@@ -730,15 +725,13 @@ function destructure(
|
||||
function guardCheck(errors: CompilerError, operand: Place, env: Env): void {
|
||||
if (env.get(operand.identifier.id)?.kind === 'Guard') {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot access refs during render',
|
||||
description: ERROR_DESCRIPTION,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: operand.loc,
|
||||
message: `Cannot access ref value during render`,
|
||||
}),
|
||||
CompilerDiagnostic.fromCode(ErrorCode.NO_REF_ACCESS_IN_RENDER).withDetail(
|
||||
{
|
||||
kind: 'error',
|
||||
loc: operand.loc,
|
||||
message: `Cannot access ref value during render`,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -754,15 +747,13 @@ function validateNoRefValueAccess(
|
||||
(type?.kind === 'Structure' && type.fn?.readRefEffect)
|
||||
) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot access refs during render',
|
||||
description: ERROR_DESCRIPTION,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: (type.kind === 'RefValue' && type.loc) || operand.loc,
|
||||
message: `Cannot access ref value during render`,
|
||||
}),
|
||||
CompilerDiagnostic.fromCode(ErrorCode.NO_REF_ACCESS_IN_RENDER).withDetail(
|
||||
{
|
||||
kind: 'error',
|
||||
loc: (type.kind === 'RefValue' && type.loc) || operand.loc,
|
||||
message: `Cannot access ref value during render`,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -780,15 +771,13 @@ function validateNoRefPassedToFunction(
|
||||
(type?.kind === 'Structure' && type.fn?.readRefEffect)
|
||||
) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot access refs during render',
|
||||
description: ERROR_DESCRIPTION,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: (type.kind === 'RefValue' && type.loc) || loc,
|
||||
message: `Passing a ref to a function may read its value during render`,
|
||||
}),
|
||||
CompilerDiagnostic.fromCode(ErrorCode.NO_REF_ACCESS_IN_RENDER).withDetail(
|
||||
{
|
||||
kind: 'error',
|
||||
loc: (type.kind === 'RefValue' && type.loc) || loc,
|
||||
message: `Passing a ref to a function may read its value during render`,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -802,15 +791,13 @@ function validateNoRefUpdate(
|
||||
const type = destructure(env.get(operand.identifier.id));
|
||||
if (type?.kind === 'Ref' || type?.kind === 'RefValue') {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot access refs during render',
|
||||
description: ERROR_DESCRIPTION,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: (type.kind === 'RefValue' && type.loc) || loc,
|
||||
message: `Cannot update ref during render`,
|
||||
}),
|
||||
CompilerDiagnostic.fromCode(ErrorCode.NO_REF_ACCESS_IN_RENDER).withDetail(
|
||||
{
|
||||
kind: 'error',
|
||||
loc: (type.kind === 'RefValue' && type.loc) || loc,
|
||||
message: `Cannot update ref during render`,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -823,21 +810,13 @@ function validateNoDirectRefValueAccess(
|
||||
const type = destructure(env.get(operand.identifier.id));
|
||||
if (type?.kind === 'RefValue') {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot access refs during render',
|
||||
description: ERROR_DESCRIPTION,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc: type.loc ?? operand.loc,
|
||||
message: `Cannot access ref value during render`,
|
||||
}),
|
||||
CompilerDiagnostic.fromCode(ErrorCode.NO_REF_ACCESS_IN_RENDER).withDetail(
|
||||
{
|
||||
kind: 'error',
|
||||
loc: type.loc ?? operand.loc,
|
||||
message: `Cannot access ref value during render`,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const ERROR_DESCRIPTION =
|
||||
'React refs are values that are not needed for rendering. Refs should only be accessed ' +
|
||||
'outside of render, such as in event handlers or effects. ' +
|
||||
'Accessing a ref value (the `current` property) during render can cause your component ' +
|
||||
'not to update as expected (https://react.dev/reference/react/useRef)';
|
||||
|
||||
@@ -5,11 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
CompilerDiagnostic,
|
||||
CompilerError,
|
||||
ErrorSeverity,
|
||||
} from '../CompilerError';
|
||||
import {CompilerDiagnostic, CompilerError} from '../CompilerError';
|
||||
import {
|
||||
HIRFunction,
|
||||
IdentifierId,
|
||||
@@ -20,6 +16,7 @@ import {
|
||||
Place,
|
||||
} from '../HIR';
|
||||
import {eachInstructionValueOperand} from '../HIR/visitors';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
/**
|
||||
@@ -95,19 +92,9 @@ export function validateNoSetStateInEffects(
|
||||
const setState = setStateFunctions.get(arg.identifier.id);
|
||||
if (setState !== undefined) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category:
|
||||
'Calling setState synchronously within an effect can trigger cascading renders',
|
||||
description:
|
||||
'Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. ' +
|
||||
'In general, the body of an effect should do one or both of the following:\n' +
|
||||
'* Update external systems with the latest state from React.\n' +
|
||||
'* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\n' +
|
||||
'Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. ' +
|
||||
'(https://react.dev/learn/you-might-not-need-an-effect)',
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
CompilerDiagnostic.fromCode(
|
||||
ErrorCode.INVALID_SET_STATE_IN_EFFECTS,
|
||||
).withDetail({
|
||||
kind: 'error',
|
||||
loc: setState.loc,
|
||||
message:
|
||||
|
||||
@@ -5,14 +5,11 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
CompilerDiagnostic,
|
||||
CompilerError,
|
||||
ErrorSeverity,
|
||||
} from '../CompilerError';
|
||||
import {CompilerDiagnostic, CompilerError} from '../CompilerError';
|
||||
import {HIRFunction, IdentifierId, isSetStateType} from '../HIR';
|
||||
import {computeUnconditionalBlocks} from '../HIR/ComputeUnconditionalBlocks';
|
||||
import {eachInstructionValueOperand} from '../HIR/visitors';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
/**
|
||||
@@ -127,14 +124,9 @@ function validateNoSetStateInRenderImpl(
|
||||
) {
|
||||
if (activeManualMemoId !== null) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category:
|
||||
'Calling setState from useMemo may trigger an infinite loop',
|
||||
description:
|
||||
'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)',
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
CompilerDiagnostic.fromCode(
|
||||
ErrorCode.INVALID_SET_STATE_IN_MEMO,
|
||||
).withDetail({
|
||||
kind: 'error',
|
||||
loc: callee.loc,
|
||||
message: 'Found setState() within useMemo()',
|
||||
@@ -142,17 +134,12 @@ function validateNoSetStateInRenderImpl(
|
||||
);
|
||||
} else if (unconditionalBlocks.has(block.id)) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
category:
|
||||
'Calling setState during render may trigger an infinite loop',
|
||||
description:
|
||||
'Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState)',
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
CompilerDiagnostic.fromCode(
|
||||
ErrorCode.INVALID_SET_STATE_IN_RENDER,
|
||||
).withDetail({
|
||||
kind: 'error',
|
||||
loc: callee.loc,
|
||||
message: 'Found setState() within useMemo()',
|
||||
message: 'Found setState() call here',
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,11 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
CompilerDiagnostic,
|
||||
CompilerError,
|
||||
ErrorSeverity,
|
||||
} from '../CompilerError';
|
||||
import {CompilerDiagnostic, CompilerError, ErrorCode} from '../CompilerError';
|
||||
import {
|
||||
DeclarationId,
|
||||
Effect,
|
||||
@@ -280,13 +276,8 @@ function validateInferredDep(
|
||||
}
|
||||
}
|
||||
errorState.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.CannotPreserveMemoization,
|
||||
category:
|
||||
'Compilation skipped because existing memoization could not be preserved',
|
||||
description: [
|
||||
'React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. ',
|
||||
'The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. ',
|
||||
CompilerDiagnostic.fromCode(ErrorCode.MANUAL_MEMO_DEPENDENCIES_CONFLICT, {
|
||||
description:
|
||||
DEBUG ||
|
||||
// If the dependency is a named variable then we can report it. Otherwise only print in debug mode
|
||||
(dep.identifier.name != null && dep.identifier.name.kind === 'named')
|
||||
@@ -298,11 +289,8 @@ function validateInferredDep(
|
||||
errorDiagnostic
|
||||
? getCompareDependencyResultDescription(errorDiagnostic)
|
||||
: 'Inferred dependency not present in source'
|
||||
}.`
|
||||
}`
|
||||
: '',
|
||||
]
|
||||
.join('')
|
||||
.trim(),
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
@@ -534,15 +522,9 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
|
||||
!this.prunedScopes.has(identifier.scope.id)
|
||||
) {
|
||||
state.errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.CannotPreserveMemoization,
|
||||
category:
|
||||
'Compilation skipped because existing memoization could not be preserved',
|
||||
description: [
|
||||
'React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. ',
|
||||
'This dependency may be mutated later, which could cause the value to change unexpectedly.',
|
||||
].join(''),
|
||||
}).withDetail({
|
||||
CompilerDiagnostic.fromCode(
|
||||
ErrorCode.MANUAL_MEMO_MUTATED_LATER,
|
||||
).withDetail({
|
||||
kind: 'error',
|
||||
loc,
|
||||
message: 'This dependency may be modified later',
|
||||
@@ -582,18 +564,10 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
|
||||
for (const identifier of decls) {
|
||||
if (isUnmemoized(identifier, this.scopes)) {
|
||||
state.errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.CannotPreserveMemoization,
|
||||
category:
|
||||
'Compilation skipped because existing memoization could not be preserved',
|
||||
description: [
|
||||
'React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output. ',
|
||||
DEBUG
|
||||
? `${printIdentifier(identifier)} was not memoized.`
|
||||
: '',
|
||||
]
|
||||
.join('')
|
||||
.trim(),
|
||||
CompilerDiagnostic.fromCode(ErrorCode.MANUAL_MEMO_REMOVED, {
|
||||
description: DEBUG
|
||||
? `${printIdentifier(identifier)} was not memoized`
|
||||
: '',
|
||||
}).withDetail({
|
||||
kind: 'error',
|
||||
loc,
|
||||
|
||||
@@ -5,12 +5,9 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
CompilerDiagnostic,
|
||||
CompilerError,
|
||||
ErrorSeverity,
|
||||
} from '../CompilerError';
|
||||
import {CompilerDiagnostic, CompilerError} from '../CompilerError';
|
||||
import {HIRFunction, IdentifierId, SourceLocation} from '../HIR';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
/**
|
||||
@@ -64,9 +61,7 @@ export function validateStaticComponents(
|
||||
);
|
||||
if (location != null) {
|
||||
error.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category: 'Cannot create components during render',
|
||||
CompilerDiagnostic.fromCode(ErrorCode.STATIC_COMPONENTS, {
|
||||
description: `Components created during render will reset their state each time they are created. Declare components outside of render. `,
|
||||
})
|
||||
.withDetail({
|
||||
|
||||
@@ -5,12 +5,9 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
CompilerDiagnostic,
|
||||
CompilerError,
|
||||
ErrorSeverity,
|
||||
} from '../CompilerError';
|
||||
import {CompilerDiagnostic, CompilerError} from '../CompilerError';
|
||||
import {FunctionExpression, HIRFunction, IdentifierId} from '../HIR';
|
||||
import {ErrorCode} from '../Utils/CompilerErrorCodes';
|
||||
import {Result} from '../Utils/Result';
|
||||
|
||||
export function validateUseMemo(fn: HIRFunction): Result<void, CompilerError> {
|
||||
@@ -73,13 +70,9 @@ export function validateUseMemo(fn: HIRFunction): Result<void, CompilerError> {
|
||||
? firstParam.loc
|
||||
: firstParam.place.loc;
|
||||
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({
|
||||
CompilerDiagnostic.fromCode(
|
||||
ErrorCode.INVALID_USE_MEMO_CALLBACK_PARAMETERS,
|
||||
).withDetail({
|
||||
kind: 'error',
|
||||
loc,
|
||||
message: 'Callbacks with parameters are not supported',
|
||||
@@ -89,14 +82,9 @@ export function validateUseMemo(fn: HIRFunction): Result<void, CompilerError> {
|
||||
|
||||
if (body.loweredFunc.func.async || body.loweredFunc.func.generator) {
|
||||
errors.pushDiagnostic(
|
||||
CompilerDiagnostic.create({
|
||||
severity: ErrorSeverity.InvalidReact,
|
||||
category:
|
||||
'useMemo() callbacks may not be async or generator functions',
|
||||
description:
|
||||
'useMemo() callbacks are called once and must synchronously return a value.',
|
||||
suggestions: null,
|
||||
}).withDetail({
|
||||
CompilerDiagnostic.fromCode(
|
||||
ErrorCode.INVALID_USE_MEMO_CALLBACK_ASYNC,
|
||||
).withDetail({
|
||||
kind: 'error',
|
||||
loc: body.loc,
|
||||
message: 'Async and generator functions are not supported',
|
||||
|
||||
@@ -19,13 +19,13 @@ Found 1 error:
|
||||
|
||||
Error: Cannot reassign variables declared outside of the component/hook
|
||||
|
||||
Variable `someGlobal` is declared outside of the component/hook. Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)
|
||||
Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render).
|
||||
|
||||
error.assign-global-in-component-tag-function.ts:3:4
|
||||
1 | function Component() {
|
||||
2 | const Foo = () => {
|
||||
> 3 | someGlobal = true;
|
||||
| ^^^^^^^^^^ `someGlobal` cannot be reassigned
|
||||
| ^^^^^^^^^^ `someGlobal` should not be reassigned
|
||||
4 | };
|
||||
5 | return <Foo />;
|
||||
6 | }
|
||||
|
||||
@@ -22,13 +22,13 @@ Found 1 error:
|
||||
|
||||
Error: Cannot reassign variables declared outside of the component/hook
|
||||
|
||||
Variable `someGlobal` is declared outside of the component/hook. Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)
|
||||
Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render).
|
||||
|
||||
error.assign-global-in-jsx-children.ts:3:4
|
||||
1 | function Component() {
|
||||
2 | const foo = () => {
|
||||
> 3 | someGlobal = true;
|
||||
| ^^^^^^^^^^ `someGlobal` cannot be reassigned
|
||||
| ^^^^^^^^^^ `someGlobal` should not be reassigned
|
||||
4 | };
|
||||
5 | // Children are generally access/called during render, so
|
||||
6 | // modifying a global in a children function is almost
|
||||
|
||||
@@ -18,13 +18,15 @@ function Component() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)
|
||||
Error: Cannot reassign variables declared outside of the component/hook
|
||||
|
||||
Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render).
|
||||
|
||||
error.assign-global-in-jsx-spread-attribute.ts:4:4
|
||||
2 | function Component() {
|
||||
3 | const foo = () => {
|
||||
> 4 | someGlobal = true;
|
||||
| ^^^^^^^^^^ Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)
|
||||
| ^^^^^^^^^^ Cannot reassign variables declared outside of the component/hook
|
||||
5 | };
|
||||
6 | return <div {...foo} />;
|
||||
7 | }
|
||||
|
||||
@@ -20,7 +20,7 @@ Found 1 error:
|
||||
|
||||
Error: React Compiler has skipped optimizing this component because one or more React rule violations were reported by Flow
|
||||
|
||||
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `$FlowFixMe[react-rule-hook]`
|
||||
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `$FlowFixMe[react-rule-hook]`.
|
||||
|
||||
error.bailout-on-flow-suppression.ts:4:2
|
||||
2 |
|
||||
|
||||
@@ -23,7 +23,7 @@ Found 2 errors:
|
||||
|
||||
Error: React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled
|
||||
|
||||
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable my-app/react-rule`
|
||||
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable my-app/react-rule`.
|
||||
|
||||
error.bailout-on-suppression-of-custom-rule.ts:3:0
|
||||
1 | // @eslintSuppressionRules:["my-app","react-rule"]
|
||||
@@ -36,7 +36,7 @@ error.bailout-on-suppression-of-custom-rule.ts:3:0
|
||||
|
||||
Error: React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled
|
||||
|
||||
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable-next-line my-app/react-rule`
|
||||
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable-next-line my-app/react-rule`.
|
||||
|
||||
error.bailout-on-suppression-of-custom-rule.ts:7:2
|
||||
5 | 'use forget';
|
||||
|
||||
@@ -40,7 +40,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot modify local variables after render completes
|
||||
|
||||
This argument is a function which may reassign or mutate a local variable after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
This argument is a function which may reassign or mutate a variable 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
|
||||
18 | );
|
||||
|
||||
@@ -18,7 +18,7 @@ Found 1 error:
|
||||
|
||||
Error: Capitalized functions are reserved for components, which must be invoked with JSX. If this is a component, render it with JSX. Otherwise, ensure that it has no hook calls and rename it to begin with a lowercase letter. Alternatively, if you know for a fact that this function is not a component, you can allowlist it via the compiler config
|
||||
|
||||
Bar may be a component..
|
||||
Bar may be a component.
|
||||
|
||||
error.capitalized-function-call-aliased.ts:4:2
|
||||
2 | function Foo() {
|
||||
|
||||
@@ -19,7 +19,7 @@ Found 1 error:
|
||||
|
||||
Error: Capitalized functions are reserved for components, which must be invoked with JSX. If this is a component, render it with JSX. Otherwise, ensure that it has no hook calls and rename it to begin with a lowercase letter. Alternatively, if you know for a fact that this function is not a component, you can allowlist it via the compiler config
|
||||
|
||||
SomeFunc may be a component..
|
||||
SomeFunc may be a component.
|
||||
|
||||
error.capitalized-function-call.ts:3:12
|
||||
1 | // @validateNoCapitalizedCalls
|
||||
|
||||
@@ -19,7 +19,7 @@ Found 1 error:
|
||||
|
||||
Error: Capitalized functions are reserved for components, which must be invoked with JSX. If this is a component, render it with JSX. Otherwise, ensure that it has no hook calls and rename it to begin with a lowercase letter. Alternatively, if you know for a fact that this function is not a component, you can allowlist it via the compiler config
|
||||
|
||||
SomeFunc may be a component..
|
||||
SomeFunc may be a component.
|
||||
|
||||
error.capitalized-method-call.ts:3:12
|
||||
1 | // @validateNoCapitalizedCalls
|
||||
|
||||
@@ -36,7 +36,7 @@ Found 2 errors:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.capture-ref-for-mutation.ts:12:13
|
||||
10 | };
|
||||
@@ -49,7 +49,7 @@ error.capture-ref-for-mutation.ts:12:13
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.capture-ref-for-mutation.ts:15:13
|
||||
13 | };
|
||||
|
||||
@@ -30,9 +30,9 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot reassign variable after render completes
|
||||
Error: Cannot reassign local variables after render completes
|
||||
|
||||
Reassigning `x` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
Reassigning local variables after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
|
||||
error.context-variable-only-chained-assign.ts:10:19
|
||||
8 | };
|
||||
|
||||
@@ -19,9 +19,9 @@ function Component() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot reassign variable after render completes
|
||||
Error: Cannot reassign local variables after render completes
|
||||
|
||||
Reassigning `x` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
Reassigning local variables 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
|
||||
2 | let x = null;
|
||||
|
||||
@@ -17,9 +17,9 @@ function Component() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot reassign variable after render completes
|
||||
Error: Cannot reassign local variables after render completes
|
||||
|
||||
Reassigning `callback` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
Reassigning local variables 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
|
||||
1 | function Component() {
|
||||
|
||||
@@ -24,7 +24,7 @@ Found 2 errors:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.hook-ref-value.ts:5:23
|
||||
3 | function Component(props) {
|
||||
@@ -37,7 +37,7 @@ error.hook-ref-value.ts:5:23
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.hook-ref-value.ts:5:23
|
||||
3 | function Component(props) {
|
||||
|
||||
@@ -19,7 +19,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-access-ref-during-render.ts:4:16
|
||||
2 | function Component(props) {
|
||||
|
||||
@@ -30,7 +30,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-access-ref-in-reducer-init.ts:8:4
|
||||
6 | (state, action) => state + action,
|
||||
|
||||
@@ -26,7 +26,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-access-ref-in-reducer.ts:5:29
|
||||
3 | function Component(props) {
|
||||
|
||||
@@ -22,7 +22,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-access-ref-in-render-mutate-object-with-ref-function.ts:7:19
|
||||
5 | const object = {};
|
||||
|
||||
@@ -26,7 +26,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-access-ref-in-state-initializer.ts:5:27
|
||||
3 | function Component(props) {
|
||||
|
||||
@@ -23,7 +23,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-aliased-ref-in-callback-invoked-during-render-.ts:9:33
|
||||
7 | return <Foo item={item} current={current} />;
|
||||
|
||||
@@ -22,7 +22,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
4 | component Example() {
|
||||
5 | const fooRef = makeObject_Primitives();
|
||||
|
||||
@@ -26,7 +26,7 @@ Found 2 errors:
|
||||
|
||||
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)
|
||||
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
|
||||
5 | useMemo(() => {
|
||||
@@ -39,7 +39,7 @@ error.invalid-conditional-setState-in-useMemo.ts:7:6
|
||||
|
||||
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)
|
||||
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:8:6
|
||||
6 | if (cond) {
|
||||
|
||||
@@ -17,12 +17,12 @@ Found 1 error:
|
||||
|
||||
Error: Cannot reassign variables declared outside of the component/hook
|
||||
|
||||
Variable `x` is declared outside of the component/hook. Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)
|
||||
Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render).
|
||||
|
||||
error.invalid-destructure-assignment-to-global.ts:2:3
|
||||
1 | function useFoo(props) {
|
||||
> 2 | [x] = props;
|
||||
| ^ `x` cannot be reassigned
|
||||
| ^ `x` should not be reassigned
|
||||
3 | return {x};
|
||||
4 | }
|
||||
5 |
|
||||
|
||||
@@ -19,13 +19,13 @@ Found 1 error:
|
||||
|
||||
Error: Cannot reassign variables declared outside of the component/hook
|
||||
|
||||
Variable `b` is declared outside of the component/hook. Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)
|
||||
Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render).
|
||||
|
||||
error.invalid-destructure-to-local-global-variables.ts:3:6
|
||||
1 | function Component(props) {
|
||||
2 | let a;
|
||||
> 3 | [a, b] = props.value;
|
||||
| ^ `b` cannot be reassigned
|
||||
| ^ `b` should not be reassigned
|
||||
4 |
|
||||
5 | return [a, b];
|
||||
6 | }
|
||||
|
||||
@@ -20,7 +20,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-disallow-mutating-ref-in-render.ts:4:2
|
||||
2 | function Component() {
|
||||
|
||||
@@ -25,7 +25,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-disallow-mutating-refs-in-render-transitive.ts:9:2
|
||||
7 | };
|
||||
|
||||
@@ -39,13 +39,13 @@ Found 1 error:
|
||||
|
||||
Error: Cannot reassign variables declared outside of the component/hook
|
||||
|
||||
Variable `someGlobal` is declared outside of the component/hook. Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)
|
||||
Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render).
|
||||
|
||||
error.invalid-global-reassignment-indirect.ts:9:4
|
||||
7 |
|
||||
8 | const setGlobal = () => {
|
||||
> 9 | someGlobal = true;
|
||||
| ^^^^^^^^^^ `someGlobal` cannot be reassigned
|
||||
| ^^^^^^^^^^ `someGlobal` should not be reassigned
|
||||
10 | };
|
||||
11 | const indirectSetGlobal = () => {
|
||||
12 | setGlobal();
|
||||
|
||||
@@ -42,13 +42,13 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access variable before it is declared
|
||||
|
||||
`setState` is accessed before it is declared, which prevents the earlier access from updating when this value changes over time.
|
||||
Reading a variable before it is initialized will prevent the earlier access from updating when this value changes over time. Instead, move the variable access to after it has been initialized.
|
||||
|
||||
error.invalid-hoisting-setstate.ts:19:18
|
||||
17 | * $2 = Function context=setState
|
||||
18 | */
|
||||
> 19 | useEffect(() => setState(2), []);
|
||||
| ^^^^^^^^ `setState` accessed before it is declared
|
||||
| ^^^^^^^^ `setState` is accessed before it is declared
|
||||
20 |
|
||||
21 | const [state, setState] = useState(0);
|
||||
22 | return <Stringify state={state} />;
|
||||
|
||||
@@ -21,7 +21,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot modify local variables after render completes
|
||||
|
||||
This argument is a function which may reassign or mutate `cache` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
This argument is a function which may reassign or mutate a variable 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
|
||||
3 | function useFoo() {
|
||||
|
||||
@@ -19,41 +19,41 @@ function Component() {
|
||||
```
|
||||
Found 3 errors:
|
||||
|
||||
Error: Cannot call impure function during render
|
||||
Error: Cannot call impure functions during render
|
||||
|
||||
`Date.now` is an impure function. 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)
|
||||
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).
|
||||
|
||||
error.invalid-impure-functions-in-render.ts:4:15
|
||||
2 |
|
||||
3 | function Component() {
|
||||
> 4 | const date = Date.now();
|
||||
| ^^^^^^^^^^ Cannot call impure function
|
||||
| ^^^^^^^^^^ `Date.now` is an impure function.
|
||||
5 | const now = performance.now();
|
||||
6 | const rand = Math.random();
|
||||
7 | return <Foo date={date} now={now} rand={rand} />;
|
||||
|
||||
Error: Cannot call impure function during render
|
||||
Error: Cannot call impure functions during render
|
||||
|
||||
`performance.now` is an impure function. 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)
|
||||
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).
|
||||
|
||||
error.invalid-impure-functions-in-render.ts:5:14
|
||||
3 | function Component() {
|
||||
4 | const date = Date.now();
|
||||
> 5 | const now = performance.now();
|
||||
| ^^^^^^^^^^^^^^^^^ Cannot call impure function
|
||||
| ^^^^^^^^^^^^^^^^^ `performance.now` is an impure function.
|
||||
6 | const rand = Math.random();
|
||||
7 | return <Foo date={date} now={now} rand={rand} />;
|
||||
8 | }
|
||||
|
||||
Error: Cannot call impure function during render
|
||||
Error: Cannot call impure functions during render
|
||||
|
||||
`Math.random` is an impure function. 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)
|
||||
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).
|
||||
|
||||
error.invalid-impure-functions-in-render.ts:6:15
|
||||
4 | const date = Date.now();
|
||||
5 | const now = performance.now();
|
||||
> 6 | const rand = Math.random();
|
||||
| ^^^^^^^^^^^^^ Cannot call impure function
|
||||
| ^^^^^^^^^^^^^ `Math.random` is an impure function.
|
||||
7 | return <Foo date={date} now={now} rand={rand} />;
|
||||
8 | }
|
||||
9 |
|
||||
|
||||
@@ -28,7 +28,7 @@ Found 1 error:
|
||||
|
||||
Error: This value cannot be modified
|
||||
|
||||
Modifying a value returned from 'useContext()' is not allowed..
|
||||
Modifying a value returned from 'useContext()' is not allowed.
|
||||
|
||||
error.invalid-mutate-context-in-callback.ts:12:4
|
||||
10 | // independently
|
||||
|
||||
@@ -18,7 +18,7 @@ Found 1 error:
|
||||
|
||||
Error: This value cannot be modified
|
||||
|
||||
Modifying a value returned from 'useContext()' is not allowed..
|
||||
Modifying a value returned from 'useContext()' is not allowed.
|
||||
|
||||
error.invalid-mutate-context.ts:3:2
|
||||
1 | function Component(props) {
|
||||
|
||||
@@ -23,7 +23,7 @@ Found 1 error:
|
||||
|
||||
Error: This value cannot be modified
|
||||
|
||||
Modifying a variable defined outside a component or hook is not allowed. Consider using an effect.
|
||||
Cannot reassign variables declared outside of the component/hook.
|
||||
|
||||
error.invalid-mutation-of-possible-props-phi-indirect.ts:4:4
|
||||
2 | let x = cond ? someGlobal : props.foo;
|
||||
|
||||
@@ -48,9 +48,9 @@ function Component() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot reassign variable after render completes
|
||||
Error: Cannot reassign local variables after render completes
|
||||
|
||||
Reassigning `local` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
Reassigning local variables 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
|
||||
5 | // Create the reassignment function inside another function, then return it
|
||||
|
||||
@@ -21,7 +21,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot modify local variables after render completes
|
||||
|
||||
This argument is a function which may reassign or mutate `cache` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
This argument is a function which may reassign or mutate a variable after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
|
||||
error.invalid-pass-mutable-function-as-prop.ts:7:18
|
||||
5 | cache.set('key', 'value');
|
||||
|
||||
@@ -19,7 +19,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-pass-ref-to-function.ts:4:16
|
||||
2 | function Component(props) {
|
||||
|
||||
@@ -18,7 +18,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-read-ref-prop-in-render-destructure.ts:3:16
|
||||
1 | // @validateRefAccessDuringRender @compilationMode:"infer"
|
||||
|
||||
@@ -18,7 +18,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-read-ref-prop-in-render-property-load.ts:3:16
|
||||
1 | // @validateRefAccessDuringRender @compilationMode:"infer"
|
||||
|
||||
@@ -15,7 +15,7 @@ function Component() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot reassign a `const` variable
|
||||
Error: Expect `const` declaration not to be reassigned
|
||||
|
||||
`x` is declared as const.
|
||||
|
||||
@@ -23,7 +23,7 @@ error.invalid-reassign-const.ts:3:2
|
||||
1 | function Component() {
|
||||
2 | const x = 0;
|
||||
> 3 | x = 1;
|
||||
| ^ Cannot reassign a `const` variable
|
||||
| ^ Expect `const` declaration not to be reassigned
|
||||
4 | }
|
||||
5 |
|
||||
```
|
||||
|
||||
@@ -17,9 +17,9 @@ function useFoo() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot reassign variable after render completes
|
||||
Error: Cannot reassign local variables after render completes
|
||||
|
||||
Reassigning `x` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
Reassigning local variables 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
|
||||
2 | let x = 0;
|
||||
|
||||
@@ -29,7 +29,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot reassign variable in async function
|
||||
|
||||
Reassigning a variable in an async function can cause inconsistent behavior on subsequent renders. Consider using state instead
|
||||
Reassigning a variable in an async function can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
|
||||
error.invalid-reassign-local-variable-in-async-callback.ts:8:6
|
||||
6 | // after render, so this should error regardless of where this ends up
|
||||
|
||||
@@ -49,9 +49,9 @@ function Component() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot reassign variable after render completes
|
||||
Error: Cannot reassign local variables after render completes
|
||||
|
||||
Reassigning `local` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
Reassigning local variables 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
|
||||
5 |
|
||||
|
||||
@@ -50,9 +50,9 @@ function Component() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot reassign variable after render completes
|
||||
Error: Cannot reassign local variables after render completes
|
||||
|
||||
Reassigning `local` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
Reassigning local variables 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
|
||||
6 |
|
||||
|
||||
@@ -43,9 +43,9 @@ function Component() {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot reassign variable after render completes
|
||||
Error: Cannot reassign local variables after render completes
|
||||
|
||||
Reassigning `local` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
Reassigning local variables 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
|
||||
3 |
|
||||
|
||||
@@ -22,7 +22,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-ref-in-callback-invoked-during-render.ts:8:33
|
||||
6 | return <Foo item={item} current={current} />;
|
||||
|
||||
@@ -18,7 +18,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-ref-value-as-props.ts:4:19
|
||||
2 | function Component(props) {
|
||||
|
||||
@@ -23,7 +23,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot modify local variables after render completes
|
||||
|
||||
This argument is a function which may reassign or mutate `cache` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
This argument is a function which may reassign or mutate a variable after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
|
||||
error.invalid-return-mutable-function-from-hook.ts:7:9
|
||||
5 | useHook(); // for inference to kick in
|
||||
|
||||
@@ -19,7 +19,7 @@ Found 2 errors:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-set-and-read-ref-during-render.ts:4:2
|
||||
2 | function Component(props) {
|
||||
@@ -32,7 +32,7 @@ error.invalid-set-and-read-ref-during-render.ts:4:2
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-set-and-read-ref-during-render.ts:5:9
|
||||
3 | const ref = useRef(null);
|
||||
|
||||
@@ -19,7 +19,7 @@ Found 2 errors:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-set-and-read-ref-nested-property-during-render.ts:4:2
|
||||
2 | function Component(props) {
|
||||
@@ -32,7 +32,7 @@ error.invalid-set-and-read-ref-nested-property-during-render.ts:4:2
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-set-and-read-ref-nested-property-during-render.ts:5:9
|
||||
3 | const ref = useRef({inner: null});
|
||||
|
||||
@@ -30,7 +30,7 @@ Found 1 error:
|
||||
|
||||
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)
|
||||
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
|
||||
11 |
|
||||
|
||||
@@ -24,7 +24,7 @@ Found 2 errors:
|
||||
|
||||
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)
|
||||
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
|
||||
4 |
|
||||
@@ -37,7 +37,7 @@ error.invalid-setState-in-useMemo.ts:6:4
|
||||
|
||||
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)
|
||||
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:7:4
|
||||
5 | useMemo(() => {
|
||||
|
||||
@@ -21,7 +21,7 @@ Found 2 errors:
|
||||
|
||||
Error: React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled
|
||||
|
||||
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable react-hooks/rules-of-hooks`
|
||||
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable react-hooks/rules-of-hooks`.
|
||||
|
||||
error.invalid-sketchy-code-use-forget.ts:1:0
|
||||
> 1 | /* eslint-disable react-hooks/rules-of-hooks */
|
||||
@@ -32,7 +32,7 @@ error.invalid-sketchy-code-use-forget.ts:1:0
|
||||
|
||||
Error: React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled
|
||||
|
||||
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable-next-line react-hooks/rules-of-hooks`
|
||||
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable-next-line react-hooks/rules-of-hooks`.
|
||||
|
||||
error.invalid-sketchy-code-use-forget.ts:5:2
|
||||
3 | 'use forget';
|
||||
|
||||
@@ -51,7 +51,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot modify local variables after render completes
|
||||
|
||||
This argument is a function which may reassign or mutate `cache` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
This argument is a function which may reassign or mutate a variable after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
|
||||
19 | map: TInput => TOutput
|
||||
20 | ): TInput => TOutput {
|
||||
|
||||
@@ -40,7 +40,7 @@ Found 1 error:
|
||||
|
||||
Error: React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled
|
||||
|
||||
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable react-hooks/rules-of-hooks`
|
||||
React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior. Found suppression `eslint-disable react-hooks/rules-of-hooks`.
|
||||
|
||||
error.invalid-unclosed-eslint-suppression.ts:2:0
|
||||
1 | // Note: Everything below this is sketchy
|
||||
|
||||
@@ -23,26 +23,26 @@ Found 2 errors:
|
||||
|
||||
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)
|
||||
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
|
||||
4 | const aliased = setX;
|
||||
5 |
|
||||
> 6 | setX(1);
|
||||
| ^^^^ Found setState() within useMemo()
|
||||
| ^^^^ Found setState() call here
|
||||
7 | aliased(2);
|
||||
8 |
|
||||
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)
|
||||
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:7:2
|
||||
5 |
|
||||
6 | setX(1);
|
||||
> 7 | aliased(2);
|
||||
| ^^^^^^^ Found setState() within useMemo()
|
||||
| ^^^^^^^ Found setState() call here
|
||||
8 |
|
||||
9 | return x;
|
||||
10 | }
|
||||
|
||||
@@ -26,7 +26,7 @@ Found 2 errors:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-use-ref-added-to-dep-without-type-info.ts:10:21
|
||||
8 | // however, this is an instance of accessing a ref during render and is disallowed
|
||||
@@ -39,7 +39,7 @@ error.invalid-use-ref-added-to-dep-without-type-info.ts:10:21
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-use-ref-added-to-dep-without-type-info.ts:12:28
|
||||
10 | const x = {a, val: val.ref.current};
|
||||
|
||||
@@ -21,7 +21,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-write-but-dont-read-ref-in-render.ts:5:2
|
||||
3 | const ref = useRef(null);
|
||||
|
||||
@@ -19,7 +19,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
error.invalid-write-ref-prop-in-render.ts:4:2
|
||||
2 | function Component(props) {
|
||||
|
||||
@@ -34,9 +34,9 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot reassign variable after render completes
|
||||
Error: Cannot reassign local variables after render completes
|
||||
|
||||
Reassigning `a` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
Reassigning local variables 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
|
||||
6 | const f = () => {
|
||||
|
||||
@@ -19,7 +19,7 @@ Found 1 error:
|
||||
|
||||
Error: This value cannot be modified
|
||||
|
||||
Modifying a variable defined outside a component or hook is not allowed. Consider using an effect.
|
||||
Cannot reassign variables declared outside of the component/hook.
|
||||
|
||||
error.mutate-property-from-global.ts:4:9
|
||||
2 |
|
||||
|
||||
@@ -21,7 +21,7 @@ Found 2 errors:
|
||||
|
||||
Error: This value cannot be modified
|
||||
|
||||
Modifying a variable defined outside a component or hook is not allowed. Consider using an effect.
|
||||
Cannot reassign variables declared outside of the component/hook.
|
||||
|
||||
error.not-useEffect-external-mutate.ts:5:4
|
||||
3 | function Component(props) {
|
||||
@@ -34,7 +34,7 @@ error.not-useEffect-external-mutate.ts:5:4
|
||||
|
||||
Error: This value cannot be modified
|
||||
|
||||
Modifying a variable defined outside a component or hook is not allowed. Consider using an effect.
|
||||
Cannot reassign variables declared outside of the component/hook.
|
||||
|
||||
error.not-useEffect-external-mutate.ts:6:4
|
||||
4 | foo(() => {
|
||||
|
||||
@@ -24,13 +24,15 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Modifying a variable defined outside a component or hook is not allowed. Consider using an effect
|
||||
Error: Cannot reassign variables declared outside of the component/hook
|
||||
|
||||
Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render).
|
||||
|
||||
error.object-capture-global-mutation.ts:4:4
|
||||
2 | function Foo() {
|
||||
3 | const x = () => {
|
||||
> 4 | window.href = 'foo';
|
||||
| ^^^^^^ Modifying a variable defined outside a component or hook is not allowed. Consider using an effect
|
||||
| ^^^^^^ Cannot reassign variables declared outside of the component/hook
|
||||
5 | };
|
||||
6 | const y = {x};
|
||||
7 | return <Bar y={y} />;
|
||||
|
||||
@@ -28,13 +28,13 @@ Found 1 error:
|
||||
|
||||
Error: Cannot reassign variables declared outside of the component/hook
|
||||
|
||||
Variable `b` is declared outside of the component/hook. Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)
|
||||
Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render).
|
||||
|
||||
error.reassign-global-fn-arg.ts:5:4
|
||||
3 | export default function MyApp() {
|
||||
4 | const fn = () => {
|
||||
> 5 | b = 2;
|
||||
| ^ `b` cannot be reassigned
|
||||
| ^ `b` should not be reassigned
|
||||
6 | };
|
||||
7 | return foo(fn);
|
||||
8 | }
|
||||
|
||||
@@ -21,26 +21,26 @@ Found 2 errors:
|
||||
|
||||
Error: Cannot reassign variables declared outside of the component/hook
|
||||
|
||||
Variable `someUnknownGlobal` is declared outside of the component/hook. Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)
|
||||
Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render).
|
||||
|
||||
error.reassignment-to-global-indirect.ts:4:4
|
||||
2 | const foo = () => {
|
||||
3 | // Cannot assign to globals
|
||||
> 4 | someUnknownGlobal = true;
|
||||
| ^^^^^^^^^^^^^^^^^ `someUnknownGlobal` cannot be reassigned
|
||||
| ^^^^^^^^^^^^^^^^^ `someUnknownGlobal` should not be reassigned
|
||||
5 | moduleLocal = true;
|
||||
6 | };
|
||||
7 | foo();
|
||||
|
||||
Error: Cannot reassign variables declared outside of the component/hook
|
||||
|
||||
Variable `moduleLocal` is declared outside of the component/hook. Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)
|
||||
Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render).
|
||||
|
||||
error.reassignment-to-global-indirect.ts:5:4
|
||||
3 | // Cannot assign to globals
|
||||
4 | someUnknownGlobal = true;
|
||||
> 5 | moduleLocal = true;
|
||||
| ^^^^^^^^^^^ `moduleLocal` cannot be reassigned
|
||||
| ^^^^^^^^^^^ `moduleLocal` should not be reassigned
|
||||
6 | };
|
||||
7 | foo();
|
||||
8 | }
|
||||
|
||||
@@ -18,26 +18,26 @@ Found 2 errors:
|
||||
|
||||
Error: Cannot reassign variables declared outside of the component/hook
|
||||
|
||||
Variable `someUnknownGlobal` is declared outside of the component/hook. Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)
|
||||
Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render).
|
||||
|
||||
error.reassignment-to-global.ts:3:2
|
||||
1 | function Component() {
|
||||
2 | // Cannot assign to globals
|
||||
> 3 | someUnknownGlobal = true;
|
||||
| ^^^^^^^^^^^^^^^^^ `someUnknownGlobal` cannot be reassigned
|
||||
| ^^^^^^^^^^^^^^^^^ `someUnknownGlobal` should not be reassigned
|
||||
4 | moduleLocal = true;
|
||||
5 | }
|
||||
6 |
|
||||
|
||||
Error: Cannot reassign variables declared outside of the component/hook
|
||||
|
||||
Variable `moduleLocal` is declared outside of the component/hook. Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)
|
||||
Reassigning this value during render is a form of side effect, which can cause unpredictable behavior depending on when the component happens to re-render. If this variable is used in rendering, use useState instead. Otherwise, consider updating it in an effect. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render).
|
||||
|
||||
error.reassignment-to-global.ts:4:2
|
||||
2 | // Cannot assign to globals
|
||||
3 | someUnknownGlobal = true;
|
||||
> 4 | moduleLocal = true;
|
||||
| ^^^^^^^^^^^ `moduleLocal` cannot be reassigned
|
||||
| ^^^^^^^^^^^ `moduleLocal` should not be reassigned
|
||||
5 | }
|
||||
6 |
|
||||
```
|
||||
|
||||
@@ -29,7 +29,7 @@ Found 2 errors:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
6 | component C() {
|
||||
7 | const r = useRef(DEFAULT_VALUE);
|
||||
@@ -41,7 +41,7 @@ React refs are values that are not needed for rendering. Refs should only be acc
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
7 | const r = useRef(DEFAULT_VALUE);
|
||||
8 | if (r.current == DEFAULT_VALUE) {
|
||||
|
||||
@@ -27,7 +27,7 @@ Found 1 error:
|
||||
|
||||
Error: Cannot access refs during render
|
||||
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)
|
||||
React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef).
|
||||
|
||||
5 | const r = useRef(null);
|
||||
6 | if (r.current == null) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user