Skip to content

Conversation

@manNomi
Copy link

@manNomi manNomi commented Nov 10, 2025

Provides different warning messages based on code cleanliness:

  1. Clean code (no eslint-disable):
    • ⚠️ Informational tone
    • Simple guidance: add 'use no memo' directive
  2. With eslint-disable:
    • 🚨 Critical warning
    • Explains: hook will NOT be memoized
    • Impact: breaks parent memoization
    • 3 concrete solutions

Changes:

  • ReactCompiler.ts: Detect eslint-disable and show warnings despite suppression
  • InferMutationAliasingEffects.ts: Improved default message
  • Added test fixtures: error.incompatible-with-eslint-disable.js

Implementation:
Works at ESLint plugin level only - no compiler core modifications.
The ESLint plugin detects eslint-disable comments and shows critical
warnings anyway, without changing compilation behavior.

Benefits:

  • Simpler, safer implementation (ESLint plugin only)
  • Context-appropriate severity
  • Clear impact explanation
  • Situation-specific solutions
  • Prevents developer confusion

Based on real debugging experience - spent hours on silent failure
when eslint-disable suppressed critical warnings.

Real-world example:
https://github.com/manNomi/rc-test/blob/main/src/hooks/useIncompatibleMovieList.ts#L49-L90

Testing:

# Automated regression test
cd compiler/packages/babel-plugin-react-compiler
yarn snap --testPathPattern=error.incompatible-with-eslint-disable

# Build verification
yarn build
yarn prettier
yarn linc

All tests pass. Changes isolated to ESLint plugin only.

Provides different warning messages based on code cleanliness:

1. Clean code (no eslint-disable):
   - ⚠️ Informational tone
   - Simple guidance: add 'use no memo' directive

2. With eslint-disable:
   - 🚨 Critical warning
   - Explains: hook will NOT be memoized
   - Impact: breaks parent memoization
   - 3 concrete solutions

Changes:
- InferMutationAliasingEffects.ts: Improved default message
- Program.ts: Skip suppression check in noEmit mode
- ReactCompiler.ts: Detect eslint-disable + customize message

Benefits:
- Context-appropriate severity
- Clear impact explanation
- Situation-specific solutions
- Prevents developer confusion

Based on real debugging experience - spent hours on silent failure
when eslint-disable suppressed critical warnings.
Critical improvement: Message now clearly states developers should add
'use no memo' to COMPONENTS that use the affected hook, not the hook itself.

Problem scenario:
- Custom hook with eslint-disable → NOT memoized
- Component using that hook → IS memoized
- Result: Hook returns new objects → breaks component memoization

Correct solution:
Add 'use no memo' to the COMPONENT, not just the hook.

Updated message:
- Clearly states: 'Add "use no memo" to COMPONENTS that use this hook'
- Shows example with component code
- Explains why: prevents mismatch between hook and component memoization

This matches the actual debugging experience where moving code from
component to custom hook caused the issue.
Added detailed PR document including:
- Real debugging experience (3-4 hours wasted)
- Phase-by-phase explanation of the bug
- Why direct use worked but custom hook didn't
- Silent failure chain explanation
- Context-aware solution
- Before/after comparisons

Key story:
- Working code → Refactored to custom hook → Broke mysteriously
- eslint-disable suppressed warnings
- Hook not memoized, component IS memoized → conflict
- No guidance, spent hours debugging

This PR would have saved those hours with clear warnings.
Complete PR template with:
- Detailed motivation (3-4 hour debugging story)
- Comprehensive testing documentation
- Manual testing results for both scenarios
- Real project reproduction
- Edge case coverage
- Exact verification of problem resolution

All tests verified manually due to rimraf dependency.
Core functionality confirmed working correctly.
Key reframing:
- NOT a 'mysterious bug' (too vague)
- IS an 'eslint-disable-next-line scope issue' (clear)

Developer expects:
- Line 14: eslint-disable-next-line → only line 15 affected
- Line 10: incompatible API warning should STILL show

Reality:
- Line 14: eslint-disable-next-line → ENTIRE FUNCTION affected
- Line 10: warning HIDDEN (not expected!)

This is about information delivery:
- eslint-disable incorrectly hides critical warnings
- Developers need those warnings to write correct code
- PR ensures warnings are shown with clear explanations

Much clearer and more focused than 'debugging mystery'.
Added test case that verifies incompatible-library warnings are shown
even when eslint-disable-next-line is present in the function.

Test files:
- error.incompatible-with-eslint-disable.js: Test input with eslint-disable
- error.incompatible-with-eslint-disable.expect.md: Expected warning output

This test ensures the exact scenario that caused the 3-4 hour debugging
session is now caught by automated tests.
Updated PR documentation to include:
- Test 0: Added automated regression test
- Clear file paths to test files
- Command to run the test
- Explanation of what the test verifies

This addresses the suggestion to mention automated test cases
in the test plan, making the PR more complete and credible.
@meta-cla
Copy link

meta-cla bot commented Nov 10, 2025

Hi @manNomi!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at [email protected]. Thanks!

Removed documentation files that were for reference only:
- CONTEXT_AWARE_PR.md
- FINAL_PR_DOCUMENT.md
- FINAL_SUMMARY.md
- PR_FINAL.md
- PR_TEMPLATE_FILLED.md

Kept only essential files:
- Test files (error.incompatible-with-eslint-disable.*)
- Code changes (Program.ts, ReactCompiler.ts, InferMutationAliasingEffects.ts)
@meta-cla meta-cla bot added the CLA Signed label Nov 10, 2025
@manNomi manNomi changed the title feat: add context-aware warnings for incompatible APIs feat: show critical warnings despite eslint-disable-next-line scope issue Nov 10, 2025
manNomi and others added 3 commits November 11, 2025 11:00
Design improvement:
- Keep Program.ts unchanged (respects suppressions as intended)
- ESLint plugin directly scans AST for incompatible APIs
- No noEmit semantic expansion - cleaner separation of concerns

The ESLint plugin now:
1. Receives CompileError events from React Compiler (existing)
2. Directly visits functions with suppressions (new)
3. Reports critical warnings when incompatible APIs are detected

This approach:
- Maintains React Compiler's core safety guarantees
- Avoids expanding noEmit semantics beyond 'no output generation'
- Provides clear, context-aware warnings to developers
- Less controversial for upstream merge
- Single-line function signatures
- Multi-line if conditions for long expressions
- Use double quotes to avoid escaping apostrophes
Comment on lines +2461 to +2469
'⚠️ Incompatible API detected\n\n' +
'This API cannot be safely memoized.\n\n' +
'**Recommendation:**\n' +
'Add "use no memo" directive to opt-out of memoization:\n\n' +
'function useCustomHook() {\n' +
' "use no memo";\n' +
' const api = useIncompatibleAPI({...});\n' +
' ...\n' +
'}',
Copy link
Member

Choose a reason for hiding this comment

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

This is incorrect, the point of this validation is that the compiler automatically skips code calling incompatible libraries

Copy link
Member

@josephsavona josephsavona left a comment

Choose a reason for hiding this comment

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

First, it isn't clear what problem this is solving. If there's an issue with the compiler, please start by filing an issue, where we can discuss appropriate remediations.

Second, we are intentionally not duplicating rules between the compiler and linter (there is some minor duplication that we're working to get rid of over time w the compiler as the sole implementation, so we definitely would not accept a change like this.

Third - this is probably AI spam, but I'm giving you the benefit of the doubt that it's well intentioned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants