1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+import {CompilerError, ErrorSeverity} from '../CompilerError';
9
+import {HIRFunction, IdentifierId, SourceLocation} from '../HIR';
10
+import {Err, Ok, Result} from '../Utils/Result';
11
+
12
+/**
13
+ * Validates against components that are created dynamically and whose identity is not guaranteed
14
+ * to be stable (which would cause the component to reset on each re-render).
15
+ */
16
+export function validateStaticComponents(
17
+ fn: HIRFunction,
18
+): Result<void, CompilerError> {
19
+ const error = new CompilerError();
20
+ const knownDynamicComponents = new Map<IdentifierId, SourceLocation>();
21
+ for (const block of fn.body.blocks.values()) {
22
+ phis: for (const phi of block.phis) {
23
+ for (const operand of phi.operands.values()) {
24
+ const loc = knownDynamicComponents.get(operand.identifier.id);
25
+ if (loc != null) {
26
+ knownDynamicComponents.set(phi.place.identifier.id, loc);
27
+ continue phis;
28
+ }
29
+ }
30
+ }
31
+ for (const instr of block.instructions) {
32
+ const {lvalue, value} = instr;
33
+ switch (value.kind) {
34
+ case 'FunctionExpression':
35
+ case 'NewExpression':
36
+ case 'MethodCall':
37
+ case 'CallExpression': {
38
+ knownDynamicComponents.set(lvalue.identifier.id, value.loc);
39
+ break;
40
+ }
41
+ case 'LoadLocal': {
42
+ const loc = knownDynamicComponents.get(value.place.identifier.id);
43
+ if (loc != null) {
44
+ knownDynamicComponents.set(lvalue.identifier.id, loc);
45
+ }
46
+ break;
47
+ }
48
+ case 'StoreLocal': {
49
+ const loc = knownDynamicComponents.get(value.value.identifier.id);
50
+ if (loc != null) {
51
+ knownDynamicComponents.set(lvalue.identifier.id, loc);
52
+ knownDynamicComponents.set(value.lvalue.place.identifier.id, loc);
53
+ }
54
+ break;
55
+ }
56
+ case 'JsxExpression': {
57
+ if (value.tag.kind === 'Identifier') {
58
+ const location = knownDynamicComponents.get(
59
+ value.tag.identifier.id,
60
+ );
61
+ if (location != null) {
62
+ error.push({
63
+ reason: `Components created during render will reset their state each time they are created. Declare components outside of render. `,
64
+ severity: ErrorSeverity.InvalidReact,
65
+ loc: value.tag.loc,
66
+ description: null,
67
+ suggestions: null,
68
+ });
69
+ error.push({
70
+ reason: `The component may be created during render`,
71
+ severity: ErrorSeverity.InvalidReact,
72
+ loc: location,
73
+ description: null,
74
+ suggestions: null,
75
+ });
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ if (error.hasErrors()) {
83
+ return Err(error);
84
+ } else {
85
+ return Ok(undefined);
86
+ }
87
+}