mirror of
https://github.com/react/react.git
synced 2026-09-28 13:14:03 +08:00
[rust-compiler] Propagate block lowering errors (#37364)
Propagate errors from block lowering instead of continuing with incomplete HIR. This prevents the Rust compiler from emitting partial output when it encounters unsupported syntax, including nested TypeScript `this` parameters. This landed earlier as #37232, which exposed an existing FBT diagnostic ordering issue. Resolve local FBT bindings before checking whether an `<fbt>` tag comes from a module import, so the compiler reports the earlier, more useful Todo instead of a later invariant. Run from the repository root: - `yarn --cwd compiler workspace babel-plugin-react-compiler-rust test` - `cargo test --manifest-path compiler/Cargo.toml -p react_compiler_lowering`
This commit is contained in:
@@ -2055,20 +2055,19 @@ fn lower_expression(
|
||||
JsxTag::Builtin(b) => b.name.clone(),
|
||||
_ => "fbt".to_string(),
|
||||
};
|
||||
// Get the opening element's name identifier and check if it's a local binding
|
||||
if let react_compiler_ast::jsx::JSXElementName::JSXIdentifier(jsx_id) =
|
||||
&jsx_element.opening_element.name
|
||||
{
|
||||
let id_loc = convert_opt_loc(&jsx_id.base.loc);
|
||||
// Check if fbt/fbs tag name resolves to a local binding.
|
||||
// JSX identifiers may not be in our position-based reference map,
|
||||
// so check if ANY binding with this name exists in the function scope.
|
||||
let is_local_binding = builder.has_local_binding(&jsx_id.name);
|
||||
if is_local_binding {
|
||||
// Record as a Diagnostic (not ErrorDetail) to match TS behavior
|
||||
// where CompilerError.invariant creates a CompilerDiagnostic.
|
||||
// TS invariant() throws immediately, so only the first fbt error
|
||||
// is reported. We return Err to match this behavior.
|
||||
let error_count = builder.environment().error_count();
|
||||
let local_binding =
|
||||
builder.resolve_local_binding_by_name(&jsx_id.name, id_loc.clone())?;
|
||||
if builder.environment().error_count() > error_count {
|
||||
// If fbt introduced a new error, return it specifically
|
||||
return Err(builder.environment_mut().take_errors_since(error_count));
|
||||
}
|
||||
|
||||
if local_binding.is_some() {
|
||||
let reason = format!("<{}> tags should be module-level imports", tag_name);
|
||||
return Err(CompilerDiagnostic::new(
|
||||
ErrorCategory::Invariant,
|
||||
@@ -2589,8 +2588,12 @@ fn lower_block_statement(
|
||||
block: &react_compiler_ast::statements::BlockStatement,
|
||||
parent_scope: Option<react_compiler_ast::scope::ScopeId>,
|
||||
) -> Result<(), CompilerError> {
|
||||
let _ = lower_block_statement_inner(builder, block, None, parent_scope);
|
||||
Ok(())
|
||||
Ok(lower_block_statement_inner(
|
||||
builder,
|
||||
block,
|
||||
None,
|
||||
parent_scope,
|
||||
)?)
|
||||
}
|
||||
|
||||
fn lower_block_statement_with_scope(
|
||||
@@ -2598,8 +2601,12 @@ fn lower_block_statement_with_scope(
|
||||
block: &react_compiler_ast::statements::BlockStatement,
|
||||
scope_override: react_compiler_ast::scope::ScopeId,
|
||||
) -> Result<(), CompilerError> {
|
||||
let _ = lower_block_statement_inner(builder, block, Some(scope_override), None);
|
||||
Ok(())
|
||||
Ok(lower_block_statement_inner(
|
||||
builder,
|
||||
block,
|
||||
Some(scope_override),
|
||||
None,
|
||||
)?)
|
||||
}
|
||||
|
||||
fn lower_block_statement_inner(
|
||||
|
||||
@@ -741,22 +741,26 @@ impl<'a> HirBuilder<'a> {
|
||||
self.env.record_diagnostic(diagnostic);
|
||||
}
|
||||
|
||||
/// Check if a name has a local binding (non-module-level).
|
||||
/// This is used for checking if fbt/fbs JSX tags are local bindings
|
||||
/// (which is not supported).
|
||||
pub fn has_local_binding(&self, name: &str) -> bool {
|
||||
if let Some(binding) = self
|
||||
/// Resolve a local binding when the AST reference has no scope mapping.
|
||||
pub fn resolve_local_binding_by_name(
|
||||
&mut self,
|
||||
name: &str,
|
||||
loc: Option<SourceLocation>,
|
||||
) -> Result<Option<IdentifierId>, CompilerError> {
|
||||
if let Some((binding_id, binding)) = self
|
||||
.scope_info
|
||||
.find_binding_in_descendants(name, self.component_scope)
|
||||
.find_binding_id_in_descendants(name, self.component_scope)
|
||||
{
|
||||
// When component_scope == program_scope (e2e path where scope info
|
||||
// is extracted from the function itself), any binding found is local.
|
||||
if self.component_scope == self.scope_info.program_scope {
|
||||
return true;
|
||||
if self.component_scope != self.scope_info.program_scope
|
||||
&& binding.scope == self.scope_info.program_scope
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
return binding.scope != self.scope_info.program_scope;
|
||||
return self
|
||||
.resolve_binding_with_loc(name, binding_id, loc)
|
||||
.map(Some);
|
||||
}
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Return the kind of the current block.
|
||||
@@ -891,7 +895,7 @@ impl<'a> HirBuilder<'a> {
|
||||
),
|
||||
loc: error_loc,
|
||||
suggestions: None,
|
||||
})?;
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user