Skip to content

Fix bug: validate <field> before <operation> to prevent <error/crash>#402

Closed
everettbu wants to merge 1 commit into
mainfrom
patch-1-35597
Closed

Fix bug: validate <field> before <operation> to prevent <error/crash>#402
everettbu wants to merge 1 commit into
mainfrom
patch-1-35597

Conversation

@everettbu

Copy link
Copy Markdown

Mirror of facebook/react#35597
Original author: harshilparekh874


Refactor mutation diagnostics for better clarity and specificity. Introduce checks for prior hoisted accesses and provide more detailed error messages.

Summary

How did you test this change?

Refactor mutation diagnostics for better clarity and specificity. Introduce checks for prior hoisted accesses and provide more detailed error messages.
@greptile-apps

greptile-apps Bot commented Jan 22, 2026

Copy link
Copy Markdown

Greptile Summary

This PR refactors mutation diagnostic error handling to provide more specific error messages when hoisted context declarations are accessed before they are declared. However, the PR contains a critical syntax error that prevents compilation.

Critical Issue:

  • 92 lines (1263-1355) contain literal + characters from diff markers that were not properly removed during merge/patch application
  • This syntax error will cause TypeScript/JavaScript compilation to fail completely
  • The file cannot be parsed or executed in its current state

Intended Changes (obscured by syntax error):

  • Added conditional logic to check for prior hoisted access before generating specialized "accessed before declared" diagnostics
  • Provides fallback to generic "cannot be modified" error message when no distinct prior hoisted access is recorded
  • Improved specificity of error messages for better developer experience

Confidence Score: 0/5

  • This PR cannot be merged due to critical syntax errors that will cause build failure
  • Score reflects critical compilation-blocking syntax errors from improperly removed diff markers. The file contains 92 lines with literal + characters that make the code unparseable. This must be fixed before any code review of the logic can be meaningful.
  • InferMutationAliasingEffects.ts requires immediate attention to remove diff markers before compilation can succeed

Important Files Changed

Filename Overview
compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts Critical syntax error: 92 lines contain literal + diff markers that will cause compilation to fail

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +1263 to +1355
+ if (
+ mutationKind === 'mutate-frozen' &&
+ context.hoistedContextDeclarations.has(
+ effect.value.identifier.declarationId,
+ )
+ ) {
+ const variable =
+ effect.value.identifier.name !== null &&
+ effect.value.identifier.name.kind === 'named'
+ ? `\`${effect.value.identifier.name.value}\``
+ : null;
+ const hoistedAccess = context.hoistedContextDeclarations.get(
+ effect.value.identifier.declarationId,
+ );
+
+ // Only produce the specialized "accessed before it is declared" diagnostic
+ // when we actually observed a prior distinct hoisted access location.
+ // If we don't have a recorded prior access location (hoistedAccess === null)
+ // or the prior access location is the same as the declaration, fall back to
+ // the generic "This value cannot be modified" diagnostic below.
+ if (hoistedAccess != null && hoistedAccess.loc != effect.value.loc) {
+ const diagnostic = CompilerDiagnostic.create({
+ category: ErrorCategory.Immutability,
+ reason: '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`,
+ });
+ diagnostic.withDetails({
+ kind: 'error',
+ loc: hoistedAccess.loc,
+ message: `${variable ?? 'variable'} accessed before it is declared`,
+ });
+ diagnostic.withDetails({
+ kind: 'error',
+ loc: effect.value.loc,
+ message: `${variable ?? 'variable'} is declared here`,
+ });
+
+ applyEffect(
+ context,
+ state,
+ {
+ kind: 'MutateFrozen',
+ place: effect.value,
+ error: diagnostic,
+ },
+ initialized,
+ effects,
+ );
+ } else {
+ // No prior distinct hoisted access recorded: fall back to the generic diagnostic
+ // that explains why the value cannot be modified.
+ const reason = getWriteErrorReason({
+ kind: value.kind,
+ reason: value.reason,
+ });
+ const variableName =
+ effect.value.identifier.name !== null &&
+ effect.value.identifier.name.kind === 'named'
+ ? `\`${effect.value.identifier.name.value}\``
+ : 'value';
+ const diagnostic = CompilerDiagnostic.create({
+ category: ErrorCategory.Immutability,
+ reason: 'This value cannot be modified',
+ description: reason,
+ }).withDetails({
+ kind: 'error',
+ loc: effect.value.loc,
+ message: `${variableName} cannot be modified`,
+ });
+ if (
+ effect.kind === 'Mutate' &&
+ effect.reason?.kind === 'AssignCurrentProperty'
+ ) {
+ diagnostic.withDetails({
+ kind: 'hint',
+ message: `Hint: If this value is a Ref (value returned by \`useRef()\`), rename the variable to end in "Ref".`,
+ });
+ }
+
+ applyEffect(
+ context,
+ state,
+ {
+ kind:
+ value.kind === ValueKind.Frozen ? 'MutateFrozen' : 'MutateGlobal',
+ place: effect.value,
+ error: diagnostic,
+ },
+ initialized,
+ effects,
+ );
+ }
+ } else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax: Invalid syntax: literal + characters at the start of lines

