main
rs 100 lines 4.47 KB
Raw
1 // Copyright (c) Meta Platforms, Inc. and affiliates.
2 //
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the root directory of this source tree.
5
6 //! Validates against components that are created dynamically and whose identity
7 //! is not guaranteed to be stable (which would cause the component to reset on
8 //! each re-render).
9 //!
10 //! Port of ValidateStaticComponents.ts.
11
12 use rustc_hash::FxHashMap;
13
14 use react_compiler_diagnostics::{
15 CompilerDiagnostic, CompilerDiagnosticDetail, CompilerError, ErrorCategory, SourceLocation,
16 };
17 use react_compiler_hir::{HirFunction, IdentifierId, InstructionValue, JsxTag};
18
19 /// Validates that components used in JSX are not dynamically created during render.
20 ///
21 /// Returns a CompilerError containing all diagnostics found (may be empty).
22 /// Called via `env.logErrors()` pattern in Pipeline.ts.
23 pub fn validate_static_components(func: &HirFunction) -> CompilerError {
24 let mut error = CompilerError::new();
25 let mut known_dynamic_components: FxHashMap<IdentifierId, Option<SourceLocation>> =
26 FxHashMap::default();
27
28 for (_block_id, block) in &func.body.blocks {
29 // Process phis: propagate dynamic component knowledge through phi nodes
30 'phis: for phi in &block.phis {
31 for (_pred, operand) in &phi.operands {
32 if let Some(loc) = known_dynamic_components.get(&operand.identifier) {
33 known_dynamic_components.insert(phi.place.identifier, *loc);
34 continue 'phis;
35 }
36 }
37 }
38
39 // Process instructions
40 for &instr_id in &block.instructions {
41 let instr = &func.instructions[instr_id.0 as usize];
42 let lvalue_id = instr.lvalue.identifier;
43 let value = &instr.value;
44
45 match value {
46 InstructionValue::FunctionExpression { loc, .. }
47 | InstructionValue::NewExpression { loc, .. }
48 | InstructionValue::MethodCall { loc, .. }
49 | InstructionValue::CallExpression { loc, .. } => {
50 known_dynamic_components.insert(lvalue_id, *loc);
51 }
52 InstructionValue::LoadLocal { place, .. } => {
53 if let Some(loc) = known_dynamic_components.get(&place.identifier) {
54 known_dynamic_components.insert(lvalue_id, *loc);
55 }
56 }
57 InstructionValue::StoreLocal {
58 lvalue, value: val, ..
59 } => {
60 if let Some(loc) = known_dynamic_components.get(&val.identifier) {
61 let loc = *loc;
62 known_dynamic_components.insert(lvalue_id, loc);
63 known_dynamic_components.insert(lvalue.place.identifier, loc);
64 }
65 }
66 InstructionValue::JsxExpression { tag, .. } => {
67 if let JsxTag::Place(tag_place) = tag {
68 if let Some(location) = known_dynamic_components.get(&tag_place.identifier)
69 {
70 let location = *location;
71 let diagnostic = CompilerDiagnostic::new(
72 ErrorCategory::StaticComponents,
73 "Cannot create components during render",
74 Some("Components created during render will reset their state each time they are created. Declare components outside of render".to_string()),
75 )
76 .with_detail(CompilerDiagnosticDetail::Error {
77 loc: tag_place.loc,
78 message: Some(
79 "This component is created during render".to_string(),
80 ),
81 identifier_name: None,
82 })
83 .with_detail(CompilerDiagnosticDetail::Error {
84 loc: location,
85 message: Some(
86 "The component is created during render here".to_string(),
87 ),
88 identifier_name: None,
89 });
90 error.push_diagnostic(diagnostic);
91 }
92 }
93 }
94 _ => {}
95 }
96 }
97 }
98
99 error
100 }