eslint-plugin-react-hooks 6.1.0-canary-19baee81-20250725 → 6.1.0-canary-9be531cd-20250729
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -18676,7 +18676,7 @@ function printTerminal(terminal) {
|
|
|
18676
18676
|
break;
|
|
18677
18677
|
}
|
|
18678
18678
|
case 'return': {
|
|
18679
|
-
value = `[${terminal.id}] Return${terminal.value != null ? ' ' + printPlace(terminal.value) : ''}`;
|
|
18679
|
+
value = `[${terminal.id}] Return ${terminal.returnVariant}${terminal.value != null ? ' ' + printPlace(terminal.value) : ''}`;
|
|
18680
18680
|
if (terminal.effects != null) {
|
|
18681
18681
|
value += `\n ${terminal.effects.map(printAliasingEffect).join('\n ')}`;
|
|
18682
18682
|
}
|
|
@@ -20025,6 +20025,7 @@ function mapTerminalSuccessors(terminal, fn) {
|
|
|
20025
20025
|
case 'return': {
|
|
20026
20026
|
return {
|
|
20027
20027
|
kind: 'return',
|
|
20028
|
+
returnVariant: terminal.returnVariant,
|
|
20028
20029
|
loc: terminal.loc,
|
|
20029
20030
|
value: terminal.value,
|
|
20030
20031
|
id: makeInstructionId(0),
|
|
@@ -22388,6 +22389,7 @@ function lower$1(func, env, bindings = null, capturedRefs = new Map()) {
|
|
|
22388
22389
|
const fallthrough = builder.reserve('block');
|
|
22389
22390
|
const terminal = {
|
|
22390
22391
|
kind: 'return',
|
|
22392
|
+
returnVariant: 'Implicit',
|
|
22391
22393
|
loc: GeneratedSource,
|
|
22392
22394
|
value: lowerExpressionToTemporary(builder, body),
|
|
22393
22395
|
id: makeInstructionId(0),
|
|
@@ -22415,6 +22417,7 @@ function lower$1(func, env, bindings = null, capturedRefs = new Map()) {
|
|
|
22415
22417
|
}
|
|
22416
22418
|
builder.terminate({
|
|
22417
22419
|
kind: 'return',
|
|
22420
|
+
returnVariant: 'Void',
|
|
22418
22421
|
loc: GeneratedSource,
|
|
22419
22422
|
value: lowerValueToTemporary(builder, {
|
|
22420
22423
|
kind: 'Primitive',
|
|
@@ -22482,6 +22485,7 @@ function lowerStatement(builder, stmtPath, label = null) {
|
|
|
22482
22485
|
}
|
|
22483
22486
|
const terminal = {
|
|
22484
22487
|
kind: 'return',
|
|
22488
|
+
returnVariant: 'Explicit',
|
|
22485
22489
|
loc: (_c = stmt.node.loc) !== null && _c !== void 0 ? _c : GeneratedSource,
|
|
22486
22490
|
value,
|
|
22487
22491
|
id: makeInstructionId(0),
|
|
@@ -30680,6 +30684,7 @@ const EnvironmentConfigSchema = zod.z.object({
|
|
|
30680
30684
|
hookPattern: zod.z.string().nullable().default(null),
|
|
30681
30685
|
enableTreatRefLikeIdentifiersAsRefs: zod.z.boolean().default(false),
|
|
30682
30686
|
lowerContextAccess: ExternalFunctionSchema.nullable().default(null),
|
|
30687
|
+
validateNoVoidUseMemo: zod.z.boolean().default(false),
|
|
30683
30688
|
});
|
|
30684
30689
|
class Environment {
|
|
30685
30690
|
constructor(scope, fnType, compilerMode, config, contextIdentifiers, parentFunction, logger, filename, code, programContext) {
|
|
@@ -43797,43 +43802,69 @@ function getManualMemoizationReplacement(fn, loc, kind) {
|
|
|
43797
43802
|
};
|
|
43798
43803
|
}
|
|
43799
43804
|
}
|
|
43800
|
-
function extractManualMemoizationArgs(instr, kind, sidemap) {
|
|
43805
|
+
function extractManualMemoizationArgs(instr, kind, sidemap, errors) {
|
|
43801
43806
|
const [fnPlace, depsListPlace] = instr.value.args;
|
|
43802
43807
|
if (fnPlace == null) {
|
|
43803
|
-
|
|
43804
|
-
|
|
43805
|
-
|
|
43808
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
43809
|
+
severity: ErrorSeverity.InvalidReact,
|
|
43810
|
+
category: `Expected a callback function to be passed to ${kind}`,
|
|
43811
|
+
description: `Expected a callback function to be passed to ${kind}`,
|
|
43806
43812
|
suggestions: null,
|
|
43807
|
-
})
|
|
43813
|
+
}).withDetail({
|
|
43814
|
+
kind: 'error',
|
|
43815
|
+
loc: instr.value.loc,
|
|
43816
|
+
message: `Expected a callback function to be passed to ${kind}`,
|
|
43817
|
+
}));
|
|
43818
|
+
return { fnPlace: null, depsList: null };
|
|
43808
43819
|
}
|
|
43809
43820
|
if (fnPlace.kind === 'Spread' || (depsListPlace === null || depsListPlace === void 0 ? void 0 : depsListPlace.kind) === 'Spread') {
|
|
43810
|
-
|
|
43811
|
-
|
|
43812
|
-
|
|
43821
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
43822
|
+
severity: ErrorSeverity.InvalidReact,
|
|
43823
|
+
category: `Unexpected spread argument to ${kind}`,
|
|
43824
|
+
description: `Unexpected spread argument to ${kind}`,
|
|
43813
43825
|
suggestions: null,
|
|
43814
|
-
})
|
|
43826
|
+
}).withDetail({
|
|
43827
|
+
kind: 'error',
|
|
43828
|
+
loc: instr.value.loc,
|
|
43829
|
+
message: `Unexpected spread argument to ${kind}`,
|
|
43830
|
+
}));
|
|
43831
|
+
return { fnPlace: null, depsList: null };
|
|
43815
43832
|
}
|
|
43816
43833
|
let depsList = null;
|
|
43817
43834
|
if (depsListPlace != null) {
|
|
43818
43835
|
const maybeDepsList = sidemap.maybeDepsLists.get(depsListPlace.identifier.id);
|
|
43819
43836
|
if (maybeDepsList == null) {
|
|
43820
|
-
|
|
43821
|
-
|
|
43837
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
43838
|
+
severity: ErrorSeverity.InvalidReact,
|
|
43839
|
+
category: `Expected the dependency list for ${kind} to be an array literal`,
|
|
43840
|
+
description: `Expected the dependency list for ${kind} to be an array literal`,
|
|
43822
43841
|
suggestions: null,
|
|
43842
|
+
}).withDetail({
|
|
43843
|
+
kind: 'error',
|
|
43823
43844
|
loc: depsListPlace.loc,
|
|
43824
|
-
|
|
43845
|
+
message: `Expected the dependency list for ${kind} to be an array literal`,
|
|
43846
|
+
}));
|
|
43847
|
+
return { fnPlace, depsList: null };
|
|
43825
43848
|
}
|
|
43826
|
-
depsList =
|
|
43849
|
+
depsList = [];
|
|
43850
|
+
for (const dep of maybeDepsList) {
|
|
43827
43851
|
const maybeDep = sidemap.maybeDeps.get(dep.identifier.id);
|
|
43828
43852
|
if (maybeDep == null) {
|
|
43829
|
-
|
|
43830
|
-
|
|
43853
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
43854
|
+
severity: ErrorSeverity.InvalidReact,
|
|
43855
|
+
category: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
|
43856
|
+
description: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
|
43831
43857
|
suggestions: null,
|
|
43858
|
+
}).withDetail({
|
|
43859
|
+
kind: 'error',
|
|
43832
43860
|
loc: dep.loc,
|
|
43833
|
-
|
|
43861
|
+
message: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
|
43862
|
+
}));
|
|
43834
43863
|
}
|
|
43835
|
-
|
|
43836
|
-
|
|
43864
|
+
else {
|
|
43865
|
+
depsList.push(maybeDep);
|
|
43866
|
+
}
|
|
43867
|
+
}
|
|
43837
43868
|
}
|
|
43838
43869
|
return {
|
|
43839
43870
|
fnPlace,
|
|
@@ -43841,6 +43872,8 @@ function extractManualMemoizationArgs(instr, kind, sidemap) {
|
|
|
43841
43872
|
};
|
|
43842
43873
|
}
|
|
43843
43874
|
function dropManualMemoization(func) {
|
|
43875
|
+
var _a;
|
|
43876
|
+
const errors = new CompilerError();
|
|
43844
43877
|
const isValidationEnabled = func.env.config.validatePreserveExistingMemoizationGuarantees ||
|
|
43845
43878
|
func.env.config.validateNoSetStateInRender ||
|
|
43846
43879
|
func.env.config.enablePreserveExistingMemoizationGuarantees;
|
|
@@ -43865,15 +43898,44 @@ function dropManualMemoization(func) {
|
|
|
43865
43898
|
: instr.value.property.identifier.id;
|
|
43866
43899
|
const manualMemo = sidemap.manualMemos.get(id);
|
|
43867
43900
|
if (manualMemo != null) {
|
|
43868
|
-
const { fnPlace, depsList } = extractManualMemoizationArgs(instr, manualMemo.kind, sidemap);
|
|
43901
|
+
const { fnPlace, depsList } = extractManualMemoizationArgs(instr, manualMemo.kind, sidemap, errors);
|
|
43902
|
+
if (fnPlace == null) {
|
|
43903
|
+
continue;
|
|
43904
|
+
}
|
|
43905
|
+
if (func.env.config.validateNoVoidUseMemo &&
|
|
43906
|
+
manualMemo.kind === 'useMemo') {
|
|
43907
|
+
const funcToCheck = (_a = sidemap.functions.get(fnPlace.identifier.id)) === null || _a === void 0 ? void 0 : _a.value;
|
|
43908
|
+
if (funcToCheck !== undefined && funcToCheck.loweredFunc.func) {
|
|
43909
|
+
if (!hasNonVoidReturn(funcToCheck.loweredFunc.func)) {
|
|
43910
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
43911
|
+
severity: ErrorSeverity.InvalidReact,
|
|
43912
|
+
category: 'useMemo() callbacks must return a value',
|
|
43913
|
+
description: `This ${manualMemo.loadInstr.value.kind === 'PropertyLoad'
|
|
43914
|
+
? 'React.useMemo'
|
|
43915
|
+
: 'useMemo'} callback doesn't return a value. useMemo is for computing and caching values, not for arbitrary side effects.`,
|
|
43916
|
+
suggestions: null,
|
|
43917
|
+
}).withDetail({
|
|
43918
|
+
kind: 'error',
|
|
43919
|
+
loc: instr.value.loc,
|
|
43920
|
+
message: 'useMemo() callbacks must return a value',
|
|
43921
|
+
}));
|
|
43922
|
+
}
|
|
43923
|
+
}
|
|
43924
|
+
}
|
|
43869
43925
|
instr.value = getManualMemoizationReplacement(fnPlace, instr.value.loc, manualMemo.kind);
|
|
43870
43926
|
if (isValidationEnabled) {
|
|
43871
43927
|
if (!sidemap.functions.has(fnPlace.identifier.id)) {
|
|
43872
|
-
|
|
43873
|
-
|
|
43928
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
43929
|
+
severity: ErrorSeverity.InvalidReact,
|
|
43930
|
+
category: `Expected the first argument to be an inline function expression`,
|
|
43931
|
+
description: `Expected the first argument to be an inline function expression`,
|
|
43874
43932
|
suggestions: [],
|
|
43933
|
+
}).withDetail({
|
|
43934
|
+
kind: 'error',
|
|
43875
43935
|
loc: fnPlace.loc,
|
|
43876
|
-
|
|
43936
|
+
message: `Expected the first argument to be an inline function expression`,
|
|
43937
|
+
}));
|
|
43938
|
+
continue;
|
|
43877
43939
|
}
|
|
43878
43940
|
const memoDecl = manualMemo.kind === 'useMemo'
|
|
43879
43941
|
? instr.lvalue
|
|
@@ -43920,6 +43982,7 @@ function dropManualMemoization(func) {
|
|
|
43920
43982
|
markInstructionIds(func.body);
|
|
43921
43983
|
}
|
|
43922
43984
|
}
|
|
43985
|
+
return errors.asResult();
|
|
43923
43986
|
}
|
|
43924
43987
|
function findOptionalPlaces(fn) {
|
|
43925
43988
|
const optionals = new Set();
|
|
@@ -43963,6 +44026,17 @@ function findOptionalPlaces(fn) {
|
|
|
43963
44026
|
}
|
|
43964
44027
|
return optionals;
|
|
43965
44028
|
}
|
|
44029
|
+
function hasNonVoidReturn(func) {
|
|
44030
|
+
for (const [, block] of func.body.blocks) {
|
|
44031
|
+
if (block.terminal.kind === 'return') {
|
|
44032
|
+
if (block.terminal.returnVariant === 'Explicit' ||
|
|
44033
|
+
block.terminal.returnVariant === 'Implicit') {
|
|
44034
|
+
return true;
|
|
44035
|
+
}
|
|
44036
|
+
}
|
|
44037
|
+
}
|
|
44038
|
+
return false;
|
|
44039
|
+
}
|
|
43966
44040
|
|
|
43967
44041
|
class StableSidemap {
|
|
43968
44042
|
constructor(env) {
|
|
@@ -49406,6 +49480,7 @@ function emitSelectorFn(env, keys) {
|
|
|
49406
49480
|
terminal: {
|
|
49407
49481
|
id: makeInstructionId(0),
|
|
49408
49482
|
kind: 'return',
|
|
49483
|
+
returnVariant: 'Explicit',
|
|
49409
49484
|
loc: GeneratedSource,
|
|
49410
49485
|
value: arrayInstr.lvalue,
|
|
49411
49486
|
effects: null,
|
|
@@ -49837,6 +49912,7 @@ function emitOutlinedFn(env, jsx, oldProps, globals) {
|
|
|
49837
49912
|
terminal: {
|
|
49838
49913
|
id: makeInstructionId(0),
|
|
49839
49914
|
kind: 'return',
|
|
49915
|
+
returnVariant: 'Explicit',
|
|
49840
49916
|
loc: GeneratedSource,
|
|
49841
49917
|
value: instructions.at(-1).lvalue,
|
|
49842
49918
|
effects: null,
|
|
@@ -50657,7 +50733,7 @@ function runWithEnvironment(func, env) {
|
|
|
50657
50733
|
!env.config.enablePreserveExistingManualUseMemo &&
|
|
50658
50734
|
!env.config.disableMemoizationForDebugging &&
|
|
50659
50735
|
!env.config.enableChangeDetectionForDebugging) {
|
|
50660
|
-
dropManualMemoization(hir);
|
|
50736
|
+
dropManualMemoization(hir).unwrap();
|
|
50661
50737
|
log({ kind: 'hir', name: 'DropManualMemoization', value: hir });
|
|
50662
50738
|
}
|
|
50663
50739
|
inlineImmediatelyInvokedFunctionExpressions(hir);
|
|
@@ -52607,6 +52683,7 @@ const COMPILER_OPTIONS = {
|
|
|
52607
52683
|
validateNoImpureFunctionsInRender: true,
|
|
52608
52684
|
validateStaticComponents: true,
|
|
52609
52685
|
validateNoFreezingKnownMutableFunctions: true,
|
|
52686
|
+
validateNoVoidUseMemo: true,
|
|
52610
52687
|
}),
|
|
52611
52688
|
};
|
|
52612
52689
|
const rule$1 = {
|
|
@@ -18667,7 +18667,7 @@ function printTerminal(terminal) {
|
|
|
18667
18667
|
break;
|
|
18668
18668
|
}
|
|
18669
18669
|
case 'return': {
|
|
18670
|
-
value = `[${terminal.id}] Return${terminal.value != null ? ' ' + printPlace(terminal.value) : ''}`;
|
|
18670
|
+
value = `[${terminal.id}] Return ${terminal.returnVariant}${terminal.value != null ? ' ' + printPlace(terminal.value) : ''}`;
|
|
18671
18671
|
if (terminal.effects != null) {
|
|
18672
18672
|
value += `\n ${terminal.effects.map(printAliasingEffect).join('\n ')}`;
|
|
18673
18673
|
}
|
|
@@ -20016,6 +20016,7 @@ function mapTerminalSuccessors(terminal, fn) {
|
|
|
20016
20016
|
case 'return': {
|
|
20017
20017
|
return {
|
|
20018
20018
|
kind: 'return',
|
|
20019
|
+
returnVariant: terminal.returnVariant,
|
|
20019
20020
|
loc: terminal.loc,
|
|
20020
20021
|
value: terminal.value,
|
|
20021
20022
|
id: makeInstructionId(0),
|
|
@@ -22379,6 +22380,7 @@ function lower$1(func, env, bindings = null, capturedRefs = new Map()) {
|
|
|
22379
22380
|
const fallthrough = builder.reserve('block');
|
|
22380
22381
|
const terminal = {
|
|
22381
22382
|
kind: 'return',
|
|
22383
|
+
returnVariant: 'Implicit',
|
|
22382
22384
|
loc: GeneratedSource,
|
|
22383
22385
|
value: lowerExpressionToTemporary(builder, body),
|
|
22384
22386
|
id: makeInstructionId(0),
|
|
@@ -22406,6 +22408,7 @@ function lower$1(func, env, bindings = null, capturedRefs = new Map()) {
|
|
|
22406
22408
|
}
|
|
22407
22409
|
builder.terminate({
|
|
22408
22410
|
kind: 'return',
|
|
22411
|
+
returnVariant: 'Void',
|
|
22409
22412
|
loc: GeneratedSource,
|
|
22410
22413
|
value: lowerValueToTemporary(builder, {
|
|
22411
22414
|
kind: 'Primitive',
|
|
@@ -22473,6 +22476,7 @@ function lowerStatement(builder, stmtPath, label = null) {
|
|
|
22473
22476
|
}
|
|
22474
22477
|
const terminal = {
|
|
22475
22478
|
kind: 'return',
|
|
22479
|
+
returnVariant: 'Explicit',
|
|
22476
22480
|
loc: (_c = stmt.node.loc) !== null && _c !== void 0 ? _c : GeneratedSource,
|
|
22477
22481
|
value,
|
|
22478
22482
|
id: makeInstructionId(0),
|
|
@@ -30507,6 +30511,7 @@ const EnvironmentConfigSchema = zod.z.object({
|
|
|
30507
30511
|
hookPattern: zod.z.string().nullable().default(null),
|
|
30508
30512
|
enableTreatRefLikeIdentifiersAsRefs: zod.z.boolean().default(false),
|
|
30509
30513
|
lowerContextAccess: ExternalFunctionSchema.nullable().default(null),
|
|
30514
|
+
validateNoVoidUseMemo: zod.z.boolean().default(false),
|
|
30510
30515
|
});
|
|
30511
30516
|
class Environment {
|
|
30512
30517
|
constructor(scope, fnType, compilerMode, config, contextIdentifiers, parentFunction, logger, filename, code, programContext) {
|
|
@@ -43624,43 +43629,69 @@ function getManualMemoizationReplacement(fn, loc, kind) {
|
|
|
43624
43629
|
};
|
|
43625
43630
|
}
|
|
43626
43631
|
}
|
|
43627
|
-
function extractManualMemoizationArgs(instr, kind, sidemap) {
|
|
43632
|
+
function extractManualMemoizationArgs(instr, kind, sidemap, errors) {
|
|
43628
43633
|
const [fnPlace, depsListPlace] = instr.value.args;
|
|
43629
43634
|
if (fnPlace == null) {
|
|
43630
|
-
|
|
43631
|
-
|
|
43632
|
-
|
|
43635
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
43636
|
+
severity: ErrorSeverity.InvalidReact,
|
|
43637
|
+
category: `Expected a callback function to be passed to ${kind}`,
|
|
43638
|
+
description: `Expected a callback function to be passed to ${kind}`,
|
|
43633
43639
|
suggestions: null,
|
|
43634
|
-
})
|
|
43640
|
+
}).withDetail({
|
|
43641
|
+
kind: 'error',
|
|
43642
|
+
loc: instr.value.loc,
|
|
43643
|
+
message: `Expected a callback function to be passed to ${kind}`,
|
|
43644
|
+
}));
|
|
43645
|
+
return { fnPlace: null, depsList: null };
|
|
43635
43646
|
}
|
|
43636
43647
|
if (fnPlace.kind === 'Spread' || (depsListPlace === null || depsListPlace === void 0 ? void 0 : depsListPlace.kind) === 'Spread') {
|
|
43637
|
-
|
|
43638
|
-
|
|
43639
|
-
|
|
43648
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
43649
|
+
severity: ErrorSeverity.InvalidReact,
|
|
43650
|
+
category: `Unexpected spread argument to ${kind}`,
|
|
43651
|
+
description: `Unexpected spread argument to ${kind}`,
|
|
43640
43652
|
suggestions: null,
|
|
43641
|
-
})
|
|
43653
|
+
}).withDetail({
|
|
43654
|
+
kind: 'error',
|
|
43655
|
+
loc: instr.value.loc,
|
|
43656
|
+
message: `Unexpected spread argument to ${kind}`,
|
|
43657
|
+
}));
|
|
43658
|
+
return { fnPlace: null, depsList: null };
|
|
43642
43659
|
}
|
|
43643
43660
|
let depsList = null;
|
|
43644
43661
|
if (depsListPlace != null) {
|
|
43645
43662
|
const maybeDepsList = sidemap.maybeDepsLists.get(depsListPlace.identifier.id);
|
|
43646
43663
|
if (maybeDepsList == null) {
|
|
43647
|
-
|
|
43648
|
-
|
|
43664
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
43665
|
+
severity: ErrorSeverity.InvalidReact,
|
|
43666
|
+
category: `Expected the dependency list for ${kind} to be an array literal`,
|
|
43667
|
+
description: `Expected the dependency list for ${kind} to be an array literal`,
|
|
43649
43668
|
suggestions: null,
|
|
43669
|
+
}).withDetail({
|
|
43670
|
+
kind: 'error',
|
|
43650
43671
|
loc: depsListPlace.loc,
|
|
43651
|
-
|
|
43672
|
+
message: `Expected the dependency list for ${kind} to be an array literal`,
|
|
43673
|
+
}));
|
|
43674
|
+
return { fnPlace, depsList: null };
|
|
43652
43675
|
}
|
|
43653
|
-
depsList =
|
|
43676
|
+
depsList = [];
|
|
43677
|
+
for (const dep of maybeDepsList) {
|
|
43654
43678
|
const maybeDep = sidemap.maybeDeps.get(dep.identifier.id);
|
|
43655
43679
|
if (maybeDep == null) {
|
|
43656
|
-
|
|
43657
|
-
|
|
43680
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
43681
|
+
severity: ErrorSeverity.InvalidReact,
|
|
43682
|
+
category: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
|
43683
|
+
description: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
|
43658
43684
|
suggestions: null,
|
|
43685
|
+
}).withDetail({
|
|
43686
|
+
kind: 'error',
|
|
43659
43687
|
loc: dep.loc,
|
|
43660
|
-
|
|
43688
|
+
message: `Expected the dependency list to be an array of simple expressions (e.g. \`x\`, \`x.y.z\`, \`x?.y?.z\`)`,
|
|
43689
|
+
}));
|
|
43661
43690
|
}
|
|
43662
|
-
|
|
43663
|
-
|
|
43691
|
+
else {
|
|
43692
|
+
depsList.push(maybeDep);
|
|
43693
|
+
}
|
|
43694
|
+
}
|
|
43664
43695
|
}
|
|
43665
43696
|
return {
|
|
43666
43697
|
fnPlace,
|
|
@@ -43668,6 +43699,8 @@ function extractManualMemoizationArgs(instr, kind, sidemap) {
|
|
|
43668
43699
|
};
|
|
43669
43700
|
}
|
|
43670
43701
|
function dropManualMemoization(func) {
|
|
43702
|
+
var _a;
|
|
43703
|
+
const errors = new CompilerError();
|
|
43671
43704
|
const isValidationEnabled = func.env.config.validatePreserveExistingMemoizationGuarantees ||
|
|
43672
43705
|
func.env.config.validateNoSetStateInRender ||
|
|
43673
43706
|
func.env.config.enablePreserveExistingMemoizationGuarantees;
|
|
@@ -43692,15 +43725,44 @@ function dropManualMemoization(func) {
|
|
|
43692
43725
|
: instr.value.property.identifier.id;
|
|
43693
43726
|
const manualMemo = sidemap.manualMemos.get(id);
|
|
43694
43727
|
if (manualMemo != null) {
|
|
43695
|
-
const { fnPlace, depsList } = extractManualMemoizationArgs(instr, manualMemo.kind, sidemap);
|
|
43728
|
+
const { fnPlace, depsList } = extractManualMemoizationArgs(instr, manualMemo.kind, sidemap, errors);
|
|
43729
|
+
if (fnPlace == null) {
|
|
43730
|
+
continue;
|
|
43731
|
+
}
|
|
43732
|
+
if (func.env.config.validateNoVoidUseMemo &&
|
|
43733
|
+
manualMemo.kind === 'useMemo') {
|
|
43734
|
+
const funcToCheck = (_a = sidemap.functions.get(fnPlace.identifier.id)) === null || _a === void 0 ? void 0 : _a.value;
|
|
43735
|
+
if (funcToCheck !== undefined && funcToCheck.loweredFunc.func) {
|
|
43736
|
+
if (!hasNonVoidReturn(funcToCheck.loweredFunc.func)) {
|
|
43737
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
43738
|
+
severity: ErrorSeverity.InvalidReact,
|
|
43739
|
+
category: 'useMemo() callbacks must return a value',
|
|
43740
|
+
description: `This ${manualMemo.loadInstr.value.kind === 'PropertyLoad'
|
|
43741
|
+
? 'React.useMemo'
|
|
43742
|
+
: 'useMemo'} callback doesn't return a value. useMemo is for computing and caching values, not for arbitrary side effects.`,
|
|
43743
|
+
suggestions: null,
|
|
43744
|
+
}).withDetail({
|
|
43745
|
+
kind: 'error',
|
|
43746
|
+
loc: instr.value.loc,
|
|
43747
|
+
message: 'useMemo() callbacks must return a value',
|
|
43748
|
+
}));
|
|
43749
|
+
}
|
|
43750
|
+
}
|
|
43751
|
+
}
|
|
43696
43752
|
instr.value = getManualMemoizationReplacement(fnPlace, instr.value.loc, manualMemo.kind);
|
|
43697
43753
|
if (isValidationEnabled) {
|
|
43698
43754
|
if (!sidemap.functions.has(fnPlace.identifier.id)) {
|
|
43699
|
-
|
|
43700
|
-
|
|
43755
|
+
errors.pushDiagnostic(CompilerDiagnostic.create({
|
|
43756
|
+
severity: ErrorSeverity.InvalidReact,
|
|
43757
|
+
category: `Expected the first argument to be an inline function expression`,
|
|
43758
|
+
description: `Expected the first argument to be an inline function expression`,
|
|
43701
43759
|
suggestions: [],
|
|
43760
|
+
}).withDetail({
|
|
43761
|
+
kind: 'error',
|
|
43702
43762
|
loc: fnPlace.loc,
|
|
43703
|
-
|
|
43763
|
+
message: `Expected the first argument to be an inline function expression`,
|
|
43764
|
+
}));
|
|
43765
|
+
continue;
|
|
43704
43766
|
}
|
|
43705
43767
|
const memoDecl = manualMemo.kind === 'useMemo'
|
|
43706
43768
|
? instr.lvalue
|
|
@@ -43747,6 +43809,7 @@ function dropManualMemoization(func) {
|
|
|
43747
43809
|
markInstructionIds(func.body);
|
|
43748
43810
|
}
|
|
43749
43811
|
}
|
|
43812
|
+
return errors.asResult();
|
|
43750
43813
|
}
|
|
43751
43814
|
function findOptionalPlaces(fn) {
|
|
43752
43815
|
const optionals = new Set();
|
|
@@ -43790,6 +43853,17 @@ function findOptionalPlaces(fn) {
|
|
|
43790
43853
|
}
|
|
43791
43854
|
return optionals;
|
|
43792
43855
|
}
|
|
43856
|
+
function hasNonVoidReturn(func) {
|
|
43857
|
+
for (const [, block] of func.body.blocks) {
|
|
43858
|
+
if (block.terminal.kind === 'return') {
|
|
43859
|
+
if (block.terminal.returnVariant === 'Explicit' ||
|
|
43860
|
+
block.terminal.returnVariant === 'Implicit') {
|
|
43861
|
+
return true;
|
|
43862
|
+
}
|
|
43863
|
+
}
|
|
43864
|
+
}
|
|
43865
|
+
return false;
|
|
43866
|
+
}
|
|
43793
43867
|
|
|
43794
43868
|
class StableSidemap {
|
|
43795
43869
|
constructor(env) {
|
|
@@ -49233,6 +49307,7 @@ function emitSelectorFn(env, keys) {
|
|
|
49233
49307
|
terminal: {
|
|
49234
49308
|
id: makeInstructionId(0),
|
|
49235
49309
|
kind: 'return',
|
|
49310
|
+
returnVariant: 'Explicit',
|
|
49236
49311
|
loc: GeneratedSource,
|
|
49237
49312
|
value: arrayInstr.lvalue,
|
|
49238
49313
|
effects: null,
|
|
@@ -49664,6 +49739,7 @@ function emitOutlinedFn(env, jsx, oldProps, globals) {
|
|
|
49664
49739
|
terminal: {
|
|
49665
49740
|
id: makeInstructionId(0),
|
|
49666
49741
|
kind: 'return',
|
|
49742
|
+
returnVariant: 'Explicit',
|
|
49667
49743
|
loc: GeneratedSource,
|
|
49668
49744
|
value: instructions.at(-1).lvalue,
|
|
49669
49745
|
effects: null,
|
|
@@ -50484,7 +50560,7 @@ function runWithEnvironment(func, env) {
|
|
|
50484
50560
|
!env.config.enablePreserveExistingManualUseMemo &&
|
|
50485
50561
|
!env.config.disableMemoizationForDebugging &&
|
|
50486
50562
|
!env.config.enableChangeDetectionForDebugging) {
|
|
50487
|
-
dropManualMemoization(hir);
|
|
50563
|
+
dropManualMemoization(hir).unwrap();
|
|
50488
50564
|
log({ kind: 'hir', name: 'DropManualMemoization', value: hir });
|
|
50489
50565
|
}
|
|
50490
50566
|
inlineImmediatelyInvokedFunctionExpressions(hir);
|
|
@@ -52434,6 +52510,7 @@ const COMPILER_OPTIONS = {
|
|
|
52434
52510
|
validateNoImpureFunctionsInRender: true,
|
|
52435
52511
|
validateStaticComponents: true,
|
|
52436
52512
|
validateNoFreezingKnownMutableFunctions: true,
|
|
52513
|
+
validateNoVoidUseMemo: true,
|
|
52437
52514
|
}),
|
|
52438
52515
|
};
|
|
52439
52516
|
const rule$1 = {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-react-hooks",
|
|
3
3
|
"description": "ESLint rules for React Hooks",
|
|
4
|
-
"version": "6.1.0-canary-
|
|
4
|
+
"version": "6.1.0-canary-9be531cd-20250729",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/facebook/react.git",
|