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
+import { CompilerError, EnvironmentConfig } from "..";
8
+import { HIRFunction, IdentifierId } from "../HIR";
9
+import { DEFAULT_GLOBALS } from "../HIR/Globals";
10
+
11
+export function validateNoCapitalizedCalls(fn: HIRFunction): void {
12
+ const envConfig: EnvironmentConfig = fn.env.config;
13
+ const ALLOW_LIST = new Set([
14
+ ...DEFAULT_GLOBALS.keys(),
15
+ ...(envConfig.validateNoCapitalizedCalls ?? []),
16
+ ]);
17
+ /*
18
+ * The hook pattern may allow uppercase names, like React$useState, so we need to be sure that we
19
+ * do not error in those cases
20
+ */
21
+ const hookPattern =
22
+ envConfig.hookPattern != null ? new RegExp(envConfig.hookPattern) : null;
23
+ const isAllowed = (name: string): boolean => {
24
+ return (
25
+ ALLOW_LIST.has(name) || (hookPattern != null && hookPattern.test(name))
26
+ );
27
+ };
28
+
29
+ const capitalLoadGlobals = new Map<IdentifierId, string>();
30
+ for (const [, block] of fn.body.blocks) {
31
+ for (const { lvalue, value } of block.instructions) {
32
+ switch (value.kind) {
33
+ case "LoadGlobal": {
34
+ if (
35
+ value.name != "" &&
36
+ /^[A-Z]/.test(value.name) &&
37
+ // We don't want to flag CONSTANTS()
38
+ !(value.name.toUpperCase() === value.name) &&
39
+ !isAllowed(value.name)
40
+ ) {
41
+ capitalLoadGlobals.set(lvalue.identifier.id, value.name);
42
+ }
43
+
44
+ break;
45
+ }
46
+ case "CallExpression": {
47
+ const calleeIdentifier = value.callee.identifier.id;
48
+ const calleeName = capitalLoadGlobals.get(calleeIdentifier);
49
+ if (calleeName != null) {
50
+ CompilerError.throwInvalidReact({
51
+ reason: `Capitalized function calls may be calling components that use hooks, which make them dangerous to memoize. Ensure there are no hook calls in the function and rename it to begin with a lowercase letter to fix this error`,
52
+ description: `${calleeName} may be a component.`,
53
+ loc: value.loc,
54
+ suggestions: null,
55
+ });
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+}