main
ts 60 lines 2.35 KB
Raw
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 {CompilerDiagnostic, CompilerError} from '..';
9 import {ErrorCategory} from '../CompilerError';
10 import {BlockId, HIRFunction} from '../HIR';
11 import {Result} from '../Utils/Result';
12 import {retainWhere} from '../Utils/utils';
13
14 /**
15 * Developers may not be aware of error boundaries and lazy evaluation of JSX, leading them
16 * to use patterns such as `let el; try { el = <Component /> } catch { ... }` to attempt to
17 * catch rendering errors. Such code will fail to catch errors in rendering, but developers
18 * may not realize this right away.
19 *
20 * This validation pass validates against this pattern: specifically, it errors for JSX
21 * created within a try block. JSX is allowed within a catch statement, unless that catch
22 * is itself nested inside an outer try.
23 */
24 export function validateNoJSXInTryStatement(
25 fn: HIRFunction,
26 ): Result<void, CompilerError> {
27 const activeTryBlocks: Array<BlockId> = [];
28 const errors = new CompilerError();
29 for (const [, block] of fn.body.blocks) {
30 retainWhere(activeTryBlocks, id => id !== block.id);
31
32 if (activeTryBlocks.length !== 0) {
33 for (const instr of block.instructions) {
34 const {value} = instr;
35 switch (value.kind) {
36 case 'JsxExpression':
37 case 'JsxFragment': {
38 errors.pushDiagnostic(
39 CompilerDiagnostic.create({
40 category: ErrorCategory.ErrorBoundaries,
41 reason: 'Avoid constructing JSX within try/catch',
42 description: `React does not immediately render components when JSX is rendered, so any errors from this component will not be caught by the try/catch. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)`,
43 }).withDetails({
44 kind: 'error',
45 loc: value.loc,
46 message: 'Avoid constructing JSX within try/catch',
47 }),
48 );
49 break;
50 }
51 }
52 }
53 }
54
55 if (block.terminal.kind === 'try') {
56 activeTryBlocks.push(block.terminal.handler);
57 }
58 }
59 return errors.asResult();
60 }