This entire code block has diff markers (+) that were not removed during merge/patch application. These are literal characters in the source file, not just visual diff indicators. This will cause compilation to fail.

Remove the leading + characters from all lines in this range (92 lines total from line 1263-1355).

Suggested change
+ if (
+ mutationKind === 'mutate-frozen' &&
+ context.hoistedContextDeclarations.has(
+ effect.value.identifier.declarationId,
+ )
+ ) {
+ const variable =
+ effect.value.identifier.name !== null &&
+ effect.value.identifier.name.kind === 'named'
+ ? `\`${effect.value.identifier.name.value}\``
+ : null;
+ const hoistedAccess = context.hoistedContextDeclarations.get(
+ effect.value.identifier.declarationId,
+ );
+
+ // Only produce the specialized "accessed before it is declared" diagnostic
+ // when we actually observed a prior distinct hoisted access location.
+ // If we don't have a recorded prior access location (hoistedAccess === null)
+ // or the prior access location is the same as the declaration, fall back to
+ // the generic "This value cannot be modified" diagnostic below.
+ if (hoistedAccess != null && hoistedAccess.loc != effect.value.loc) {
+ const diagnostic = CompilerDiagnostic.create({
+ category: ErrorCategory.Immutability,
+ reason: '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`,
+ });
+ diagnostic.withDetails({
+ kind: 'error',
+ loc: hoistedAccess.loc,
+ message: `${variable ?? 'variable'} accessed before it is declared`,
+ });
+ diagnostic.withDetails({
+ kind: 'error',
+ loc: effect.value.loc,
+ message: `${variable ?? 'variable'} is declared here`,
+ });
+
+ applyEffect(
+ context,
+ state,
+ {
+ kind: 'MutateFrozen',
+ place: effect.value,
+ error: diagnostic,
+ },
+ initialized,
+ effects,
+ );
+ } else {
+ // No prior distinct hoisted access recorded: fall back to the generic diagnostic
+ // that explains why the value cannot be modified.
+ const reason = getWriteErrorReason({
+ kind: value.kind,
+ reason: value.reason,
+ });
+ const variableName =
+ effect.value.identifier.name !== null &&
+ effect.value.identifier.name.kind === 'named'
+ ? `\`${effect.value.identifier.name.value}\``
+ : 'value';
+ const diagnostic = CompilerDiagnostic.create({
+ category: ErrorCategory.Immutability,
+ reason: 'This value cannot be modified',
+ description: reason,
+ }).withDetails({
+ kind: 'error',
+ loc: effect.value.loc,
+ message: `${variableName} cannot be modified`,
+ });
+ if (
+ effect.kind === 'Mutate' &&
+ effect.reason?.kind === 'AssignCurrentProperty'
+ ) {
+ diagnostic.withDetails({
+ kind: 'hint',
+ message: `Hint: If this value is a Ref (value returned by \`useRef()\`), rename the variable to end in "Ref".`,
+ });
+ }
+
+ applyEffect(
+ context,
+ state,
+ {
+ kind:
+ value.kind === ValueKind.Frozen ? 'MutateFrozen' : 'MutateGlobal',
+ place: effect.value,
+ error: diagnostic,
+ },
+ initialized,
+ effects,
+ );
+ }
+ } else {
if (
mutationKind === 'mutate-frozen' &&
context.hoistedContextDeclarations.has(
effect.value.identifier.declarationId,
)
) {
const variable =
effect.value.identifier.name !== null &&
effect.value.identifier.name.kind === 'named'
? `\`${effect.value.identifier.name.value}\``
: null;
const hoistedAccess = context.hoistedContextDeclarations.get(
effect.value.identifier.declarationId,
);
// Only produce the specialized "accessed before it is declared" diagnostic
// when we actually observed a prior distinct hoisted access location.
// If we don't have a recorded prior access location (hoistedAccess === null)
// or the prior access location is the same as the declaration, fall back to
// the generic "This value cannot be modified" diagnostic below.
if (hoistedAccess != null && hoistedAccess.loc != effect.value.loc) {
const diagnostic = CompilerDiagnostic.create({
category: ErrorCategory.Immutability,
reason: '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`,
});
diagnostic.withDetails({
kind: 'error',
loc: hoistedAccess.loc,
message: `${variable ?? 'variable'} accessed before it is declared`,
});
diagnostic.withDetails({
kind: 'error',
loc: effect.value.loc,
message: `${variable ?? 'variable'} is declared here`,
});
applyEffect(
context,
state,
{
kind: 'MutateFrozen',
place: effect.value,
error: diagnostic,
},
initialized,
effects,
);
} else {
// No prior distinct hoisted access recorded: fall back to the generic diagnostic
// that explains why the value cannot be modified.
const reason = getWriteErrorReason({
kind: value.kind,
reason: value.reason,
});
const variableName =
effect.value.identifier.name !== null &&
effect.value.identifier.name.kind.kind === 'named'
? `\`${effect.value.identifier.name.value}\``
: 'value';
const diagnostic = CompilerDiagnostic.create({
category: ErrorCategory.Immutability,
reason: 'This value cannot be modified',
description: reason,
}).withDetails({
kind: 'error',
loc: effect.value.loc,
message: `${variableName} cannot be modified`,
});
if (
effect.kind === 'Mutate' &&
effect.reason?.kind === 'AssignCurrentProperty'
) {
diagnostic.withDetails({
kind: 'hint',
message: `Hint: If this value is a Ref (value returned by \`useRef()\`), rename the variable to end in "Ref".`,
});
}
applyEffect(
context,
state,
{
kind:
value.kind === ValueKind.Frozen ? 'MutateFrozen' : 'MutateGlobal',
place: effect.value,
error: diagnostic,
},
initialized,
effects,
);
}
} else {
Prompt To Fix With AI
This is a comment left during a code review.
Path: compiler/packages/babel-plugin-react-compiler/src/Inference/InferMutationAliasingEffects.ts
Line: 1263:1355

Comment:
**syntax:** Invalid syntax: literal `+` characters at the start of lines

This entire code block has diff markers (`+`) that were not removed during merge/patch application. These are literal characters in the source file, not just visual diff indicators. This will cause compilation to fail.

Remove the leading `+` characters from all lines in this range (92 lines total from line 1263-1355).

```suggestion
        if (
          mutationKind === 'mutate-frozen' &&
          context.hoistedContextDeclarations.has(
            effect.value.identifier.declarationId,
          )
        ) {
          const variable =
            effect.value.identifier.name !== null &&
            effect.value.identifier.name.kind === 'named'
              ? `\`${effect.value.identifier.name.value}\``
              : null;
          const hoistedAccess = context.hoistedContextDeclarations.get(
            effect.value.identifier.declarationId,
          );

          // Only produce the specialized "accessed before it is declared" diagnostic
          // when we actually observed a prior distinct hoisted access location.
          // If we don't have a recorded prior access location (hoistedAccess === null)
          // or the prior access location is the same as the declaration, fall back to
          // the generic "This value cannot be modified" diagnostic below.
          if (hoistedAccess != null && hoistedAccess.loc != effect.value.loc) {
            const diagnostic = CompilerDiagnostic.create({
              category: ErrorCategory.Immutability,
              reason: '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`,
            });
            diagnostic.withDetails({
              kind: 'error',
              loc: hoistedAccess.loc,
              message: `${variable ?? 'variable'} accessed before it is declared`,
            });
            diagnostic.withDetails({
              kind: 'error',
              loc: effect.value.loc,
              message: `${variable ?? 'variable'} is declared here`,
            });

            applyEffect(
              context,
              state,
              {
                kind: 'MutateFrozen',
                place: effect.value,
                error: diagnostic,
              },
              initialized,
              effects,
            );
          } else {
            // No prior distinct hoisted access recorded: fall back to the generic diagnostic
            // that explains why the value cannot be modified.
            const reason = getWriteErrorReason({
              kind: value.kind,
              reason: value.reason,
            });
            const variableName =
              effect.value.identifier.name !== null &&
              effect.value.identifier.name.kind.kind === 'named'
                ? `\`${effect.value.identifier.name.value}\``
                : 'value';
            const diagnostic = CompilerDiagnostic.create({
              category: ErrorCategory.Immutability,
              reason: 'This value cannot be modified',
              description: reason,
            }).withDetails({
              kind: 'error',
              loc: effect.value.loc,
              message: `${variableName} cannot be modified`,
            });
            if (
              effect.kind === 'Mutate' &&
              effect.reason?.kind === 'AssignCurrentProperty'
            ) {
              diagnostic.withDetails({
                kind: 'hint',
                message: `Hint: If this value is a Ref (value returned by \`useRef()\`), rename the variable to end in "Ref".`,
              });
            }

            applyEffect(
              context,
              state,
              {
                kind:
                  value.kind === ValueKind.Frozen ? 'MutateFrozen' : 'MutateGlobal',
                place: effect.value,
                error: diagnostic,
              },
              initialized,
              effects,
            );
          }
        } else {
```

How can I resolve this? If you propose a fix, please make it concise.

@github-actions

Copy link
Copy Markdown

This pull request has been automatically marked as stale. If this pull request is still relevant, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize reviewing it yet. Your contribution is very much appreciated.

@github-actions github-actions Bot added the Resolution: Stale Automatically closed due to inactivity label Apr 22, 2026
@github-actions

Copy link
Copy Markdown

Closing this pull request after a prolonged period of inactivity. If this issue is still present in the latest release, please ask for this pull request to be reopened. Thank you!

@github-actions github-actions Bot closed this Apr 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed Resolution: Stale Automatically closed due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants