text: /**
   * Copyright (c) Facebook, Inc. and its affiliates.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   */
  
  'use strict';
  
  const ESLintTester = require('eslint').RuleTester;
  const ReactHooksESLintPlugin = require('eslint-plugin-react-hooks');
  const ReactHooksESLintRule = ReactHooksESLintPlugin.rules['exhaustive-deps'];
  
  /**
   * A string template tag that removes padding from the left side of multi-line strings
   * @param {Array} strings array of code strings (only one expected)
   */
  function normalizeIndent(strings) {
    const codeLines = strings[0].split('\n');
    const leftPadding = codeLines[1].match(/\s+/)[0];
    return codeLines.map(line => line.substr(leftPadding.length)).join('\n');
  }
  
  // ***************************************************
  // For easier local testing, you can add to any case:
  // {
  //   skip: true,
  //   --or--
  //   only: true,
  //   ...
  // }
  // ***************************************************
  
  // Tests that are valid/invalid across all parsers
  const tests = {
    valid: [
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            useEffect(() => {
              console.log(local);
            });
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            useEffect(() => {
              const local = {};
              console.log(local);
            }, []);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = someFunc();
            useEffect(() => {
              console.log(local);
            }, [local]);
          }
        `,
      },
      {
        // OK because `props` wasn't defined.
        // We don't technically know if `props` is supposed
        // to be an import that hasn't been added yet, or
        // a component-level variable. Ignore it until it
        //  gets defined (a different rule would flag it anyway).
        code: normalizeIndent`
          function MyComponent() {
            useEffect(() => {
              console.log(props.foo);
            }, []);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local1 = {};
            {
              const local2 = {};
              useEffect(() => {
                console.log(local1);
                console.log(local2);
              });
            }
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local1 = someFunc();
            {
              const local2 = someFunc();
              useCallback(() => {
                console.log(local1);
                console.log(local2);
              }, [local1, local2]);
            }
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local1 = someFunc();
            function MyNestedComponent() {
              const local2 = someFunc();
              useCallback(() => {
                console.log(local1);
                console.log(local2);
              }, [local2]);
            }
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = someFunc();
            useEffect(() => {
              console.log(local);
              console.log(local);
            }, [local]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            useEffect(() => {
              console.log(unresolved);
            }, []);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = someFunc();
            useEffect(() => {
              console.log(local);
            }, [,,,local,,,]);
          }
        `,
      },
      {
        // Regression test
        code: normalizeIndent`
          function MyComponent({ foo }) {
            useEffect(() => {
              console.log(foo.length);
            }, [foo]);
          }
        `,
      },
      {
        // Regression test
        code: normalizeIndent`
          function MyComponent({ foo }) {
            useEffect(() => {
              console.log(foo.length);
              console.log(foo.slice(0));
            }, [foo]);
          }
        `,
      },
      {
        // Regression test
        code: normalizeIndent`
          function MyComponent({ history }) {
            useEffect(() => {
              return history.listen();
            }, [history]);
          }
        `,
      },
      {
        // Valid because they have meaning without deps.
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {});
            useLayoutEffect(() => {});
            useImperativeHandle(props.innerRef, () => {});
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.foo);
            }, [props.foo]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.foo);
              console.log(props.bar);
            }, [props.bar, props.foo]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.foo);
              console.log(props.bar);
            }, [props.foo, props.bar]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            const local = someFunc();
            useEffect(() => {
              console.log(props.foo);
              console.log(props.bar);
              console.log(local);
            }, [props.foo, props.bar, local]);
          }
        `,
      },
      {
        // [props, props.foo] is technically unnecessary ('props' covers 'props.foo').
        // However, it's valid for effects to over-specify their deps.
        // So we don't warn about this. We *would* warn about useMemo/useCallback.
        code: normalizeIndent`
          function MyComponent(props) {
            const local = {};
            useEffect(() => {
              console.log(props.foo);
              console.log(props.bar);
            }, [props, props.foo]);
  
            let color = someFunc();
            useEffect(() => {
              console.log(props.foo.bar.baz);
              console.log(color);
            }, [props.foo, props.foo.bar.baz, color]);
          }
        `,
      },
      // Nullish coalescing and optional chaining
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.foo?.bar?.baz ?? null);
            }, [props.foo]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.foo?.bar);
            }, [props.foo?.bar]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.foo?.bar);
            }, [props.foo.bar]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.foo.bar);
            }, [props.foo?.bar]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.foo.bar);
              console.log(props.foo?.bar);
            }, [props.foo?.bar]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.foo.bar);
              console.log(props.foo?.bar);
            }, [props.foo.bar]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.foo);
              console.log(props.foo?.bar);
            }, [props.foo]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.foo?.toString());
            }, [props.foo]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useMemo(() => {
              console.log(props.foo?.toString());
            }, [props.foo]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useCallback(() => {
              console.log(props.foo?.toString());
            }, [props.foo]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useCallback(() => {
              console.log(props.foo.bar?.toString());
            }, [props.foo.bar]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useCallback(() => {
              console.log(props.foo?.bar?.toString());
            }, [props.foo.bar]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useCallback(() => {
              console.log(props.foo.bar.toString());
            }, [props?.foo?.bar]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useCallback(() => {
              console.log(props.foo?.bar?.baz);
            }, [props?.foo.bar?.baz]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const myEffect = () => {
              // Doesn't use anything
            };
            useEffect(myEffect, []);
          }
        `,
      },
      {
        code: normalizeIndent`
          const local = {};
          function MyComponent() {
            const myEffect = () => {
              console.log(local);
            };
            useEffect(myEffect, []);
          }
        `,
      },
      {
        code: normalizeIndent`
          const local = {};
          function MyComponent() {
            function myEffect() {
              console.log(local);
            }
            useEffect(myEffect, []);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = someFunc();
            function myEffect() {
              console.log(local);
            }
            useEffect(myEffect, [local]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            function myEffect() {
              console.log(global);
            }
            useEffect(myEffect, []);
          }
        `,
      },
      {
        code: normalizeIndent`
          const local = {};
          function MyComponent() {
            const myEffect = () => {
              otherThing()
            }
            const otherThing = () => {
              console.log(local);
            }
            useEffect(myEffect, []);
          }
        `,
      },
      {
        // Valid because even though we don't inspect the function itself,
        // at least it's passed as a dependency.
        code: normalizeIndent`
          function MyComponent({delay}) {
            const local = {};
            const myEffect = debounce(() => {
              console.log(local);
            }, delay);
            useEffect(myEffect, [myEffect]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent({myEffect}) {
            useEffect(myEffect, [,myEffect]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent({myEffect}) {
            useEffect(myEffect, [,myEffect,,]);
          }
        `,
      },
      {
        code: normalizeIndent`
          let local = {};
          function myEffect() {
            console.log(local);
          }
          function MyComponent() {
            useEffect(myEffect, []);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent({myEffect}) {
            useEffect(myEffect, [myEffect]);
          }
        `,
      },
      {
        // Valid because has no deps.
        code: normalizeIndent`
          function MyComponent({myEffect}) {
            useEffect(myEffect);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useCustomEffect(() => {
              console.log(props.foo);
            });
          }
        `,
        options: [{additionalHooks: 'useCustomEffect'}],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useCustomEffect(() => {
              console.log(props.foo);
            }, [props.foo]);
          }
        `,
        options: [{additionalHooks: 'useCustomEffect'}],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useCustomEffect(() => {
              console.log(props.foo);
            }, []);
          }
        `,
        options: [{additionalHooks: 'useAnotherEffect'}],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useWithoutEffectSuffix(() => {
              console.log(props.foo);
            }, []);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            return renderHelperConfusedWithEffect(() => {
              console.log(props.foo);
            }, []);
          }
        `,
      },
      {
        // Valid because we don't care about hooks outside of components.
        code: normalizeIndent`
          const local = {};
          useEffect(() => {
            console.log(local);
          }, []);
        `,
      },
      {
        // Valid because we don't care about hooks outside of components.
        code: normalizeIndent`
          const local1 = {};
          {
            const local2 = {};
            useEffect(() => {
              console.log(local1);
              console.log(local2);
            }, []);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const ref = useRef();
            useEffect(() => {
              console.log(ref.current);
            }, [ref]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const ref = useRef();
            useEffect(() => {
              console.log(ref.current);
            }, []);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent({ maybeRef2, foo }) {
            const definitelyRef1 = useRef();
            const definitelyRef2 = useRef();
            const maybeRef1 = useSomeOtherRefyThing();
            const [state1, setState1] = useState();
            const [state2, setState2] = React.useState();
            const [state3, dispatch1] = useReducer();
            const [state4, dispatch2] = React.useReducer();
            const [state5, maybeSetState] = useFunnyState();
            const [state6, maybeDispatch] = useFunnyReducer();
            const [startTransition1] = useTransition();
            const [startTransition2, isPending2] = useTransition();
            const [startTransition3] = React.useTransition();
            const [startTransition4, isPending4] = React.useTransition();
            const mySetState = useCallback(() => {}, []);
            let myDispatch = useCallback(() => {}, []);
  
            useEffect(() => {
              // Known to be static
              console.log(definitelyRef1.current);
              console.log(definitelyRef2.current);
              console.log(maybeRef1.current);
              console.log(maybeRef2.current);
              setState1();
              setState2();
              dispatch1();
              dispatch2();
              startTransition1();
              startTransition2();
              startTransition3();
              startTransition4();
  
              // Dynamic
              console.log(state1);
              console.log(state2);
              console.log(state3);
              console.log(state4);
              console.log(state5);
              console.log(state6);
              console.log(isPending2);
              console.log(isPending4);
              mySetState();
              myDispatch();
  
              // Not sure; assume dynamic
              maybeSetState();
              maybeDispatch();
            }, [
              // Dynamic
              state1, state2, state3, state4, state5, state6,
              maybeRef1, maybeRef2,
              isPending2, isPending4,
  
              // Not sure; assume dynamic
              mySetState, myDispatch,
              maybeSetState, maybeDispatch
  
              // In this test, we don't specify static deps.
              // That should be okay.
            ]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent({ maybeRef2 }) {
            const definitelyRef1 = useRef();
            const definitelyRef2 = useRef();
            const maybeRef1 = useSomeOtherRefyThing();
  
            const [state1, setState1] = useState();
            const [state2, setState2] = React.useState();
            const [state3, dispatch1] = useReducer();
            const [state4, dispatch2] = React.useReducer();
  
            const [state5, maybeSetState] = useFunnyState();
            const [state6, maybeDispatch] = useFunnyReducer();
  
            const mySetState = useCallback(() => {}, []);
            let myDispatch = useCallback(() => {}, []);
  
            useEffect(() => {
              // Known to be static
              console.log(definitelyRef1.current);
              console.log(definitelyRef2.current);
              console.log(maybeRef1.current);
              console.log(maybeRef2.current);
              setState1();
              setState2();
              dispatch1();
              dispatch2();
  
              // Dynamic
              console.log(state1);
              console.log(state2);
              console.log(state3);
              console.log(state4);
              console.log(state5);
              console.log(state6);
              mySetState();
              myDispatch();
  
              // Not sure; assume dynamic
              maybeSetState();
              maybeDispatch();
            }, [
              // Dynamic
              state1, state2, state3, state4, state5, state6,
              maybeRef1, maybeRef2,
  
              // Not sure; assume dynamic
              mySetState, myDispatch,
              maybeSetState, maybeDispatch,
  
              // In this test, we specify static deps.
              // That should be okay too!
              definitelyRef1, definitelyRef2, setState1, setState2, dispatch1, dispatch2
            ]);
          }
        `,
      },
      {
        code: normalizeIndent`
          const MyComponent = forwardRef((props, ref) => {
            useImperativeHandle(ref, () => ({
              focus() {
                alert(props.hello);
              }
            }))
          });
        `,
      },
      {
        code: normalizeIndent`
          const MyComponent = forwardRef((props, ref) => {
            useImperativeHandle(ref, () => ({
              focus() {
                alert(props.hello);
              }
            }), [props.hello])
          });
        `,
      },
      {
        // This is not ideal but warning would likely create
        // too many false positives. We do, however, prevent
        // direct assignments.
        code: normalizeIndent`
          function MyComponent(props) {
            let obj = someFunc();
            useEffect(() => {
              obj.foo = true;
            }, [obj]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            let foo = {}
            useEffect(() => {
              foo.bar.baz = 43;
            }, [foo.bar]);
          }
        `,
      },
      {
        // Valid because we assign ref.current
        // ourselves. Therefore it's likely not
        // a ref managed by React.
        code: normalizeIndent`
          function MyComponent() {
            const myRef = useRef();
            useEffect(() => {
              const handleMove = () => {};
              myRef.current = {};
              return () => {
                console.log(myRef.current.toString())
              };
            }, []);
            return <div />;
          }
        `,
      },
      {
        // Valid because we assign ref.current
        // ourselves. Therefore it's likely not
        // a ref managed by React.
        code: normalizeIndent`
          function MyComponent() {
            const myRef = useRef();
            useEffect(() => {
              const handleMove = () => {};
              myRef.current = {};
              return () => {
                console.log(myRef?.current?.toString())
              };
            }, []);
            return <div />;
          }
        `,
      },
      {
        // Valid because we assign ref.current
        // ourselves. Therefore it's likely not
        // a ref managed by React.
        code: normalizeIndent`
          function useMyThing(myRef) {
            useEffect(() => {
              const handleMove = () => {};
              myRef.current = {};
              return () => {
                console.log(myRef.current.toString())
              };
            }, [myRef]);
          }
        `,
      },
      {
        // Valid because the ref is captured.
        code: normalizeIndent`
          function MyComponent() {
            const myRef = useRef();
            useEffect(() => {
              const handleMove = () => {};
              const node = myRef.current;
              node.addEventListener('mousemove', handleMove);
              return () => node.removeEventListener('mousemove', handleMove);
            }, []);
            return <div ref={myRef} />;
          }
        `,
      },
      {
        // Valid because the ref is captured.
        code: normalizeIndent`
          function useMyThing(myRef) {
            useEffect(() => {
              const handleMove = () => {};
              const node = myRef.current;
              node.addEventListener('mousemove', handleMove);
              return () => node.removeEventListener('mousemove', handleMove);
            }, [myRef]);
            return <div ref={myRef} />;
          }
        `,
      },
      {
        // Valid because it's not an effect.
        code: normalizeIndent`
          function useMyThing(myRef) {
            useCallback(() => {
              const handleMouse = () => {};
              myRef.current.addEventListener('mousemove', handleMouse);
              myRef.current.addEventListener('mousein', handleMouse);
              return function() {
                setTimeout(() => {
                  myRef.current.removeEventListener('mousemove', handleMouse);
                  myRef.current.removeEventListener('mousein', handleMouse);
                });
              }
            }, [myRef]);
          }
        `,
      },
      {
        // Valid because we read ref.current in a function that isn't cleanup.
        code: normalizeIndent`
          function useMyThing() {
            const myRef = useRef();
            useEffect(() => {
              const handleMove = () => {
                console.log(myRef.current)
              };
              window.addEventListener('mousemove', handleMove);
              return () => window.removeEventListener('mousemove', handleMove);
            }, []);
            return <div ref={myRef} />;
          }
        `,
      },
      {
        // Valid because we read ref.current in a function that isn't cleanup.
        code: normalizeIndent`
          function useMyThing() {
            const myRef = useRef();
            useEffect(() => {
              const handleMove = () => {
                return () => window.removeEventListener('mousemove', handleMove);
              };
              window.addEventListener('mousemove', handleMove);
              return () => {};
            }, []);
            return <div ref={myRef} />;
          }
        `,
      },
      {
        // Valid because it's a primitive constant.
        code: normalizeIndent`
          function MyComponent() {
            const local1 = 42;
            const local2 = '42';
            const local3 = null;
            useEffect(() => {
              console.log(local1);
              console.log(local2);
              console.log(local3);
            }, []);
          }
        `,
      },
      {
        // It's not a mistake to specify constant values though.
        code: normalizeIndent`
          function MyComponent() {
            const local1 = 42;
            const local2 = '42';
            const local3 = null;
            useEffect(() => {
              console.log(local1);
              console.log(local2);
              console.log(local3);
            }, [local1, local2, local3]);
          }
        `,
      },
      {
        // It is valid for effects to over-specify their deps.
        code: normalizeIndent`
          function MyComponent(props) {
            const local = props.local;
            useEffect(() => {}, [local]);
          }
        `,
      },
      {
        // Valid even though activeTab is "unused".
        // We allow over-specifying deps for effects, but not callbacks or memo.
        code: normalizeIndent`
          function Foo({ activeTab }) {
            useEffect(() => {
              window.scrollTo(0, 0);
            }, [activeTab]);
          }
        `,
      },
      {
        // It is valid to specify broader effect deps than strictly necessary.
        // Don't warn for this.
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.foo.bar.baz);
            }, [props]);
            useEffect(() => {
              console.log(props.foo.bar.baz);
            }, [props.foo]);
            useEffect(() => {
              console.log(props.foo.bar.baz);
            }, [props.foo.bar]);
            useEffect(() => {
              console.log(props.foo.bar.baz);
            }, [props.foo.bar.baz]);
          }
        `,
      },
      {
        // It is *also* valid to specify broader memo/callback deps than strictly necessary.
        // Don't warn for this either.
        code: normalizeIndent`
          function MyComponent(props) {
            const fn = useCallback(() => {
              console.log(props.foo.bar.baz);
            }, [props]);
            const fn2 = useCallback(() => {
              console.log(props.foo.bar.baz);
            }, [props.foo]);
            const fn3 = useMemo(() => {
              console.log(props.foo.bar.baz);
            }, [props.foo.bar]);
            const fn4 = useMemo(() => {
              console.log(props.foo.bar.baz);
            }, [props.foo.bar.baz]);
          }
        `,
      },
      {
        // Declaring handleNext is optional because
        // it doesn't use anything in the function scope.
        code: normalizeIndent`
          function MyComponent(props) {
            function handleNext1() {
              console.log('hello');
            }
            const handleNext2 = () => {
              console.log('hello');
            };
            let handleNext3 = function() {
              console.log('hello');
            };
            useEffect(() => {
              return Store.subscribe(handleNext1);
            }, []);
            useLayoutEffect(() => {
              return Store.subscribe(handleNext2);
            }, []);
            useMemo(() => {
              return Store.subscribe(handleNext3);
            }, []);
          }
        `,
      },
      {
        // Declaring handleNext is optional because
        // it doesn't use anything in the function scope.
        code: normalizeIndent`
          function MyComponent(props) {
            function handleNext() {
              console.log('hello');
            }
            useEffect(() => {
              return Store.subscribe(handleNext);
            }, []);
            useLayoutEffect(() => {
              return Store.subscribe(handleNext);
            }, []);
            useMemo(() => {
              return Store.subscribe(handleNext);
            }, []);
          }
        `,
      },
      {
        // Declaring handleNext is optional because
        // everything they use is fully static.
        code: normalizeIndent`
          function MyComponent(props) {
            let [, setState] = useState();
            let [, dispatch] = React.useReducer();
  
            function handleNext1(value) {
              let value2 = value * 100;
              setState(value2);
              console.log('hello');
            }
            const handleNext2 = (value) => {
              setState(foo(value));
              console.log('hello');
            };
            let handleNext3 = function(value) {
              console.log(value);
              dispatch({ type: 'x', value });
            };
            useEffect(() => {
              return Store.subscribe(handleNext1);
            }, []);
            useLayoutEffect(() => {
              return Store.subscribe(handleNext2);
            }, []);
            useMemo(() => {
              return Store.subscribe(handleNext3);
            }, []);
          }
        `,
      },
      {
        code: normalizeIndent`
          function useInterval(callback, delay) {
            const savedCallback = useRef();
            useEffect(() => {
              savedCallback.current = callback;
            });
            useEffect(() => {
              function tick() {
                savedCallback.current();
              }
              if (delay !== null) {
                let id = setInterval(tick, delay);
                return () => clearInterval(id);
              }
            }, [delay]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function Counter() {
            const [count, setCount] = useState(0);
  
            useEffect(() => {
              let id = setInterval(() => {
                setCount(c => c + 1);
              }, 1000);
              return () => clearInterval(id);
            }, []);
  
            return <h1>{count}</h1>;
          }
        `,
      },
      {
        code: normalizeIndent`
          function Counter() {
            const [count, setCount] = useState(0);
  
            function tick() {
              setCount(c => c + 1);
            }
  
            useEffect(() => {
              let id = setInterval(() => {
                tick();
              }, 1000);
              return () => clearInterval(id);
            }, []);
  
            return <h1>{count}</h1>;
          }
        `,
      },
      {
        code: normalizeIndent`
          function Counter() {
            const [count, dispatch] = useReducer((state, action) => {
              if (action === 'inc') {
                return state + 1;
              }
            }, 0);
  
            useEffect(() => {
              let id = setInterval(() => {
                dispatch('inc');
              }, 1000);
              return () => clearInterval(id);
            }, []);
  
            return <h1>{count}</h1>;
          }
        `,
      },
      {
        code: normalizeIndent`
          function Counter() {
            const [count, dispatch] = useReducer((state, action) => {
              if (action === 'inc') {
                return state + 1;
              }
            }, 0);
  
            const tick = () => {
              dispatch('inc');
            };
  
            useEffect(() => {
              let id = setInterval(tick, 1000);
              return () => clearInterval(id);
            }, []);
  
            return <h1>{count}</h1>;
          }
        `,
      },
      {
        // Regression test for a crash
        code: normalizeIndent`
          function Podcasts() {
            useEffect(() => {
              setPodcasts([]);
            }, []);
            let [podcasts, setPodcasts] = useState(null);
          }
        `,
      },
      {
        code: normalizeIndent`
          function withFetch(fetchPodcasts) {
            return function Podcasts({ id }) {
              let [podcasts, setPodcasts] = useState(null);
              useEffect(() => {
                fetchPodcasts(id).then(setPodcasts);
              }, [id]);
            }
          }
        `,
      },
      {
        code: normalizeIndent`
          function Podcasts({ id }) {
            let [podcasts, setPodcasts] = useState(null);
            useEffect(() => {
              function doFetch({ fetchPodcasts }) {
                fetchPodcasts(id).then(setPodcasts);
              }
              doFetch({ fetchPodcasts: API.fetchPodcasts });
            }, [id]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function Counter() {
            let [count, setCount] = useState(0);
  
            function increment(x) {
              return x + 1;
            }
  
            useEffect(() => {
              let id = setInterval(() => {
                setCount(increment);
              }, 1000);
              return () => clearInterval(id);
            }, []);
  
            return <h1>{count}</h1>;
          }
        `,
      },
      {
        code: normalizeIndent`
          function Counter() {
            let [count, setCount] = useState(0);
  
            function increment(x) {
              return x + 1;
            }
  
            useEffect(() => {
              let id = setInterval(() => {
                setCount(count => increment(count));
              }, 1000);
              return () => clearInterval(id);
            }, []);
  
            return <h1>{count}</h1>;
          }
        `,
      },
      {
        code: normalizeIndent`
          import increment from './increment';
          function Counter() {
            let [count, setCount] = useState(0);
  
            useEffect(() => {
              let id = setInterval(() => {
                setCount(count => count + increment);
              }, 1000);
              return () => clearInterval(id);
            }, []);
  
            return <h1>{count}</h1>;
          }
        `,
      },
      {
        code: normalizeIndent`
          function withStuff(increment) {
            return function Counter() {
              let [count, setCount] = useState(0);
  
              useEffect(() => {
                let id = setInterval(() => {
                  setCount(count => count + increment);
                }, 1000);
                return () => clearInterval(id);
              }, []);
  
              return <h1>{count}</h1>;
            }
          }
        `,
      },
      {
        code: normalizeIndent`
          function App() {
            const [query, setQuery] = useState('react');
            const [state, setState] = useState(null);
            useEffect(() => {
              let ignore = false;
              fetchSomething();
              async function fetchSomething() {
                const result = await (await fetch('http://hn.algolia.com/api/v1/search?query=' + query)).json();
                if (!ignore) setState(result);
              }
              return () => { ignore = true; };
            }, [query]);
            return (
              <>
                <input value={query} onChange={e => setQuery(e.target.value)} />
                {JSON.stringify(state)}
              </>
            );
          }
        `,
      },
      {
        code: normalizeIndent`
          function Example() {
            const foo = useCallback(() => {
              foo();
            }, []);
          }
        `,
      },
      {
        code: normalizeIndent`
          function Example({ prop }) {
            const foo = useCallback(() => {
              if (prop) {
                foo();
              }
            }, [prop]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function Hello() {
            const [state, setState] = useState(0);
            useEffect(() => {
              const handleResize = () => setState(window.innerWidth);
              window.addEventListener('resize', handleResize);
              return () => window.removeEventListener('resize', handleResize);
            });
          }
        `,
      },
      // Ignore arguments keyword for arrow functions.
      {
        code: normalizeIndent`
          function Example() {
            useEffect(() => {
              arguments
            }, [])
          }
        `,
      },
      {
        code: normalizeIndent`
          function Example() {
            useEffect(() => {
              const bar = () => {
                arguments;
              };
              bar();
            }, [])
          }
        `,
      },
      // Regression test.
      {
        code: normalizeIndent`
          function Example(props) {
            useEffect(() => {
              let topHeight = 0;
              topHeight = props.upperViewHeight;
            }, [props.upperViewHeight]);
          }
        `,
      },
      // Regression test.
      {
        code: normalizeIndent`
          function Example(props) {
            useEffect(() => {
              let topHeight = 0;
              topHeight = props?.upperViewHeight;
            }, [props?.upperViewHeight]);
          }
        `,
      },
      // Regression test.
      {
        code: normalizeIndent`
          function Example(props) {
            useEffect(() => {
              let topHeight = 0;
              topHeight = props?.upperViewHeight;
            }, [props]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function useFoo(foo){
            return useMemo(() => foo, [foo]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function useFoo(){
            const foo = "hi!";
            return useMemo(() => foo, [foo]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function useFoo(){
            let {foo} = {foo: 1};
            return useMemo(() => foo, [foo]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function useFoo(){
            let [foo] = [1];
            return useMemo(() => foo, [foo]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function useFoo() {
            const foo = "fine";
            if (true) {
              // Shadowed variable with constant construction in a nested scope is fine.
              const foo = {};
            }
            return useMemo(() => foo, [foo]);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent({foo}) {
            return useMemo(() => foo, [foo])
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const foo = true ? "fine" : "also fine";
            return useMemo(() => foo, [foo]);
          }
        `,
      },
    ],
    invalid: [
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useCallback(() => {
              console.log(props.foo?.toString());
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has a missing dependency: 'props.foo'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useCallback(() => {
                      console.log(props.foo?.toString());
                    }, [props.foo]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useCallback(() => {
              console.log(props.foo?.bar.baz);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has a missing dependency: 'props.foo?.bar.baz'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo?.bar.baz]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useCallback(() => {
                      console.log(props.foo?.bar.baz);
                    }, [props.foo?.bar.baz]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useCallback(() => {
              console.log(props.foo?.bar?.baz);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has a missing dependency: 'props.foo?.bar?.baz'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [props.foo?.bar?.baz]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useCallback(() => {
                      console.log(props.foo?.bar?.baz);
                    }, [props.foo?.bar?.baz]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useCallback(() => {
              console.log(props.foo?.bar.toString());
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has a missing dependency: 'props.foo?.bar'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo?.bar]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useCallback(() => {
                      console.log(props.foo?.bar.toString());
                    }, [props.foo?.bar]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = someFunc();
            useEffect(() => {
              console.log(local);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = someFunc();
                    useEffect(() => {
                      console.log(local);
                    }, [local]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // Note: we *could* detect it's a primitive and never assigned
        // even though it's not a constant -- but we currently don't.
        // So this is an error.
        code: normalizeIndent`
          function MyComponent() {
            let local = 42;
            useEffect(() => {
              console.log(local);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    let local = 42;
                    useEffect(() => {
                      console.log(local);
                    }, [local]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // Regexes are literals but potentially stateful.
        code: normalizeIndent`
          function MyComponent() {
            const local = /foo/;
            useEffect(() => {
              console.log(local);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = /foo/;
                    useEffect(() => {
                      console.log(local);
                    }, [local]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // Invalid because they don't have a meaning without deps.
        code: normalizeIndent`
          function MyComponent(props) {
            const value = useMemo(() => { return 2*2; });
            const fn = useCallback(() => { alert('foo'); });
          }
        `,
        // We don't know what you meant.
        errors: [
          {
            message:
              'React Hook useMemo does nothing when called with only one argument. ' +
              'Did you forget to pass an array of dependencies?',
            suggestions: undefined,
          },
          {
            message:
              'React Hook useCallback does nothing when called with only one argument. ' +
              'Did you forget to pass an array of dependencies?',
            suggestions: undefined,
          },
        ],
      },
      {
        // Invalid because they don't have a meaning without deps.
        code: normalizeIndent`
          function MyComponent({ fn1, fn2 }) {
            const value = useMemo(fn1);
            const fn = useCallback(fn2);
          }
        `,
        errors: [
          {
            message:
              'React Hook useMemo does nothing when called with only one argument. ' +
              'Did you forget to pass an array of dependencies?',
            suggestions: undefined,
          },
          {
            message:
              'React Hook useCallback does nothing when called with only one argument. ' +
              'Did you forget to pass an array of dependencies?',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            useEffect()
            useLayoutEffect()
            useCallback()
            useMemo()
          }
        `,
        errors: [
          {
            message:
              'React Hook useEffect requires an effect callback. ' +
              'Did you forget to pass a callback to the hook?',
            suggestions: undefined,
          },
          {
            message:
              'React Hook useLayoutEffect requires an effect callback. ' +
              'Did you forget to pass a callback to the hook?',
            suggestions: undefined,
          },
          {
            message:
              'React Hook useCallback requires an effect callback. ' +
              'Did you forget to pass a callback to the hook?',
            suggestions: undefined,
          },
          {
            message:
              'React Hook useMemo requires an effect callback. ' +
              'Did you forget to pass a callback to the hook?',
            suggestions: undefined,
          },
        ],
      },
      {
        // Regression test
        code: normalizeIndent`
          function MyComponent() {
            const local = someFunc();
            useEffect(() => {
              if (true) {
                console.log(local);
              }
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = someFunc();
                    useEffect(() => {
                      if (true) {
                        console.log(local);
                      }
                    }, [local]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // Regression test
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            useEffect(() => {
              try {
                console.log(local);
              } finally {}
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {};
                    useEffect(() => {
                      try {
                        console.log(local);
                      } finally {}
                    }, [local]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // Regression test
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            useEffect(() => {
              function inner() {
                console.log(local);
              }
              inner();
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {};
                    useEffect(() => {
                      function inner() {
                        console.log(local);
                      }
                      inner();
                    }, [local]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local1 = someFunc();
            {
              const local2 = someFunc();
              useEffect(() => {
                console.log(local1);
                console.log(local2);
              }, []);
            }
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has missing dependencies: 'local1' and 'local2'. " +
              'Either include them or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local1, local2]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local1 = someFunc();
                    {
                      const local2 = someFunc();
                      useEffect(() => {
                        console.log(local1);
                        console.log(local2);
                      }, [local1, local2]);
                    }
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local1 = {};
            const local2 = {};
            useEffect(() => {
              console.log(local1);
              console.log(local2);
            }, [local1]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local2'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local1, local2]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local1 = {};
                    const local2 = {};
                    useEffect(() => {
                      console.log(local1);
                      console.log(local2);
                    }, [local1, local2]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local1 = {};
            const local2 = {};
            useMemo(() => {
              console.log(local1);
            }, [local1, local2]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useMemo has an unnecessary dependency: 'local2'. " +
              'Either exclude it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local1]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local1 = {};
                    const local2 = {};
                    useMemo(() => {
                      console.log(local1);
                    }, [local1]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local1 = someFunc();
            function MyNestedComponent() {
              const local2 = {};
              useCallback(() => {
                console.log(local1);
                console.log(local2);
              }, [local1]);
            }
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has a missing dependency: 'local2'. " +
              'Either include it or remove the dependency array. ' +
              "Outer scope values like 'local1' aren't valid dependencies " +
              "because mutating them doesn't re-render the component.",
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local2]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local1 = someFunc();
                    function MyNestedComponent() {
                      const local2 = {};
                      useCallback(() => {
                        console.log(local1);
                        console.log(local2);
                      }, [local2]);
                    }
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            useEffect(() => {
              console.log(local);
              console.log(local);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {};
                    useEffect(() => {
                      console.log(local);
                      console.log(local);
                    }, [local]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            useEffect(() => {
              console.log(local);
              console.log(local);
            }, [local, local]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a duplicate dependency: 'local'. " +
              'Either omit it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {};
                    useEffect(() => {
                      console.log(local);
                      console.log(local);
                    }, [local]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            useCallback(() => {}, [window]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has an unnecessary dependency: 'window'. " +
              'Either exclude it or remove the dependency array. ' +
              "Outer scope values like 'window' aren't valid dependencies " +
              "because mutating them doesn't re-render the component.",
            suggestions: [
              {
                desc: 'Update the dependencies array to be: []',
                output: normalizeIndent`
                  function MyComponent() {
                    useCallback(() => {}, []);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // It is not valid for useCallback to specify extraneous deps
        // because it doesn't serve as a side effect trigger unlike useEffect.
        code: normalizeIndent`
          function MyComponent(props) {
            let local = props.foo;
            useCallback(() => {}, [local]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has an unnecessary dependency: 'local'. " +
              'Either exclude it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: []',
                output: normalizeIndent`
                  function MyComponent(props) {
                    let local = props.foo;
                    useCallback(() => {}, []);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent({ history }) {
            useEffect(() => {
              return history.listen();
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'history'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [history]',
                output: normalizeIndent`
                  function MyComponent({ history }) {
                    useEffect(() => {
                      return history.listen();
                    }, [history]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent({ history }) {
            useEffect(() => {
              return [
                history.foo.bar[2].dobedo.listen(),
                history.foo.bar().dobedo.listen[2]
              ];
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'history.foo'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [history.foo]',
                output: normalizeIndent`
                  function MyComponent({ history }) {
                    useEffect(() => {
                      return [
                        history.foo.bar[2].dobedo.listen(),
                        history.foo.bar().dobedo.listen[2]
                      ];
                    }, [history.foo]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent({ history }) {
            useEffect(() => {
              return [
                history?.foo
              ];
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'history?.foo'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [history?.foo]',
                output: normalizeIndent`
                  function MyComponent({ history }) {
                    useEffect(() => {
                      return [
                        history?.foo
                      ];
                    }, [history?.foo]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            useEffect(() => {}, ['foo']);
          }
        `,
        errors: [
          {
            message:
              // Don't assume user meant `foo` because it's not used in the effect.
              "The 'foo' literal is not a valid dependency because it never changes. " +
              'You can safely remove it.',
            // TODO: provide suggestion.
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent({ foo, bar, baz }) {
            useEffect(() => {
              console.log(foo, bar, baz);
            }, ['foo', 'bar']);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has missing dependencies: 'bar', 'baz', and 'foo'. " +
              'Either include them or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [bar, baz, foo]',
                output: normalizeIndent`
                  function MyComponent({ foo, bar, baz }) {
                    useEffect(() => {
                      console.log(foo, bar, baz);
                    }, [bar, baz, foo]);
                  }
                `,
              },
            ],
          },
          {
            message:
              "The 'foo' literal is not a valid dependency because it never changes. " +
              'Did you mean to include foo in the array instead?',
            suggestions: undefined,
          },
          {
            message:
              "The 'bar' literal is not a valid dependency because it never changes. " +
              'Did you mean to include bar in the array instead?',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent({ foo, bar, baz }) {
            useEffect(() => {
              console.log(foo, bar, baz);
            }, [42, false, null]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has missing dependencies: 'bar', 'baz', and 'foo'. " +
              'Either include them or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [bar, baz, foo]',
                output: normalizeIndent`
                  function MyComponent({ foo, bar, baz }) {
                    useEffect(() => {
                      console.log(foo, bar, baz);
                    }, [bar, baz, foo]);
                  }
                `,
              },
            ],
          },
          {
            message:
              'The 42 literal is not a valid dependency because it never changes. You can safely remove it.',
            suggestions: undefined,
          },
          {
            message:
              'The false literal is not a valid dependency because it never changes. You can safely remove it.',
            suggestions: undefined,
          },
          {
            message:
              'The null literal is not a valid dependency because it never changes. You can safely remove it.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const dependencies = [];
            useEffect(() => {}, dependencies);
          }
        `,
        errors: [
          {
            message:
              'React Hook useEffect was passed a dependency list that is not an ' +
              "array literal. This means we can't statically verify whether you've " +
              'passed the correct dependencies.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            const dependencies = [local];
            useEffect(() => {
              console.log(local);
            }, dependencies);
          }
        `,
        errors: [
          {
            message:
              'React Hook useEffect was passed a dependency list that is not an ' +
              "array literal. This means we can't statically verify whether you've " +
              'passed the correct dependencies.',
            // TODO: should this autofix or bail out?
            suggestions: undefined,
          },
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {};
                    const dependencies = [local];
                    useEffect(() => {
                      console.log(local);
                    }, [local]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            const dependencies = [local];
            useEffect(() => {
              console.log(local);
            }, [...dependencies]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {};
                    const dependencies = [local];
                    useEffect(() => {
                      console.log(local);
                    }, [local]);
                  }
                `,
              },
            ],
          },
          {
            message:
              'React Hook useEffect has a spread element in its dependency array. ' +
              "This means we can't statically verify whether you've passed the " +
              'correct dependencies.',
            // TODO: should this autofix or bail out?
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = someFunc();
            useEffect(() => {
              console.log(local);
            }, [local, ...dependencies]);
          }
        `,
        errors: [
          {
            message:
              'React Hook useEffect has a spread element in its dependency array. ' +
              "This means we can't statically verify whether you've passed the " +
              'correct dependencies.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            useEffect(() => {
              console.log(local);
            }, [computeCacheKey(local)]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            // TODO: I'm not sure this is a good idea.
            // Maybe bail out?
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {};
                    useEffect(() => {
                      console.log(local);
                    }, [local]);
                  }
                `,
              },
            ],
          },
          {
            message:
              'React Hook useEffect has a complex expression in the dependency array. ' +
              'Extract it to a separate variable so it can be statically checked.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.items[0]);
            }, [props.items[0]]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'props.items'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.items]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useEffect(() => {
                      console.log(props.items[0]);
                    }, [props.items]);
                  }
                `,
              },
            ],
          },
          {
            message:
              'React Hook useEffect has a complex expression in the dependency array. ' +
              'Extract it to a separate variable so it can be statically checked.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.items[0]);
            }, [props.items, props.items[0]]);
          }
        `,
        errors: [
          {
            message:
              'React Hook useEffect has a complex expression in the dependency array. ' +
              'Extract it to a separate variable so it can be statically checked.',
            // TODO: ideally suggestion would remove the bad expression?
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent({ items }) {
            useEffect(() => {
              console.log(items[0]);
            }, [items[0]]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'items'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [items]',
                output: normalizeIndent`
                  function MyComponent({ items }) {
                    useEffect(() => {
                      console.log(items[0]);
                    }, [items]);
                  }
                `,
              },
            ],
          },
          {
            message:
              'React Hook useEffect has a complex expression in the dependency array. ' +
              'Extract it to a separate variable so it can be statically checked.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent({ items }) {
            useEffect(() => {
              console.log(items[0]);
            }, [items, items[0]]);
          }
        `,
        errors: [
          {
            message:
              'React Hook useEffect has a complex expression in the dependency array. ' +
              'Extract it to a separate variable so it can be statically checked.',
            // TODO: ideally suggeston would remove the bad expression?
            suggestions: undefined,
          },
        ],
      },
      {
        // It is not valid for useCallback to specify extraneous deps
        // because it doesn't serve as a side effect trigger unlike useEffect.
        // However, we generally allow specifying *broader* deps as escape hatch.
        // So while [props, props.foo] is unnecessary, 'props' wins here as the
        // broader one, and this is why 'props.foo' is reported as unnecessary.
        code: normalizeIndent`
          function MyComponent(props) {
            const local = {};
            useCallback(() => {
              console.log(props.foo);
              console.log(props.bar);
            }, [props, props.foo]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has an unnecessary dependency: 'props.foo'. " +
              'Either exclude it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    const local = {};
                    useCallback(() => {
                      console.log(props.foo);
                      console.log(props.bar);
                    }, [props]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // Since we don't have 'props' in the list, we'll suggest narrow dependencies.
        code: normalizeIndent`
          function MyComponent(props) {
            const local = {};
            useCallback(() => {
              console.log(props.foo);
              console.log(props.bar);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has missing dependencies: 'props.bar' and 'props.foo'. " +
              'Either include them or remove the dependency array.',
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [props.bar, props.foo]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    const local = {};
                    useCallback(() => {
                      console.log(props.foo);
                      console.log(props.bar);
                    }, [props.bar, props.foo]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // Effects are allowed to over-specify deps. We'll complain about missing
        // 'local', but we won't remove the already-specified 'local.id' from your list.
        code: normalizeIndent`
          function MyComponent() {
            const local = {id: 42};
            useEffect(() => {
              console.log(local);
            }, [local.id]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local, local.id]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {id: 42};
                    useEffect(() => {
                      console.log(local);
                    }, [local, local.id]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // Callbacks are not allowed to over-specify deps. So we'll complain about missing
        // 'local' and we will also *remove* 'local.id' from your list.
        code: normalizeIndent`
          function MyComponent() {
            const local = {id: 42};
            const fn = useCallback(() => {
              console.log(local);
            }, [local.id]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {id: 42};
                    const fn = useCallback(() => {
                      console.log(local);
                    }, [local]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // Callbacks are not allowed to over-specify deps. So we'll complain about
        // the unnecessary 'local.id'.
        code: normalizeIndent`
          function MyComponent() {
            const local = {id: 42};
            const fn = useCallback(() => {
              console.log(local);
            }, [local.id, local]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has an unnecessary dependency: 'local.id'. " +
              'Either exclude it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {id: 42};
                    const fn = useCallback(() => {
                      console.log(local);
                    }, [local]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            const fn = useCallback(() => {
              console.log(props.foo.bar.baz);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has a missing dependency: 'props.foo.bar.baz'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo.bar.baz]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    const fn = useCallback(() => {
                      console.log(props.foo.bar.baz);
                    }, [props.foo.bar.baz]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            let color = {}
            const fn = useCallback(() => {
              console.log(props.foo.bar.baz);
              console.log(color);
            }, [props.foo, props.foo.bar.baz]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has a missing dependency: 'color'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [color, props.foo.bar.baz]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    let color = {}
                    const fn = useCallback(() => {
                      console.log(props.foo.bar.baz);
                      console.log(color);
                    }, [color, props.foo.bar.baz]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // Callbacks are not allowed to over-specify deps. So one of these is extra.
        // However, it *is* allowed to specify broader deps then strictly necessary.
        // So in this case we ask you to remove 'props.foo.bar.baz' because 'props.foo'
        // already covers it, and having both is unnecessary.
        // TODO: maybe consider suggesting a narrower one by default in these cases.
        code: normalizeIndent`
          function MyComponent(props) {
            const fn = useCallback(() => {
              console.log(props.foo.bar.baz);
            }, [props.foo.bar.baz, props.foo]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has an unnecessary dependency: 'props.foo.bar.baz'. " +
              'Either exclude it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    const fn = useCallback(() => {
                      console.log(props.foo.bar.baz);
                    }, [props.foo]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            const fn = useCallback(() => {
              console.log(props.foo.bar.baz);
              console.log(props.foo.fizz.bizz);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has missing dependencies: 'props.foo.bar.baz' and 'props.foo.fizz.bizz'. " +
              'Either include them or remove the dependency array.',
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [props.foo.bar.baz, props.foo.fizz.bizz]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    const fn = useCallback(() => {
                      console.log(props.foo.bar.baz);
                      console.log(props.foo.fizz.bizz);
                    }, [props.foo.bar.baz, props.foo.fizz.bizz]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // Normally we allow specifying deps too broadly.
        // So we'd be okay if 'props.foo.bar' was there rather than 'props.foo.bar.baz'.
        // However, 'props.foo.bar.baz' is missing. So we know there is a mistake.
        // When we're sure there is a mistake, for callbacks we will rebuild the list
        // from scratch. This will set the user on a better path by default.
        // This is why we end up with just 'props.foo.bar', and not them both.
        code: normalizeIndent`
          function MyComponent(props) {
            const fn = useCallback(() => {
              console.log(props.foo.bar);
            }, [props.foo.bar.baz]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has a missing dependency: 'props.foo.bar'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo.bar]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    const fn = useCallback(() => {
                      console.log(props.foo.bar);
                    }, [props.foo.bar]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            const fn = useCallback(() => {
              console.log(props);
              console.log(props.hello);
            }, [props.foo.bar.baz]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has a missing dependency: 'props'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    const fn = useCallback(() => {
                      console.log(props);
                      console.log(props.hello);
                    }, [props]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            useEffect(() => {
              console.log(local);
            }, [local, local]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a duplicate dependency: 'local'. " +
              'Either omit it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {};
                    useEffect(() => {
                      console.log(local);
                    }, [local]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local1 = {};
            useCallback(() => {
              const local1 = {};
              console.log(local1);
            }, [local1]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has an unnecessary dependency: 'local1'. " +
              'Either exclude it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: []',
                output: normalizeIndent`
                  function MyComponent() {
                    const local1 = {};
                    useCallback(() => {
                      const local1 = {};
                      console.log(local1);
                    }, []);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local1 = {};
            useCallback(() => {}, [local1]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has an unnecessary dependency: 'local1'. " +
              'Either exclude it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: []',
                output: normalizeIndent`
                  function MyComponent() {
                    const local1 = {};
                    useCallback(() => {}, []);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.foo);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'props.foo'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useEffect(() => {
                      console.log(props.foo);
                    }, [props.foo]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.foo);
              console.log(props.bar);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has missing dependencies: 'props.bar' and 'props.foo'. " +
              'Either include them or remove the dependency array.',
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [props.bar, props.foo]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useEffect(() => {
                      console.log(props.foo);
                      console.log(props.bar);
                    }, [props.bar, props.foo]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            let a, b, c, d, e, f, g;
            useEffect(() => {
              console.log(b, e, d, c, a, g, f);
            }, [c, a, g]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has missing dependencies: 'b', 'd', 'e', and 'f'. " +
              'Either include them or remove the dependency array.',
            // Don't alphabetize if it wasn't alphabetized in the first place.
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [c, a, g, b, e, d, f]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    let a, b, c, d, e, f, g;
                    useEffect(() => {
                      console.log(b, e, d, c, a, g, f);
                    }, [c, a, g, b, e, d, f]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            let a, b, c, d, e, f, g;
            useEffect(() => {
              console.log(b, e, d, c, a, g, f);
            }, [a, c, g]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has missing dependencies: 'b', 'd', 'e', and 'f'. " +
              'Either include them or remove the dependency array.',
            // Alphabetize if it was alphabetized.
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [a, b, c, d, e, f, g]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    let a, b, c, d, e, f, g;
                    useEffect(() => {
                      console.log(b, e, d, c, a, g, f);
                    }, [a, b, c, d, e, f, g]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            let a, b, c, d, e, f, g;
            useEffect(() => {
              console.log(b, e, d, c, a, g, f);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has missing dependencies: 'a', 'b', 'c', 'd', 'e', 'f', and 'g'. " +
              'Either include them or remove the dependency array.',
            // Alphabetize if it was empty.
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [a, b, c, d, e, f, g]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    let a, b, c, d, e, f, g;
                    useEffect(() => {
                      console.log(b, e, d, c, a, g, f);
                    }, [a, b, c, d, e, f, g]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            const local = {};
            useEffect(() => {
              console.log(props.foo);
              console.log(props.bar);
              console.log(local);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has missing dependencies: 'local', 'props.bar', and 'props.foo'. " +
              'Either include them or remove the dependency array.',
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [local, props.bar, props.foo]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    const local = {};
                    useEffect(() => {
                      console.log(props.foo);
                      console.log(props.bar);
                      console.log(local);
                    }, [local, props.bar, props.foo]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            const local = {};
            useEffect(() => {
              console.log(props.foo);
              console.log(props.bar);
              console.log(local);
            }, [props]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local, props]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    const local = {};
                    useEffect(() => {
                      console.log(props.foo);
                      console.log(props.bar);
                      console.log(local);
                    }, [local, props]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              console.log(props.foo);
            }, []);
            useCallback(() => {
              console.log(props.foo);
            }, []);
            useMemo(() => {
              console.log(props.foo);
            }, []);
            React.useEffect(() => {
              console.log(props.foo);
            }, []);
            React.useCallback(() => {
              console.log(props.foo);
            }, []);
            React.useMemo(() => {
              console.log(props.foo);
            }, []);
            React.notReactiveHook(() => {
              console.log(props.foo);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'props.foo'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useEffect(() => {
                      console.log(props.foo);
                    }, [props.foo]);
                    useCallback(() => {
                      console.log(props.foo);
                    }, []);
                    useMemo(() => {
                      console.log(props.foo);
                    }, []);
                    React.useEffect(() => {
                      console.log(props.foo);
                    }, []);
                    React.useCallback(() => {
                      console.log(props.foo);
                    }, []);
                    React.useMemo(() => {
                      console.log(props.foo);
                    }, []);
                    React.notReactiveHook(() => {
                      console.log(props.foo);
                    }, []);
                  }
                `,
              },
            ],
          },
          {
            message:
              "React Hook useCallback has a missing dependency: 'props.foo'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useEffect(() => {
                      console.log(props.foo);
                    }, []);
                    useCallback(() => {
                      console.log(props.foo);
                    }, [props.foo]);
                    useMemo(() => {
                      console.log(props.foo);
                    }, []);
                    React.useEffect(() => {
                      console.log(props.foo);
                    }, []);
                    React.useCallback(() => {
                      console.log(props.foo);
                    }, []);
                    React.useMemo(() => {
                      console.log(props.foo);
                    }, []);
                    React.notReactiveHook(() => {
                      console.log(props.foo);
                    }, []);
                  }
                `,
              },
            ],
          },
          {
            message:
              "React Hook useMemo has a missing dependency: 'props.foo'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useEffect(() => {
                      console.log(props.foo);
                    }, []);
                    useCallback(() => {
                      console.log(props.foo);
                    }, []);
                    useMemo(() => {
                      console.log(props.foo);
                    }, [props.foo]);
                    React.useEffect(() => {
                      console.log(props.foo);
                    }, []);
                    React.useCallback(() => {
                      console.log(props.foo);
                    }, []);
                    React.useMemo(() => {
                      console.log(props.foo);
                    }, []);
                    React.notReactiveHook(() => {
                      console.log(props.foo);
                    }, []);
                  }
                `,
              },
            ],
          },
          {
            message:
              "React Hook React.useEffect has a missing dependency: 'props.foo'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useEffect(() => {
                      console.log(props.foo);
                    }, []);
                    useCallback(() => {
                      console.log(props.foo);
                    }, []);
                    useMemo(() => {
                      console.log(props.foo);
                    }, []);
                    React.useEffect(() => {
                      console.log(props.foo);
                    }, [props.foo]);
                    React.useCallback(() => {
                      console.log(props.foo);
                    }, []);
                    React.useMemo(() => {
                      console.log(props.foo);
                    }, []);
                    React.notReactiveHook(() => {
                      console.log(props.foo);
                    }, []);
                  }
                `,
              },
            ],
          },
          {
            message:
              "React Hook React.useCallback has a missing dependency: 'props.foo'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useEffect(() => {
                      console.log(props.foo);
                    }, []);
                    useCallback(() => {
                      console.log(props.foo);
                    }, []);
                    useMemo(() => {
                      console.log(props.foo);
                    }, []);
                    React.useEffect(() => {
                      console.log(props.foo);
                    }, []);
                    React.useCallback(() => {
                      console.log(props.foo);
                    }, [props.foo]);
                    React.useMemo(() => {
                      console.log(props.foo);
                    }, []);
                    React.notReactiveHook(() => {
                      console.log(props.foo);
                    }, []);
                  }
                `,
              },
            ],
          },
          {
            message:
              "React Hook React.useMemo has a missing dependency: 'props.foo'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useEffect(() => {
                      console.log(props.foo);
                    }, []);
                    useCallback(() => {
                      console.log(props.foo);
                    }, []);
                    useMemo(() => {
                      console.log(props.foo);
                    }, []);
                    React.useEffect(() => {
                      console.log(props.foo);
                    }, []);
                    React.useCallback(() => {
                      console.log(props.foo);
                    }, []);
                    React.useMemo(() => {
                      console.log(props.foo);
                    }, [props.foo]);
                    React.notReactiveHook(() => {
                      console.log(props.foo);
                    }, []);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useCustomEffect(() => {
              console.log(props.foo);
            }, []);
            useEffect(() => {
              console.log(props.foo);
            }, []);
            React.useEffect(() => {
              console.log(props.foo);
            }, []);
            React.useCustomEffect(() => {
              console.log(props.foo);
            }, []);
          }
        `,
        options: [{additionalHooks: 'useCustomEffect'}],
        errors: [
          {
            message:
              "React Hook useCustomEffect has a missing dependency: 'props.foo'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useCustomEffect(() => {
                      console.log(props.foo);
                    }, [props.foo]);
                    useEffect(() => {
                      console.log(props.foo);
                    }, []);
                    React.useEffect(() => {
                      console.log(props.foo);
                    }, []);
                    React.useCustomEffect(() => {
                      console.log(props.foo);
                    }, []);
                  }
                `,
              },
            ],
          },
          {
            message:
              "React Hook useEffect has a missing dependency: 'props.foo'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useCustomEffect(() => {
                      console.log(props.foo);
                    }, []);
                    useEffect(() => {
                      console.log(props.foo);
                    }, [props.foo]);
                    React.useEffect(() => {
                      console.log(props.foo);
                    }, []);
                    React.useCustomEffect(() => {
                      console.log(props.foo);
                    }, []);
                  }
                `,
              },
            ],
          },
          {
            message:
              "React Hook React.useEffect has a missing dependency: 'props.foo'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useCustomEffect(() => {
                      console.log(props.foo);
                    }, []);
                    useEffect(() => {
                      console.log(props.foo);
                    }, []);
                    React.useEffect(() => {
                      console.log(props.foo);
                    }, [props.foo]);
                    React.useCustomEffect(() => {
                      console.log(props.foo);
                    }, []);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            useEffect(() => {
              console.log(local);
            }, [a ? local : b]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            // TODO: should we bail out instead?
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {};
                    useEffect(() => {
                      console.log(local);
                    }, [local]);
                  }
                `,
              },
            ],
          },
          {
            message:
              'React Hook useEffect has a complex expression in the dependency array. ' +
              'Extract it to a separate variable so it can be statically checked.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            useEffect(() => {
              console.log(local);
            }, [a && local]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            // TODO: should we bail out instead?
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {};
                    useEffect(() => {
                      console.log(local);
                    }, [local]);
                  }
                `,
              },
            ],
          },
          {
            message:
              'React Hook useEffect has a complex expression in the dependency array. ' +
              'Extract it to a separate variable so it can be statically checked.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const ref = useRef();
            const [state, setState] = useState();
            useEffect(() => {
              ref.current = {};
              setState(state + 1);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'state'. " +
              'Either include it or remove the dependency array. ' +
              `You can also do a functional update 'setState(s => ...)' ` +
              `if you only need 'state' in the 'setState' call.`,
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [state]',
                output: normalizeIndent`
                  function MyComponent() {
                    const ref = useRef();
                    const [state, setState] = useState();
                    useEffect(() => {
                      ref.current = {};
                      setState(state + 1);
                    }, [state]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const ref = useRef();
            const [state, setState] = useState();
            useEffect(() => {
              ref.current = {};
              setState(state + 1);
            }, [ref]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'state'. " +
              'Either include it or remove the dependency array. ' +
              `You can also do a functional update 'setState(s => ...)' ` +
              `if you only need 'state' in the 'setState' call.`,
            // We don't ask to remove static deps but don't add them either.
            // Don't suggest removing "ref" (it's fine either way)
            // but *do* add "state". *Don't* add "setState" ourselves.
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [ref, state]',
                output: normalizeIndent`
                  function MyComponent() {
                    const ref = useRef();
                    const [state, setState] = useState();
                    useEffect(() => {
                      ref.current = {};
                      setState(state + 1);
                    }, [ref, state]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            const ref1 = useRef();
            const ref2 = useRef();
            useEffect(() => {
              ref1.current.focus();
              console.log(ref2.current.textContent);
              alert(props.someOtherRefs.current.innerHTML);
              fetch(props.color);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has missing dependencies: 'props.color' and 'props.someOtherRefs'. " +
              'Either include them or remove the dependency array.',
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [props.color, props.someOtherRefs]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    const ref1 = useRef();
                    const ref2 = useRef();
                    useEffect(() => {
                      ref1.current.focus();
                      console.log(ref2.current.textContent);
                      alert(props.someOtherRefs.current.innerHTML);
                      fetch(props.color);
                    }, [props.color, props.someOtherRefs]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            const ref1 = useRef();
            const ref2 = useRef();
            useEffect(() => {
              ref1.current.focus();
              console.log(ref2.current.textContent);
              alert(props.someOtherRefs.current.innerHTML);
              fetch(props.color);
            }, [ref1.current, ref2.current, props.someOtherRefs, props.color]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has unnecessary dependencies: 'ref1.current' and 'ref2.current'. " +
              'Either exclude them or remove the dependency array. ' +
              "Mutable values like 'ref1.current' aren't valid dependencies " +
              "because mutating them doesn't re-render the component.",
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [props.someOtherRefs, props.color]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    const ref1 = useRef();
                    const ref2 = useRef();
                    useEffect(() => {
                      ref1.current.focus();
                      console.log(ref2.current.textContent);
                      alert(props.someOtherRefs.current.innerHTML);
                      fetch(props.color);
                    }, [props.someOtherRefs, props.color]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            const ref1 = useRef();
            const ref2 = useRef();
            useEffect(() => {
              ref1?.current?.focus();
              console.log(ref2?.current?.textContent);
              alert(props.someOtherRefs.current.innerHTML);
              fetch(props.color);
            }, [ref1?.current, ref2?.current, props.someOtherRefs, props.color]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has unnecessary dependencies: 'ref1.current' and 'ref2.current'. " +
              'Either exclude them or remove the dependency array. ' +
              "Mutable values like 'ref1.current' aren't valid dependencies " +
              "because mutating them doesn't re-render the component.",
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [props.someOtherRefs, props.color]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    const ref1 = useRef();
                    const ref2 = useRef();
                    useEffect(() => {
                      ref1?.current?.focus();
                      console.log(ref2?.current?.textContent);
                      alert(props.someOtherRefs.current.innerHTML);
                      fetch(props.color);
                    }, [props.someOtherRefs, props.color]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const ref = useRef();
            useEffect(() => {
              console.log(ref.current);
            }, [ref.current]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has an unnecessary dependency: 'ref.current'. " +
              'Either exclude it or remove the dependency array. ' +
              "Mutable values like 'ref.current' aren't valid dependencies " +
              "because mutating them doesn't re-render the component.",
            suggestions: [
              {
                desc: 'Update the dependencies array to be: []',
                output: normalizeIndent`
                  function MyComponent() {
                    const ref = useRef();
                    useEffect(() => {
                      console.log(ref.current);
                    }, []);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent({ activeTab }) {
            const ref1 = useRef();
            const ref2 = useRef();
            useEffect(() => {
              ref1.current.scrollTop = 0;
              ref2.current.scrollTop = 0;
            }, [ref1.current, ref2.current, activeTab]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has unnecessary dependencies: 'ref1.current' and 'ref2.current'. " +
              'Either exclude them or remove the dependency array. ' +
              "Mutable values like 'ref1.current' aren't valid dependencies " +
              "because mutating them doesn't re-render the component.",
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [activeTab]',
                output: normalizeIndent`
                  function MyComponent({ activeTab }) {
                    const ref1 = useRef();
                    const ref2 = useRef();
                    useEffect(() => {
                      ref1.current.scrollTop = 0;
                      ref2.current.scrollTop = 0;
                    }, [activeTab]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent({ activeTab, initY }) {
            const ref1 = useRef();
            const ref2 = useRef();
            const fn = useCallback(() => {
              ref1.current.scrollTop = initY;
              ref2.current.scrollTop = initY;
            }, [ref1.current, ref2.current, activeTab, initY]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has unnecessary dependencies: 'activeTab', 'ref1.current', and 'ref2.current'. " +
              'Either exclude them or remove the dependency array. ' +
              "Mutable values like 'ref1.current' aren't valid dependencies " +
              "because mutating them doesn't re-render the component.",
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [initY]',
                output: normalizeIndent`
                  function MyComponent({ activeTab, initY }) {
                    const ref1 = useRef();
                    const ref2 = useRef();
                    const fn = useCallback(() => {
                      ref1.current.scrollTop = initY;
                      ref2.current.scrollTop = initY;
                    }, [initY]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const ref = useRef();
            useEffect(() => {
              console.log(ref.current);
            }, [ref.current, ref]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has an unnecessary dependency: 'ref.current'. " +
              'Either exclude it or remove the dependency array. ' +
              "Mutable values like 'ref.current' aren't valid dependencies " +
              "because mutating them doesn't re-render the component.",
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [ref]',
                output: normalizeIndent`
                  function MyComponent() {
                    const ref = useRef();
                    useEffect(() => {
                      console.log(ref.current);
                    }, [ref]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          const MyComponent = forwardRef((props, ref) => {
            useImperativeHandle(ref, () => ({
              focus() {
                alert(props.hello);
              }
            }), [])
          });
        `,
        errors: [
          {
            message:
              "React Hook useImperativeHandle has a missing dependency: 'props.hello'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.hello]',
                output: normalizeIndent`
                  const MyComponent = forwardRef((props, ref) => {
                    useImperativeHandle(ref, () => ({
                      focus() {
                        alert(props.hello);
                      }
                    }), [props.hello])
                  });
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              if (props.onChange) {
                props.onChange();
              }
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'props'. " +
              'Either include it or remove the dependency array. ' +
              `However, 'props' will change when *any* prop changes, so the ` +
              `preferred fix is to destructure the 'props' object outside ` +
              `of the useEffect call and refer to those specific ` +
              `props inside useEffect.`,
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useEffect(() => {
                      if (props.onChange) {
                        props.onChange();
                      }
                    }, [props]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              if (props?.onChange) {
                props?.onChange();
              }
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'props'. " +
              'Either include it or remove the dependency array. ' +
              `However, 'props' will change when *any* prop changes, so the ` +
              `preferred fix is to destructure the 'props' object outside ` +
              `of the useEffect call and refer to those specific ` +
              `props inside useEffect.`,
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useEffect(() => {
                      if (props?.onChange) {
                        props?.onChange();
                      }
                    }, [props]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              function play() {
                props.onPlay();
              }
              function pause() {
                props.onPause();
              }
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'props'. " +
              'Either include it or remove the dependency array. ' +
              `However, 'props' will change when *any* prop changes, so the ` +
              `preferred fix is to destructure the 'props' object outside ` +
              `of the useEffect call and refer to those specific ` +
              `props inside useEffect.`,
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useEffect(() => {
                      function play() {
                        props.onPlay();
                      }
                      function pause() {
                        props.onPause();
                      }
                    }, [props]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              if (props.foo.onChange) {
                props.foo.onChange();
              }
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'props.foo'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useEffect(() => {
                      if (props.foo.onChange) {
                        props.foo.onChange();
                      }
                    }, [props.foo]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              props.onChange();
              if (props.foo.onChange) {
                props.foo.onChange();
              }
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'props'. " +
              'Either include it or remove the dependency array. ' +
              `However, 'props' will change when *any* prop changes, so the ` +
              `preferred fix is to destructure the 'props' object outside ` +
              `of the useEffect call and refer to those specific ` +
              `props inside useEffect.`,
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useEffect(() => {
                      props.onChange();
                      if (props.foo.onChange) {
                        props.foo.onChange();
                      }
                    }, [props]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            const [skillsCount] = useState();
            useEffect(() => {
              if (skillsCount === 0 && !props.isEditMode) {
                props.toggleEditMode();
              }
            }, [skillsCount, props.isEditMode, props.toggleEditMode]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'props'. " +
              'Either include it or remove the dependency array. ' +
              `However, 'props' will change when *any* prop changes, so the ` +
              `preferred fix is to destructure the 'props' object outside ` +
              `of the useEffect call and refer to those specific ` +
              `props inside useEffect.`,
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [skillsCount, props.isEditMode, props.toggleEditMode, props]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    const [skillsCount] = useState();
                    useEffect(() => {
                      if (skillsCount === 0 && !props.isEditMode) {
                        props.toggleEditMode();
                      }
                    }, [skillsCount, props.isEditMode, props.toggleEditMode, props]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            const [skillsCount] = useState();
            useEffect(() => {
              if (skillsCount === 0 && !props.isEditMode) {
                props.toggleEditMode();
              }
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has missing dependencies: 'props' and 'skillsCount'. " +
              'Either include them or remove the dependency array. ' +
              `However, 'props' will change when *any* prop changes, so the ` +
              `preferred fix is to destructure the 'props' object outside ` +
              `of the useEffect call and refer to those specific ` +
              `props inside useEffect.`,
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props, skillsCount]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    const [skillsCount] = useState();
                    useEffect(() => {
                      if (skillsCount === 0 && !props.isEditMode) {
                        props.toggleEditMode();
                      }
                    }, [props, skillsCount]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              externalCall(props);
              props.onChange();
            }, []);
          }
        `,
        // Don't suggest to destructure props here since you can't.
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'props'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useEffect(() => {
                      externalCall(props);
                      props.onChange();
                    }, [props]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            useEffect(() => {
              props.onChange();
              externalCall(props);
            }, []);
          }
        `,
        // Don't suggest to destructure props here since you can't.
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'props'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    useEffect(() => {
                      props.onChange();
                      externalCall(props);
                    }, [props]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            let value;
            let value2;
            let value3;
            let value4;
            let asyncValue;
            useEffect(() => {
              if (value4) {
                value = {};
              }
              value2 = 100;
              value = 43;
              value4 = true;
              console.log(value2);
              console.log(value3);
              setTimeout(() => {
                asyncValue = 100;
              });
            }, []);
          }
        `,
        // This is a separate warning unrelated to others.
        // We could've made a separate rule for it but it's rare enough to name it.
        // No suggestions because the intent isn't clear.
        errors: [
          {
            message:
              // value2
              `Assignments to the 'value2' variable from inside React Hook useEffect ` +
              `will be lost after each render. To preserve the value over time, ` +
              `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +
              `Otherwise, you can move this variable directly inside useEffect.`,
            suggestions: undefined,
          },
          {
            message:
              // value
              `Assignments to the 'value' variable from inside React Hook useEffect ` +
              `will be lost after each render. To preserve the value over time, ` +
              `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +
              `Otherwise, you can move this variable directly inside useEffect.`,
            suggestions: undefined,
          },
          {
            message:
              // value4
              `Assignments to the 'value4' variable from inside React Hook useEffect ` +
              `will be lost after each render. To preserve the value over time, ` +
              `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +
              `Otherwise, you can move this variable directly inside useEffect.`,
            suggestions: undefined,
          },
          {
            message:
              // asyncValue
              `Assignments to the 'asyncValue' variable from inside React Hook useEffect ` +
              `will be lost after each render. To preserve the value over time, ` +
              `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +
              `Otherwise, you can move this variable directly inside useEffect.`,
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            let value;
            let value2;
            let value3;
            let asyncValue;
            useEffect(() => {
              value = {};
              value2 = 100;
              value = 43;
              console.log(value2);
              console.log(value3);
              setTimeout(() => {
                asyncValue = 100;
              });
            }, [value, value2, value3]);
          }
        `,
        // This is a separate warning unrelated to others.
        // We could've made a separate rule for it but it's rare enough to name it.
        // No suggestions because the intent isn't clear.
        errors: [
          {
            message:
              // value
              `Assignments to the 'value' variable from inside React Hook useEffect ` +
              `will be lost after each render. To preserve the value over time, ` +
              `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +
              `Otherwise, you can move this variable directly inside useEffect.`,
            suggestions: undefined,
          },
          {
            message:
              // value2
              `Assignments to the 'value2' variable from inside React Hook useEffect ` +
              `will be lost after each render. To preserve the value over time, ` +
              `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +
              `Otherwise, you can move this variable directly inside useEffect.`,
            suggestions: undefined,
          },
          {
            message:
              // asyncValue
              `Assignments to the 'asyncValue' variable from inside React Hook useEffect ` +
              `will be lost after each render. To preserve the value over time, ` +
              `store it in a useRef Hook and keep the mutable value in the '.current' property. ` +
              `Otherwise, you can move this variable directly inside useEffect.`,
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const myRef = useRef();
            useEffect(() => {
              const handleMove = () => {};
              myRef.current.addEventListener('mousemove', handleMove);
              return () => myRef.current.removeEventListener('mousemove', handleMove);
            }, []);
            return <div ref={myRef} />;
          }
        `,
        errors: [
          {
            message:
              `The ref value 'myRef.current' will likely have changed by the time ` +
              `this effect cleanup function runs. If this ref points to a node ` +
              `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +
              `and use that variable in the cleanup function.`,
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const myRef = useRef();
            useEffect(() => {
              const handleMove = () => {};
              myRef?.current?.addEventListener('mousemove', handleMove);
              return () => myRef?.current?.removeEventListener('mousemove', handleMove);
            }, []);
            return <div ref={myRef} />;
          }
        `,
        errors: [
          {
            message:
              `The ref value 'myRef.current' will likely have changed by the time ` +
              `this effect cleanup function runs. If this ref points to a node ` +
              `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +
              `and use that variable in the cleanup function.`,
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const myRef = useRef();
            useEffect(() => {
              const handleMove = () => {};
              myRef.current.addEventListener('mousemove', handleMove);
              return () => myRef.current.removeEventListener('mousemove', handleMove);
            });
            return <div ref={myRef} />;
          }
        `,
        errors: [
          {
            message:
              `The ref value 'myRef.current' will likely have changed by the time ` +
              `this effect cleanup function runs. If this ref points to a node ` +
              `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +
              `and use that variable in the cleanup function.`,
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function useMyThing(myRef) {
            useEffect(() => {
              const handleMove = () => {};
              myRef.current.addEventListener('mousemove', handleMove);
              return () => myRef.current.removeEventListener('mousemove', handleMove);
            }, [myRef]);
          }
        `,
        errors: [
          {
            message:
              `The ref value 'myRef.current' will likely have changed by the time ` +
              `this effect cleanup function runs. If this ref points to a node ` +
              `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +
              `and use that variable in the cleanup function.`,
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function useMyThing(myRef) {
            useEffect(() => {
              const handleMouse = () => {};
              myRef.current.addEventListener('mousemove', handleMouse);
              myRef.current.addEventListener('mousein', handleMouse);
              return function() {
                setTimeout(() => {
                  myRef.current.removeEventListener('mousemove', handleMouse);
                  myRef.current.removeEventListener('mousein', handleMouse);
                });
              }
            }, [myRef]);
          }
        `,
        errors: [
          {
            message:
              `The ref value 'myRef.current' will likely have changed by the time ` +
              `this effect cleanup function runs. If this ref points to a node ` +
              `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +
              `and use that variable in the cleanup function.`,
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function useMyThing(myRef, active) {
            useEffect(() => {
              const handleMove = () => {};
              if (active) {
                myRef.current.addEventListener('mousemove', handleMove);
                return function() {
                  setTimeout(() => {
                    myRef.current.removeEventListener('mousemove', handleMove);
                  });
                }
              }
            }, [myRef, active]);
          }
        `,
        errors: [
          {
            message:
              `The ref value 'myRef.current' will likely have changed by the time ` +
              `this effect cleanup function runs. If this ref points to a node ` +
              `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +
              `and use that variable in the cleanup function.`,
            suggestions: undefined,
          },
        ],
      },
      {
        code: `
          function MyComponent() {
            const myRef = useRef();
            useLayoutEffect_SAFE_FOR_SSR(() => {
              const handleMove = () => {};
              myRef.current.addEventListener('mousemove', handleMove);
              return () => myRef.current.removeEventListener('mousemove', handleMove);
            });
            return <div ref={myRef} />;
          }
        `,
        output: `
          function MyComponent() {
            const myRef = useRef();
            useLayoutEffect_SAFE_FOR_SSR(() => {
              const handleMove = () => {};
              myRef.current.addEventListener('mousemove', handleMove);
              return () => myRef.current.removeEventListener('mousemove', handleMove);
            });
            return <div ref={myRef} />;
          }
        `,
        errors: [
          `The ref value 'myRef.current' will likely have changed by the time ` +
            `this effect cleanup function runs. If this ref points to a node ` +
            `rendered by React, copy 'myRef.current' to a variable inside the effect, ` +
            `and use that variable in the cleanup function.`,
        ],
        options: [{additionalHooks: 'useLayoutEffect_SAFE_FOR_SSR'}],
      },
      {
        // Autofix ignores constant primitives (leaving the ones that are there).
        code: normalizeIndent`
          function MyComponent() {
            const local1 = 42;
            const local2 = '42';
            const local3 = null;
            const local4 = {};
            useEffect(() => {
              console.log(local1);
              console.log(local2);
              console.log(local3);
              console.log(local4);
            }, [local1, local3]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local4'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [local1, local3, local4]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local1 = 42;
                    const local2 = '42';
                    const local3 = null;
                    const local4 = {};
                    useEffect(() => {
                      console.log(local1);
                      console.log(local2);
                      console.log(local3);
                      console.log(local4);
                    }, [local1, local3, local4]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            useEffect(() => {
              window.scrollTo(0, 0);
            }, [window]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has an unnecessary dependency: 'window'. " +
              'Either exclude it or remove the dependency array. ' +
              "Outer scope values like 'window' aren't valid dependencies " +
              "because mutating them doesn't re-render the component.",
            suggestions: [
              {
                desc: 'Update the dependencies array to be: []',
                output: normalizeIndent`
                  function MyComponent() {
                    useEffect(() => {
                      window.scrollTo(0, 0);
                    }, []);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          import MutableStore from 'store';
  
          function MyComponent() {
            useEffect(() => {
              console.log(MutableStore.hello);
            }, [MutableStore.hello]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has an unnecessary dependency: 'MutableStore.hello'. " +
              'Either exclude it or remove the dependency array. ' +
              "Outer scope values like 'MutableStore.hello' aren't valid dependencies " +
              "because mutating them doesn't re-render the component.",
            suggestions: [
              {
                desc: 'Update the dependencies array to be: []',
                output: normalizeIndent`
                  import MutableStore from 'store';
  
                  function MyComponent() {
                    useEffect(() => {
                      console.log(MutableStore.hello);
                    }, []);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          import MutableStore from 'store';
          let z = {};
  
          function MyComponent(props) {
            let x = props.foo;
            {
              let y = props.bar;
              useEffect(() => {
                console.log(MutableStore.hello.world, props.foo, x, y, z, global.stuff);
              }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]);
            }
          }
        `,
        errors: [
          {
            message:
              'React Hook useEffect has unnecessary dependencies: ' +
              "'MutableStore.hello.world', 'global.stuff', and 'z'. " +
              'Either exclude them or remove the dependency array. ' +
              "Outer scope values like 'MutableStore.hello.world' aren't valid dependencies " +
              "because mutating them doesn't re-render the component.",
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo, x, y]',
                output: normalizeIndent`
                  import MutableStore from 'store';
                  let z = {};
  
                  function MyComponent(props) {
                    let x = props.foo;
                    {
                      let y = props.bar;
                      useEffect(() => {
                        console.log(MutableStore.hello.world, props.foo, x, y, z, global.stuff);
                      }, [props.foo, x, y]);
                    }
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          import MutableStore from 'store';
          let z = {};
  
          function MyComponent(props) {
            let x = props.foo;
            {
              let y = props.bar;
              useEffect(() => {
                // nothing
              }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]);
            }
          }
        `,
        errors: [
          {
            message:
              'React Hook useEffect has unnecessary dependencies: ' +
              "'MutableStore.hello.world', 'global.stuff', and 'z'. " +
              'Either exclude them or remove the dependency array. ' +
              "Outer scope values like 'MutableStore.hello.world' aren't valid dependencies " +
              "because mutating them doesn't re-render the component.",
            // The output should contain the ones that are inside a component
            // since there are legit reasons to over-specify them for effects.
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [props.foo, x, y]',
                output: normalizeIndent`
                  import MutableStore from 'store';
                  let z = {};
  
                  function MyComponent(props) {
                    let x = props.foo;
                    {
                      let y = props.bar;
                      useEffect(() => {
                        // nothing
                      }, [props.foo, x, y]);
                    }
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          import MutableStore from 'store';
          let z = {};
  
          function MyComponent(props) {
            let x = props.foo;
            {
              let y = props.bar;
              const fn = useCallback(() => {
                // nothing
              }, [MutableStore.hello.world, props.foo, x, y, z, global.stuff]);
            }
          }
        `,
        errors: [
          {
            message:
              'React Hook useCallback has unnecessary dependencies: ' +
              "'MutableStore.hello.world', 'global.stuff', 'props.foo', 'x', 'y', and 'z'. " +
              'Either exclude them or remove the dependency array. ' +
              "Outer scope values like 'MutableStore.hello.world' aren't valid dependencies " +
              "because mutating them doesn't re-render the component.",
            suggestions: [
              {
                desc: 'Update the dependencies array to be: []',
                output: normalizeIndent`
                  import MutableStore from 'store';
                  let z = {};
  
                  function MyComponent(props) {
                    let x = props.foo;
                    {
                      let y = props.bar;
                      const fn = useCallback(() => {
                        // nothing
                      }, []);
                    }
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          import MutableStore from 'store';
          let z = {};
  
          function MyComponent(props) {
            let x = props.foo;
            {
              let y = props.bar;
              const fn = useCallback(() => {
                // nothing
              }, [MutableStore?.hello?.world, props.foo, x, y, z, global?.stuff]);
            }
          }
        `,
        errors: [
          {
            message:
              'React Hook useCallback has unnecessary dependencies: ' +
              "'MutableStore.hello.world', 'global.stuff', 'props.foo', 'x', 'y', and 'z'. " +
              'Either exclude them or remove the dependency array. ' +
              "Outer scope values like 'MutableStore.hello.world' aren't valid dependencies " +
              "because mutating them doesn't re-render the component.",
            suggestions: [
              {
                desc: 'Update the dependencies array to be: []',
                output: normalizeIndent`
                  import MutableStore from 'store';
                  let z = {};
  
                  function MyComponent(props) {
                    let x = props.foo;
                    {
                      let y = props.bar;
                      const fn = useCallback(() => {
                        // nothing
                      }, []);
                    }
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // Every almost-static function is tainted by a dynamic value.
        code: normalizeIndent`
          function MyComponent(props) {
            let [, setState] = useState();
            let [, dispatch] = React.useReducer();
            let taint = props.foo;
  
            function handleNext1(value) {
              let value2 = value * taint;
              setState(value2);
              console.log('hello');
            }
            const handleNext2 = (value) => {
              setState(taint(value));
              console.log('hello');
            };
            let handleNext3 = function(value) {
              setTimeout(() => console.log(taint));
              dispatch({ type: 'x', value });
            };
            useEffect(() => {
              return Store.subscribe(handleNext1);
            }, []);
            useLayoutEffect(() => {
              return Store.subscribe(handleNext2);
            }, []);
            useMemo(() => {
              return Store.subscribe(handleNext3);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'handleNext1'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [handleNext1]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    let [, setState] = useState();
                    let [, dispatch] = React.useReducer();
                    let taint = props.foo;
  
                    function handleNext1(value) {
                      let value2 = value * taint;
                      setState(value2);
                      console.log('hello');
                    }
                    const handleNext2 = (value) => {
                      setState(taint(value));
                      console.log('hello');
                    };
                    let handleNext3 = function(value) {
                      setTimeout(() => console.log(taint));
                      dispatch({ type: 'x', value });
                    };
                    useEffect(() => {
                      return Store.subscribe(handleNext1);
                    }, [handleNext1]);
                    useLayoutEffect(() => {
                      return Store.subscribe(handleNext2);
                    }, []);
                    useMemo(() => {
                      return Store.subscribe(handleNext3);
                    }, []);
                  }
                `,
              },
            ],
          },
          {
            message:
              "React Hook useLayoutEffect has a missing dependency: 'handleNext2'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [handleNext2]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    let [, setState] = useState();
                    let [, dispatch] = React.useReducer();
                    let taint = props.foo;
  
                    function handleNext1(value) {
                      let value2 = value * taint;
                      setState(value2);
                      console.log('hello');
                    }
                    const handleNext2 = (value) => {
                      setState(taint(value));
                      console.log('hello');
                    };
                    let handleNext3 = function(value) {
                      setTimeout(() => console.log(taint));
                      dispatch({ type: 'x', value });
                    };
                    useEffect(() => {
                      return Store.subscribe(handleNext1);
                    }, []);
                    useLayoutEffect(() => {
                      return Store.subscribe(handleNext2);
                    }, [handleNext2]);
                    useMemo(() => {
                      return Store.subscribe(handleNext3);
                    }, []);
                  }
                `,
              },
            ],
          },
          {
            message:
              "React Hook useMemo has a missing dependency: 'handleNext3'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [handleNext3]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    let [, setState] = useState();
                    let [, dispatch] = React.useReducer();
                    let taint = props.foo;
  
                    function handleNext1(value) {
                      let value2 = value * taint;
                      setState(value2);
                      console.log('hello');
                    }
                    const handleNext2 = (value) => {
                      setState(taint(value));
                      console.log('hello');
                    };
                    let handleNext3 = function(value) {
                      setTimeout(() => console.log(taint));
                      dispatch({ type: 'x', value });
                    };
                    useEffect(() => {
                      return Store.subscribe(handleNext1);
                    }, []);
                    useLayoutEffect(() => {
                      return Store.subscribe(handleNext2);
                    }, []);
                    useMemo(() => {
                      return Store.subscribe(handleNext3);
                    }, [handleNext3]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // Regression test
        code: normalizeIndent`
          function MyComponent(props) {
            let [, setState] = useState();
            let [, dispatch] = React.useReducer();
            let taint = props.foo;
  
            // Shouldn't affect anything
            function handleChange() {}
  
            function handleNext1(value) {
              let value2 = value * taint;
              setState(value2);
              console.log('hello');
            }
            const handleNext2 = (value) => {
              setState(taint(value));
              console.log('hello');
            };
            let handleNext3 = function(value) {
              console.log(taint);
              dispatch({ type: 'x', value });
            };
            useEffect(() => {
              return Store.subscribe(handleNext1);
            }, []);
            useLayoutEffect(() => {
              return Store.subscribe(handleNext2);
            }, []);
            useMemo(() => {
              return Store.subscribe(handleNext3);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'handleNext1'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [handleNext1]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    let [, setState] = useState();
                    let [, dispatch] = React.useReducer();
                    let taint = props.foo;
  
                    // Shouldn't affect anything
                    function handleChange() {}
  
                    function handleNext1(value) {
                      let value2 = value * taint;
                      setState(value2);
                      console.log('hello');
                    }
                    const handleNext2 = (value) => {
                      setState(taint(value));
                      console.log('hello');
                    };
                    let handleNext3 = function(value) {
                      console.log(taint);
                      dispatch({ type: 'x', value });
                    };
                    useEffect(() => {
                      return Store.subscribe(handleNext1);
                    }, [handleNext1]);
                    useLayoutEffect(() => {
                      return Store.subscribe(handleNext2);
                    }, []);
                    useMemo(() => {
                      return Store.subscribe(handleNext3);
                    }, []);
                  }
                `,
              },
            ],
          },
          {
            message:
              "React Hook useLayoutEffect has a missing dependency: 'handleNext2'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [handleNext2]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    let [, setState] = useState();
                    let [, dispatch] = React.useReducer();
                    let taint = props.foo;
  
                    // Shouldn't affect anything
                    function handleChange() {}
  
                    function handleNext1(value) {
                      let value2 = value * taint;
                      setState(value2);
                      console.log('hello');
                    }
                    const handleNext2 = (value) => {
                      setState(taint(value));
                      console.log('hello');
                    };
                    let handleNext3 = function(value) {
                      console.log(taint);
                      dispatch({ type: 'x', value });
                    };
                    useEffect(() => {
                      return Store.subscribe(handleNext1);
                    }, []);
                    useLayoutEffect(() => {
                      return Store.subscribe(handleNext2);
                    }, [handleNext2]);
                    useMemo(() => {
                      return Store.subscribe(handleNext3);
                    }, []);
                  }
                `,
              },
            ],
          },
          {
            message:
              "React Hook useMemo has a missing dependency: 'handleNext3'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [handleNext3]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    let [, setState] = useState();
                    let [, dispatch] = React.useReducer();
                    let taint = props.foo;
  
                    // Shouldn't affect anything
                    function handleChange() {}
  
                    function handleNext1(value) {
                      let value2 = value * taint;
                      setState(value2);
                      console.log('hello');
                    }
                    const handleNext2 = (value) => {
                      setState(taint(value));
                      console.log('hello');
                    };
                    let handleNext3 = function(value) {
                      console.log(taint);
                      dispatch({ type: 'x', value });
                    };
                    useEffect(() => {
                      return Store.subscribe(handleNext1);
                    }, []);
                    useLayoutEffect(() => {
                      return Store.subscribe(handleNext2);
                    }, []);
                    useMemo(() => {
                      return Store.subscribe(handleNext3);
                    }, [handleNext3]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // Regression test
        code: normalizeIndent`
          function MyComponent(props) {
            let [, setState] = useState();
            let [, dispatch] = React.useReducer();
            let taint = props.foo;
  
            // Shouldn't affect anything
            const handleChange = () => {};
  
            function handleNext1(value) {
              let value2 = value * taint;
              setState(value2);
              console.log('hello');
            }
            const handleNext2 = (value) => {
              setState(taint(value));
              console.log('hello');
            };
            let handleNext3 = function(value) {
              console.log(taint);
              dispatch({ type: 'x', value });
            };
            useEffect(() => {
              return Store.subscribe(handleNext1);
            }, []);
            useLayoutEffect(() => {
              return Store.subscribe(handleNext2);
            }, []);
            useMemo(() => {
              return Store.subscribe(handleNext3);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'handleNext1'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [handleNext1]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    let [, setState] = useState();
                    let [, dispatch] = React.useReducer();
                    let taint = props.foo;
  
                    // Shouldn't affect anything
                    const handleChange = () => {};
  
                    function handleNext1(value) {
                      let value2 = value * taint;
                      setState(value2);
                      console.log('hello');
                    }
                    const handleNext2 = (value) => {
                      setState(taint(value));
                      console.log('hello');
                    };
                    let handleNext3 = function(value) {
                      console.log(taint);
                      dispatch({ type: 'x', value });
                    };
                    useEffect(() => {
                      return Store.subscribe(handleNext1);
                    }, [handleNext1]);
                    useLayoutEffect(() => {
                      return Store.subscribe(handleNext2);
                    }, []);
                    useMemo(() => {
                      return Store.subscribe(handleNext3);
                    }, []);
                  }
                `,
              },
            ],
          },
          {
            message:
              "React Hook useLayoutEffect has a missing dependency: 'handleNext2'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [handleNext2]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    let [, setState] = useState();
                    let [, dispatch] = React.useReducer();
                    let taint = props.foo;
  
                    // Shouldn't affect anything
                    const handleChange = () => {};
  
                    function handleNext1(value) {
                      let value2 = value * taint;
                      setState(value2);
                      console.log('hello');
                    }
                    const handleNext2 = (value) => {
                      setState(taint(value));
                      console.log('hello');
                    };
                    let handleNext3 = function(value) {
                      console.log(taint);
                      dispatch({ type: 'x', value });
                    };
                    useEffect(() => {
                      return Store.subscribe(handleNext1);
                    }, []);
                    useLayoutEffect(() => {
                      return Store.subscribe(handleNext2);
                    }, [handleNext2]);
                    useMemo(() => {
                      return Store.subscribe(handleNext3);
                    }, []);
                  }
                `,
              },
            ],
          },
          {
            message:
              "React Hook useMemo has a missing dependency: 'handleNext3'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [handleNext3]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    let [, setState] = useState();
                    let [, dispatch] = React.useReducer();
                    let taint = props.foo;
  
                    // Shouldn't affect anything
                    const handleChange = () => {};
  
                    function handleNext1(value) {
                      let value2 = value * taint;
                      setState(value2);
                      console.log('hello');
                    }
                    const handleNext2 = (value) => {
                      setState(taint(value));
                      console.log('hello');
                    };
                    let handleNext3 = function(value) {
                      console.log(taint);
                      dispatch({ type: 'x', value });
                    };
                    useEffect(() => {
                      return Store.subscribe(handleNext1);
                    }, []);
                    useLayoutEffect(() => {
                      return Store.subscribe(handleNext2);
                    }, []);
                    useMemo(() => {
                      return Store.subscribe(handleNext3);
                    }, [handleNext3]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            let [, setState] = useState();
  
            function handleNext(value) {
              setState(value);
            }
  
            useEffect(() => {
              return Store.subscribe(handleNext);
            }, [handleNext]);
          }
        `,
        errors: [
          {
            message:
              `The 'handleNext' function makes the dependencies of ` +
              `useEffect Hook (at line 11) change on every render. ` +
              `Move it inside the useEffect callback. Alternatively, ` +
              `wrap the definition of 'handleNext' in its own useCallback() Hook.`,
            // Not gonna fix a function definition
            // because it's not always safe due to hoisting.
            suggestions: undefined,
          },
        ],
      },
      {
        // Even if the function only references static values,
        // once you specify it in deps, it will invalidate them.
        code: normalizeIndent`
          function MyComponent(props) {
            let [, setState] = useState();
  
            const handleNext = (value) => {
              setState(value);
            };
  
            useEffect(() => {
              return Store.subscribe(handleNext);
            }, [handleNext]);
          }
        `,
        errors: [
          {
            message:
              `The 'handleNext' function makes the dependencies of ` +
              `useEffect Hook (at line 11) change on every render. ` +
              `Move it inside the useEffect callback. Alternatively, ` +
              `wrap the definition of 'handleNext' in its own useCallback() Hook.`,
            // We don't fix moving (too invasive). But that's the suggested fix
            // when only effect uses this function. Otherwise, we'd useCallback.
            suggestions: undefined,
          },
        ],
      },
      {
        // Even if the function only references static values,
        // once you specify it in deps, it will invalidate them.
        // However, we can't suggest moving handleNext into the
        // effect because it is *also* used outside of it.
        // So our suggestion is useCallback().
        code: normalizeIndent`
          function MyComponent(props) {
            let [, setState] = useState();
  
            const handleNext = (value) => {
              setState(value);
            };
  
            useEffect(() => {
              return Store.subscribe(handleNext);
            }, [handleNext]);
  
            return <div onClick={handleNext} />;
          }
        `,
        errors: [
          {
            message:
              `The 'handleNext' function makes the dependencies of ` +
              `useEffect Hook (at line 11) change on every render. ` +
              `To fix this, wrap the definition of 'handleNext' in its own useCallback() Hook.`,
            // We fix this one with useCallback since it's
            // the easy fix and you can't just move it into effect.
            suggestions: [
              {
                desc:
                  "Wrap the definition of 'handleNext' in its own useCallback() Hook.",
                output: normalizeIndent`
                  function MyComponent(props) {
                    let [, setState] = useState();
  
                    const handleNext = useCallback((value) => {
                      setState(value);
                    });
  
                    useEffect(() => {
                      return Store.subscribe(handleNext);
                    }, [handleNext]);
  
                    return <div onClick={handleNext} />;
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            function handleNext1() {
              console.log('hello');
            }
            const handleNext2 = () => {
              console.log('hello');
            };
            let handleNext3 = function() {
              console.log('hello');
            };
            useEffect(() => {
              return Store.subscribe(handleNext1);
            }, [handleNext1]);
            useLayoutEffect(() => {
              return Store.subscribe(handleNext2);
            }, [handleNext2]);
            useMemo(() => {
              return Store.subscribe(handleNext3);
            }, [handleNext3]);
          }
        `,
        errors: [
          {
            message:
              "The 'handleNext1' function makes the dependencies of useEffect Hook " +
              '(at line 14) change on every render. Move it inside the useEffect callback. ' +
              "Alternatively, wrap the definition of 'handleNext1' in its own useCallback() Hook.",
            suggestions: undefined,
          },
          {
            message:
              "The 'handleNext2' function makes the dependencies of useLayoutEffect Hook " +
              '(at line 17) change on every render. Move it inside the useLayoutEffect callback. ' +
              "Alternatively, wrap the definition of 'handleNext2' in its own useCallback() Hook.",
            suggestions: undefined,
          },
          {
            message:
              "The 'handleNext3' function makes the dependencies of useMemo Hook " +
              '(at line 20) change on every render. Move it inside the useMemo callback. ' +
              "Alternatively, wrap the definition of 'handleNext3' in its own useCallback() Hook.",
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            function handleNext1() {
              console.log('hello');
            }
            const handleNext2 = () => {
              console.log('hello');
            };
            let handleNext3 = function() {
              console.log('hello');
            };
            useEffect(() => {
              handleNext1();
              return Store.subscribe(() => handleNext1());
            }, [handleNext1]);
            useLayoutEffect(() => {
              handleNext2();
              return Store.subscribe(() => handleNext2());
            }, [handleNext2]);
            useMemo(() => {
              handleNext3();
              return Store.subscribe(() => handleNext3());
            }, [handleNext3]);
          }
        `,
        // Suggestions don't wrap into useCallback here
        // because they are only referenced by effect itself.
        errors: [
          {
            message:
              "The 'handleNext1' function makes the dependencies of useEffect Hook " +
              '(at line 15) change on every render. Move it inside the useEffect callback. ' +
              "Alternatively, wrap the definition of 'handleNext1' in its own useCallback() Hook.",
            suggestions: undefined,
          },
          {
            message:
              "The 'handleNext2' function makes the dependencies of useLayoutEffect Hook " +
              '(at line 19) change on every render. Move it inside the useLayoutEffect callback. ' +
              "Alternatively, wrap the definition of 'handleNext2' in its own useCallback() Hook.",
            suggestions: undefined,
          },
          {
            message:
              "The 'handleNext3' function makes the dependencies of useMemo Hook " +
              '(at line 23) change on every render. Move it inside the useMemo callback. ' +
              "Alternatively, wrap the definition of 'handleNext3' in its own useCallback() Hook.",
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            function handleNext1() {
              console.log('hello');
            }
            const handleNext2 = () => {
              console.log('hello');
            };
            let handleNext3 = function() {
              console.log('hello');
            };
            useEffect(() => {
              handleNext1();
              return Store.subscribe(() => handleNext1());
            }, [handleNext1]);
            useLayoutEffect(() => {
              handleNext2();
              return Store.subscribe(() => handleNext2());
            }, [handleNext2]);
            useMemo(() => {
              handleNext3();
              return Store.subscribe(() => handleNext3());
            }, [handleNext3]);
            return (
              <div
                onClick={() => {
                  handleNext1();
                  setTimeout(handleNext2);
                  setTimeout(() => {
                    handleNext3();
                  });
                }}
              />
            );
          }
        `,
        errors: [
          {
            message:
              "The 'handleNext1' function makes the dependencies of useEffect Hook " +
              '(at line 15) change on every render. To fix this, wrap the ' +
              "definition of 'handleNext1' in its own useCallback() Hook.",
            suggestions: undefined,
          },
          {
            message:
              "The 'handleNext2' function makes the dependencies of useLayoutEffect Hook " +
              '(at line 19) change on every render. To fix this, wrap the ' +
              "definition of 'handleNext2' in its own useCallback() Hook.",
            // Suggestion wraps into useCallback where possible (variables only)
            // because they are only referenced outside the effect.
            suggestions: [
              {
                desc:
                  "Wrap the definition of 'handleNext2' in its own useCallback() Hook.",
                output: normalizeIndent`
                  function MyComponent(props) {
                    function handleNext1() {
                      console.log('hello');
                    }
                    const handleNext2 = useCallback(() => {
                      console.log('hello');
                    });
                    let handleNext3 = function() {
                      console.log('hello');
                    };
                    useEffect(() => {
                      handleNext1();
                      return Store.subscribe(() => handleNext1());
                    }, [handleNext1]);
                    useLayoutEffect(() => {
                      handleNext2();
                      return Store.subscribe(() => handleNext2());
                    }, [handleNext2]);
                    useMemo(() => {
                      handleNext3();
                      return Store.subscribe(() => handleNext3());
                    }, [handleNext3]);
                    return (
                      <div
                        onClick={() => {
                          handleNext1();
                          setTimeout(handleNext2);
                          setTimeout(() => {
                            handleNext3();
                          });
                        }}
                      />
                    );
                  }
                `,
              },
            ],
          },
          {
            message:
              "The 'handleNext3' function makes the dependencies of useMemo Hook " +
              '(at line 23) change on every render. To fix this, wrap the ' +
              "definition of 'handleNext3' in its own useCallback() Hook.",
            // Autofix wraps into useCallback where possible (variables only)
            // because they are only referenced outside the effect.
            suggestions: [
              {
                desc:
                  "Wrap the definition of 'handleNext3' in its own useCallback() Hook.",
                output: normalizeIndent`
                  function MyComponent(props) {
                    function handleNext1() {
                      console.log('hello');
                    }
                    const handleNext2 = () => {
                      console.log('hello');
                    };
                    let handleNext3 = useCallback(function() {
                      console.log('hello');
                    });
                    useEffect(() => {
                      handleNext1();
                      return Store.subscribe(() => handleNext1());
                    }, [handleNext1]);
                    useLayoutEffect(() => {
                      handleNext2();
                      return Store.subscribe(() => handleNext2());
                    }, [handleNext2]);
                    useMemo(() => {
                      handleNext3();
                      return Store.subscribe(() => handleNext3());
                    }, [handleNext3]);
                    return (
                      <div
                        onClick={() => {
                          handleNext1();
                          setTimeout(handleNext2);
                          setTimeout(() => {
                            handleNext3();
                          });
                        }}
                      />
                    );
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            const handleNext1 = () => {
              console.log('hello');
            };
            function handleNext2() {
              console.log('hello');
            }
            useEffect(() => {
              return Store.subscribe(handleNext1);
              return Store.subscribe(handleNext2);
            }, [handleNext1, handleNext2]);
            useEffect(() => {
              return Store.subscribe(handleNext1);
              return Store.subscribe(handleNext2);
            }, [handleNext1, handleNext2]);
          }
        `,
        // Normally we'd suggest moving handleNext inside an
        // effect. But it's used by more than one. So we
        // suggest useCallback() and use it for the autofix
        // where possible (variable but not declaration).
        // TODO: we could coalesce messages for the same function if it affects multiple Hooks.
        errors: [
          {
            message:
              "The 'handleNext1' function makes the dependencies of useEffect Hook " +
              '(at line 12) change on every render. To fix this, wrap the ' +
              "definition of 'handleNext1' in its own useCallback() Hook.",
            suggestions: [
              {
                desc:
                  "Wrap the definition of 'handleNext1' in its own useCallback() Hook.",
                output: normalizeIndent`
                  function MyComponent(props) {
                    const handleNext1 = useCallback(() => {
                      console.log('hello');
                    });
                    function handleNext2() {
                      console.log('hello');
                    }
                    useEffect(() => {
                      return Store.subscribe(handleNext1);
                      return Store.subscribe(handleNext2);
                    }, [handleNext1, handleNext2]);
                    useEffect(() => {
                      return Store.subscribe(handleNext1);
                      return Store.subscribe(handleNext2);
                    }, [handleNext1, handleNext2]);
                  }
                `,
              },
            ],
          },
          {
            message:
              "The 'handleNext1' function makes the dependencies of useEffect Hook " +
              '(at line 16) change on every render. To fix this, wrap the ' +
              "definition of 'handleNext1' in its own useCallback() Hook.",
            suggestions: [
              {
                desc:
                  "Wrap the definition of 'handleNext1' in its own useCallback() Hook.",
                output: normalizeIndent`
                  function MyComponent(props) {
                    const handleNext1 = useCallback(() => {
                      console.log('hello');
                    });
                    function handleNext2() {
                      console.log('hello');
                    }
                    useEffect(() => {
                      return Store.subscribe(handleNext1);
                      return Store.subscribe(handleNext2);
                    }, [handleNext1, handleNext2]);
                    useEffect(() => {
                      return Store.subscribe(handleNext1);
                      return Store.subscribe(handleNext2);
                    }, [handleNext1, handleNext2]);
                  }
                `,
              },
            ],
          },
          {
            message:
              "The 'handleNext2' function makes the dependencies of useEffect Hook " +
              '(at line 12) change on every render. To fix this, wrap the ' +
              "definition of 'handleNext2' in its own useCallback() Hook.",
            suggestions: undefined,
          },
          {
            message:
              "The 'handleNext2' function makes the dependencies of useEffect Hook " +
              '(at line 16) change on every render. To fix this, wrap the ' +
              "definition of 'handleNext2' in its own useCallback() Hook.",
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            let handleNext = () => {
              console.log('hello');
            };
            if (props.foo) {
              handleNext = () => {
                console.log('hello');
              };
            }
            useEffect(() => {
              return Store.subscribe(handleNext);
            }, [handleNext]);
          }
        `,
        errors: [
          {
            message:
              "The 'handleNext' function makes the dependencies of useEffect Hook " +
              '(at line 13) change on every render. To fix this, wrap the definition of ' +
              "'handleNext' in its own useCallback() Hook.",
            // Normally we'd suggest moving handleNext inside an
            // effect. But it's used more than once.
            // TODO: our autofix here isn't quite sufficient because
            // it only wraps the first definition. But seems ok.
            suggestions: [
              {
                desc:
                  "Wrap the definition of 'handleNext' in its own useCallback() Hook.",
                output: normalizeIndent`
                  function MyComponent(props) {
                    let handleNext = useCallback(() => {
                      console.log('hello');
                    });
                    if (props.foo) {
                      handleNext = () => {
                        console.log('hello');
                      };
                    }
                    useEffect(() => {
                      return Store.subscribe(handleNext);
                    }, [handleNext]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            let [, setState] = useState();
            let taint = props.foo;
  
            function handleNext(value) {
              let value2 = value * taint;
              setState(value2);
              console.log('hello');
            }
  
            useEffect(() => {
              return Store.subscribe(handleNext);
            }, [handleNext]);
          }
        `,
        errors: [
          {
            message:
              `The 'handleNext' function makes the dependencies of ` +
              `useEffect Hook (at line 14) change on every render. ` +
              `Move it inside the useEffect callback. Alternatively, wrap the ` +
              `definition of 'handleNext' in its own useCallback() Hook.`,
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Counter() {
            let [count, setCount] = useState(0);
  
            useEffect(() => {
              let id = setInterval(() => {
                setCount(count + 1);
              }, 1000);
              return () => clearInterval(id);
            }, []);
  
            return <h1>{count}</h1>;
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'count'. " +
              'Either include it or remove the dependency array. ' +
              `You can also do a functional update 'setCount(c => ...)' if you ` +
              `only need 'count' in the 'setCount' call.`,
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [count]',
                output: normalizeIndent`
                  function Counter() {
                    let [count, setCount] = useState(0);
  
                    useEffect(() => {
                      let id = setInterval(() => {
                        setCount(count + 1);
                      }, 1000);
                      return () => clearInterval(id);
                    }, [count]);
  
                    return <h1>{count}</h1>;
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Counter() {
            let [count, setCount] = useState(0);
            let [increment, setIncrement] = useState(0);
  
            useEffect(() => {
              let id = setInterval(() => {
                setCount(count + increment);
              }, 1000);
              return () => clearInterval(id);
            }, []);
  
            return <h1>{count}</h1>;
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has missing dependencies: 'count' and 'increment'. " +
              'Either include them or remove the dependency array. ' +
              `You can also do a functional update 'setCount(c => ...)' if you ` +
              `only need 'count' in the 'setCount' call.`,
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [count, increment]',
                output: normalizeIndent`
                  function Counter() {
                    let [count, setCount] = useState(0);
                    let [increment, setIncrement] = useState(0);
  
                    useEffect(() => {
                      let id = setInterval(() => {
                        setCount(count + increment);
                      }, 1000);
                      return () => clearInterval(id);
                    }, [count, increment]);
  
                    return <h1>{count}</h1>;
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Counter() {
            let [count, setCount] = useState(0);
            let [increment, setIncrement] = useState(0);
  
            useEffect(() => {
              let id = setInterval(() => {
                setCount(count => count + increment);
              }, 1000);
              return () => clearInterval(id);
            }, []);
  
            return <h1>{count}</h1>;
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'increment'. " +
              'Either include it or remove the dependency array. ' +
              `You can also replace multiple useState variables with useReducer ` +
              `if 'setCount' needs the current value of 'increment'.`,
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [increment]',
                output: normalizeIndent`
                  function Counter() {
                    let [count, setCount] = useState(0);
                    let [increment, setIncrement] = useState(0);
  
                    useEffect(() => {
                      let id = setInterval(() => {
                        setCount(count => count + increment);
                      }, 1000);
                      return () => clearInterval(id);
                    }, [increment]);
  
                    return <h1>{count}</h1>;
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Counter() {
            let [count, setCount] = useState(0);
            let increment = useCustomHook();
  
            useEffect(() => {
              let id = setInterval(() => {
                setCount(count => count + increment);
              }, 1000);
              return () => clearInterval(id);
            }, []);
  
            return <h1>{count}</h1>;
          }
        `,
        // This intentionally doesn't show the reducer message
        // because we don't know if it's safe for it to close over a value.
        // We only show it for state variables (and possibly props).
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'increment'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [increment]',
                output: normalizeIndent`
                  function Counter() {
                    let [count, setCount] = useState(0);
                    let increment = useCustomHook();
  
                    useEffect(() => {
                      let id = setInterval(() => {
                        setCount(count => count + increment);
                      }, 1000);
                      return () => clearInterval(id);
                    }, [increment]);
  
                    return <h1>{count}</h1>;
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Counter({ step }) {
            let [count, setCount] = useState(0);
  
            function increment(x) {
              return x + step;
            }
  
            useEffect(() => {
              let id = setInterval(() => {
                setCount(count => increment(count));
              }, 1000);
              return () => clearInterval(id);
            }, []);
  
            return <h1>{count}</h1>;
          }
        `,
        // This intentionally doesn't show the reducer message
        // because we don't know if it's safe for it to close over a value.
        // We only show it for state variables (and possibly props).
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'increment'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [increment]',
                output: normalizeIndent`
                  function Counter({ step }) {
                    let [count, setCount] = useState(0);
  
                    function increment(x) {
                      return x + step;
                    }
  
                    useEffect(() => {
                      let id = setInterval(() => {
                        setCount(count => increment(count));
                      }, 1000);
                      return () => clearInterval(id);
                    }, [increment]);
  
                    return <h1>{count}</h1>;
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Counter({ step }) {
            let [count, setCount] = useState(0);
  
            function increment(x) {
              return x + step;
            }
  
            useEffect(() => {
              let id = setInterval(() => {
                setCount(count => increment(count));
              }, 1000);
              return () => clearInterval(id);
            }, [increment]);
  
            return <h1>{count}</h1>;
          }
        `,
        errors: [
          {
            message:
              `The 'increment' function makes the dependencies of useEffect Hook ` +
              `(at line 14) change on every render. Move it inside the useEffect callback. ` +
              `Alternatively, wrap the definition of \'increment\' in its own ` +
              `useCallback() Hook.`,
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Counter({ increment }) {
            let [count, setCount] = useState(0);
  
            useEffect(() => {
              let id = setInterval(() => {
                setCount(count => count + increment);
              }, 1000);
              return () => clearInterval(id);
            }, []);
  
            return <h1>{count}</h1>;
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'increment'. " +
              'Either include it or remove the dependency array. ' +
              `If 'setCount' needs the current value of 'increment', ` +
              `you can also switch to useReducer instead of useState and read 'increment' in the reducer.`,
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [increment]',
                output: normalizeIndent`
                  function Counter({ increment }) {
                    let [count, setCount] = useState(0);
  
                    useEffect(() => {
                      let id = setInterval(() => {
                        setCount(count => count + increment);
                      }, 1000);
                      return () => clearInterval(id);
                    }, [increment]);
  
                    return <h1>{count}</h1>;
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Counter() {
            const [count, setCount] = useState(0);
  
            function tick() {
              setCount(count + 1);
            }
  
            useEffect(() => {
              let id = setInterval(() => {
                tick();
              }, 1000);
              return () => clearInterval(id);
            }, []);
  
            return <h1>{count}</h1>;
          }
        `,
        // TODO: ideally this should suggest useState updater form
        // since this code doesn't actually work. The autofix could
        // at least avoid suggesting 'tick' since it's obviously
        // always different, and thus useless.
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'tick'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [tick]',
                output: normalizeIndent`
                  function Counter() {
                    const [count, setCount] = useState(0);
  
                    function tick() {
                      setCount(count + 1);
                    }
  
                    useEffect(() => {
                      let id = setInterval(() => {
                        tick();
                      }, 1000);
                      return () => clearInterval(id);
                    }, [tick]);
  
                    return <h1>{count}</h1>;
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // Regression test for a crash
        code: normalizeIndent`
          function Podcasts() {
            useEffect(() => {
              alert(podcasts);
            }, []);
            let [podcasts, setPodcasts] = useState(null);
          }
        `,
        errors: [
          {
            message:
              `React Hook useEffect has a missing dependency: 'podcasts'. ` +
              `Either include it or remove the dependency array.`,
            // Note: this autofix is shady because
            // the variable is used before declaration.
            // TODO: Maybe we can catch those fixes and not autofix.
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [podcasts]',
                output: normalizeIndent`
                  function Podcasts() {
                    useEffect(() => {
                      alert(podcasts);
                    }, [podcasts]);
                    let [podcasts, setPodcasts] = useState(null);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Podcasts({ fetchPodcasts, id }) {
            let [podcasts, setPodcasts] = useState(null);
            useEffect(() => {
              fetchPodcasts(id).then(setPodcasts);
            }, [id]);
          }
        `,
        errors: [
          {
            message:
              `React Hook useEffect has a missing dependency: 'fetchPodcasts'. ` +
              `Either include it or remove the dependency array. ` +
              `If 'fetchPodcasts' changes too often, ` +
              `find the parent component that defines it and wrap that definition in useCallback.`,
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [fetchPodcasts, id]',
                output: normalizeIndent`
                  function Podcasts({ fetchPodcasts, id }) {
                    let [podcasts, setPodcasts] = useState(null);
                    useEffect(() => {
                      fetchPodcasts(id).then(setPodcasts);
                    }, [fetchPodcasts, id]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Podcasts({ api: { fetchPodcasts }, id }) {
            let [podcasts, setPodcasts] = useState(null);
            useEffect(() => {
              fetchPodcasts(id).then(setPodcasts);
            }, [id]);
          }
        `,
        errors: [
          {
            message:
              `React Hook useEffect has a missing dependency: 'fetchPodcasts'. ` +
              `Either include it or remove the dependency array. ` +
              `If 'fetchPodcasts' changes too often, ` +
              `find the parent component that defines it and wrap that definition in useCallback.`,
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [fetchPodcasts, id]',
                output: normalizeIndent`
                  function Podcasts({ api: { fetchPodcasts }, id }) {
                    let [podcasts, setPodcasts] = useState(null);
                    useEffect(() => {
                      fetchPodcasts(id).then(setPodcasts);
                    }, [fetchPodcasts, id]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Podcasts({ fetchPodcasts, fetchPodcasts2, id }) {
            let [podcasts, setPodcasts] = useState(null);
            useEffect(() => {
              setTimeout(() => {
                console.log(id);
                fetchPodcasts(id).then(setPodcasts);
                fetchPodcasts2(id).then(setPodcasts);
              });
            }, [id]);
          }
        `,
        errors: [
          {
            message:
              `React Hook useEffect has missing dependencies: 'fetchPodcasts' and 'fetchPodcasts2'. ` +
              `Either include them or remove the dependency array. ` +
              `If 'fetchPodcasts' changes too often, ` +
              `find the parent component that defines it and wrap that definition in useCallback.`,
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [fetchPodcasts, fetchPodcasts2, id]',
                output: normalizeIndent`
                  function Podcasts({ fetchPodcasts, fetchPodcasts2, id }) {
                    let [podcasts, setPodcasts] = useState(null);
                    useEffect(() => {
                      setTimeout(() => {
                        console.log(id);
                        fetchPodcasts(id).then(setPodcasts);
                        fetchPodcasts2(id).then(setPodcasts);
                      });
                    }, [fetchPodcasts, fetchPodcasts2, id]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Podcasts({ fetchPodcasts, id }) {
            let [podcasts, setPodcasts] = useState(null);
            useEffect(() => {
              console.log(fetchPodcasts);
              fetchPodcasts(id).then(setPodcasts);
            }, [id]);
          }
        `,
        errors: [
          {
            message:
              `React Hook useEffect has a missing dependency: 'fetchPodcasts'. ` +
              `Either include it or remove the dependency array. ` +
              `If 'fetchPodcasts' changes too often, ` +
              `find the parent component that defines it and wrap that definition in useCallback.`,
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [fetchPodcasts, id]',
                output: normalizeIndent`
                  function Podcasts({ fetchPodcasts, id }) {
                    let [podcasts, setPodcasts] = useState(null);
                    useEffect(() => {
                      console.log(fetchPodcasts);
                      fetchPodcasts(id).then(setPodcasts);
                    }, [fetchPodcasts, id]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Podcasts({ fetchPodcasts, id }) {
            let [podcasts, setPodcasts] = useState(null);
            useEffect(() => {
              console.log(fetchPodcasts);
              fetchPodcasts?.(id).then(setPodcasts);
            }, [id]);
          }
        `,
        errors: [
          {
            message:
              `React Hook useEffect has a missing dependency: 'fetchPodcasts'. ` +
              `Either include it or remove the dependency array. ` +
              `If 'fetchPodcasts' changes too often, ` +
              `find the parent component that defines it and wrap that definition in useCallback.`,
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [fetchPodcasts, id]',
                output: normalizeIndent`
                  function Podcasts({ fetchPodcasts, id }) {
                    let [podcasts, setPodcasts] = useState(null);
                    useEffect(() => {
                      console.log(fetchPodcasts);
                      fetchPodcasts?.(id).then(setPodcasts);
                    }, [fetchPodcasts, id]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        // The mistake here is that it was moved inside the effect
        // so it can't be referenced in the deps array.
        code: normalizeIndent`
          function Thing() {
            useEffect(() => {
              const fetchData = async () => {};
              fetchData();
            }, [fetchData]);
          }
        `,
        errors: [
          {
            message:
              `React Hook useEffect has an unnecessary dependency: 'fetchData'. ` +
              `Either exclude it or remove the dependency array.`,
            suggestions: [
              {
                desc: 'Update the dependencies array to be: []',
                output: normalizeIndent`
                  function Thing() {
                    useEffect(() => {
                      const fetchData = async () => {};
                      fetchData();
                    }, []);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Hello() {
            const [state, setState] = useState(0);
            useEffect(() => {
              setState({});
            });
          }
        `,
        errors: [
          {
            message:
              `React Hook useEffect contains a call to 'setState'. ` +
              `Without a list of dependencies, this can lead to an infinite chain of updates. ` +
              `To fix this, pass [] as a second argument to the useEffect Hook.`,
            suggestions: [
              {
                desc: 'Add dependencies array: []',
                output: normalizeIndent`
                  function Hello() {
                    const [state, setState] = useState(0);
                    useEffect(() => {
                      setState({});
                    }, []);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Hello() {
            const [data, setData] = useState(0);
            useEffect(() => {
              fetchData.then(setData);
            });
          }
        `,
        errors: [
          {
            message:
              `React Hook useEffect contains a call to 'setData'. ` +
              `Without a list of dependencies, this can lead to an infinite chain of updates. ` +
              `To fix this, pass [] as a second argument to the useEffect Hook.`,
            suggestions: [
              {
                desc: 'Add dependencies array: []',
                output: normalizeIndent`
                  function Hello() {
                    const [data, setData] = useState(0);
                    useEffect(() => {
                      fetchData.then(setData);
                    }, []);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Hello({ country }) {
            const [data, setData] = useState(0);
            useEffect(() => {
              fetchData(country).then(setData);
            });
          }
        `,
        errors: [
          {
            message:
              `React Hook useEffect contains a call to 'setData'. ` +
              `Without a list of dependencies, this can lead to an infinite chain of updates. ` +
              `To fix this, pass [country] as a second argument to the useEffect Hook.`,
            suggestions: [
              {
                desc: 'Add dependencies array: [country]',
                output: normalizeIndent`
                  function Hello({ country }) {
                    const [data, setData] = useState(0);
                    useEffect(() => {
                      fetchData(country).then(setData);
                    }, [country]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Hello({ prop1, prop2 }) {
            const [state, setState] = useState(0);
            useEffect(() => {
              if (prop1) {
                setState(prop2);
              }
            });
          }
        `,
        errors: [
          {
            message:
              `React Hook useEffect contains a call to 'setState'. ` +
              `Without a list of dependencies, this can lead to an infinite chain of updates. ` +
              `To fix this, pass [prop1, prop2] as a second argument to the useEffect Hook.`,
            suggestions: [
              {
                desc: 'Add dependencies array: [prop1, prop2]',
                output: normalizeIndent`
                  function Hello({ prop1, prop2 }) {
                    const [state, setState] = useState(0);
                    useEffect(() => {
                      if (prop1) {
                        setState(prop2);
                      }
                    }, [prop1, prop2]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Thing() {
            useEffect(async () => {}, []);
          }
        `,
        errors: [
          {
            message:
              `Effect callbacks are synchronous to prevent race conditions. ` +
              `Put the async function inside:\n\n` +
              'useEffect(() => {\n' +
              '  async function fetchData() {\n' +
              '    // You can await here\n' +
              '    const response = await MyAPI.getData(someId);\n' +
              '    // ...\n' +
              '  }\n' +
              '  fetchData();\n' +
              `}, [someId]); // Or [] if effect doesn't need props or state\n\n` +
              'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Thing() {
            useEffect(async () => {});
          }
        `,
        errors: [
          {
            message:
              `Effect callbacks are synchronous to prevent race conditions. ` +
              `Put the async function inside:\n\n` +
              'useEffect(() => {\n' +
              '  async function fetchData() {\n' +
              '    // You can await here\n' +
              '    const response = await MyAPI.getData(someId);\n' +
              '    // ...\n' +
              '  }\n' +
              '  fetchData();\n' +
              `}, [someId]); // Or [] if effect doesn't need props or state\n\n` +
              'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Example() {
            const foo = useCallback(() => {
              foo();
            }, [foo]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has an unnecessary dependency: 'foo'. " +
              'Either exclude it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: []',
                output: normalizeIndent`
                  function Example() {
                    const foo = useCallback(() => {
                      foo();
                    }, []);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Example({ prop }) {
            const foo = useCallback(() => {
              prop.hello(foo);
            }, [foo]);
            const bar = useCallback(() => {
              foo();
            }, [foo]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useCallback has a missing dependency: 'prop'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [prop]',
                output: normalizeIndent`
                  function Example({ prop }) {
                    const foo = useCallback(() => {
                      prop.hello(foo);
                    }, [prop]);
                    const bar = useCallback(() => {
                      foo();
                    }, [foo]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            function myEffect() {
              console.log(local);
            }
            useEffect(myEffect, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {};
                    function myEffect() {
                      console.log(local);
                    }
                    useEffect(myEffect, [local]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            const myEffect = () => {
              console.log(local);
            };
            useEffect(myEffect, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {};
                    const myEffect = () => {
                      console.log(local);
                    };
                    useEffect(myEffect, [local]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            const myEffect = function() {
              console.log(local);
            };
            useEffect(myEffect, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {};
                    const myEffect = function() {
                      console.log(local);
                    };
                    useEffect(myEffect, [local]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            const myEffect = () => {
              otherThing();
            };
            const otherThing = () => {
              console.log(local);
            };
            useEffect(myEffect, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'otherThing'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [otherThing]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {};
                    const myEffect = () => {
                      otherThing();
                    };
                    const otherThing = () => {
                      console.log(local);
                    };
                    useEffect(myEffect, [otherThing]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            const myEffect = debounce(() => {
              console.log(local);
            }, delay);
            useEffect(myEffect, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'myEffect'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [myEffect]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {};
                    const myEffect = debounce(() => {
                      console.log(local);
                    }, delay);
                    useEffect(myEffect, [myEffect]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            const myEffect = debounce(() => {
              console.log(local);
            }, delay);
            useEffect(myEffect, [local]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'myEffect'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [myEffect]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {};
                    const myEffect = debounce(() => {
                      console.log(local);
                    }, delay);
                    useEffect(myEffect, [myEffect]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent({myEffect}) {
            useEffect(myEffect, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'myEffect'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [myEffect]',
                output: normalizeIndent`
                  function MyComponent({myEffect}) {
                    useEffect(myEffect, [myEffect]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            useEffect(debounce(() => {
              console.log(local);
            }, delay), []);
          }
        `,
        errors: [
          {
            message:
              'React Hook useEffect received a function whose dependencies ' +
              'are unknown. Pass an inline function instead.',
            suggestions: [],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const local = {};
            useEffect(() => {
              console.log(local);
            }, []);
          }
        `,
        // Dangerous autofix is enabled due to the option:
        output: normalizeIndent`
          function MyComponent() {
            const local = {};
            useEffect(() => {
              console.log(local);
            }, [local]);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
          },
        ],
        // Keep this until major IDEs and VS Code FB ESLint plugin support Suggestions API.
        options: [{enableDangerousAutofixThisMayCauseInfiniteLoops: true}],
      },
      {
        code: normalizeIndent`
          function MyComponent(props) {
            let foo = {}
            useEffect(() => {
              foo.bar.baz = 43;
              props.foo.bar.baz = 1;
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has missing dependencies: 'foo.bar' and 'props.foo.bar'. " +
              'Either include them or remove the dependency array.',
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [foo.bar, props.foo.bar]',
                output: normalizeIndent`
                  function MyComponent(props) {
                    let foo = {}
                    useEffect(() => {
                      foo.bar.baz = 43;
                      props.foo.bar.baz = 1;
                    }, [foo.bar, props.foo.bar]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Component() {
            const foo = {};
            useMemo(() => foo, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' object makes the dependencies of useMemo Hook (at line 4) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
              'useMemo() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Component() {
            const foo = [];
            useMemo(() => foo, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' array makes the dependencies of useMemo Hook (at line 4) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
              'useMemo() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Component() {
            const foo = () => {};
            useMemo(() => foo, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' function makes the dependencies of useMemo Hook (at line 4) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the definition of 'foo' in its own " +
              'useCallback() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Component() {
            const foo = function bar(){};
            useMemo(() => foo, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' function makes the dependencies of useMemo Hook (at line 4) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the definition of 'foo' in its own " +
              'useCallback() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Component() {
            const foo = class {};
            useMemo(() => foo, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' class makes the dependencies of useMemo Hook (at line 4) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
              'useMemo() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Component() {
            const foo = true ? {} : "fine";
            useMemo(() => foo, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' conditional could make the dependencies of useMemo Hook (at line 4) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
              'useMemo() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Component() {
            const foo = bar || {};
            useMemo(() => foo, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' logical expression could make the dependencies of useMemo Hook (at line 4) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
              'useMemo() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Component() {
            const foo = bar ?? {};
            useMemo(() => foo, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' logical expression could make the dependencies of useMemo Hook (at line 4) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
              'useMemo() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Component() {
            const foo = bar && {};
            useMemo(() => foo, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' logical expression could make the dependencies of useMemo Hook (at line 4) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
              'useMemo() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Component() {
            const foo = bar ? baz ? {} : null : null;
            useMemo(() => foo, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' conditional could make the dependencies of useMemo Hook (at line 4) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
              'useMemo() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Component() {
            let foo = {};
            useMemo(() => foo, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' object makes the dependencies of useMemo Hook (at line 4) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
              'useMemo() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Component() {
            var foo = {};
            useMemo(() => foo, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' object makes the dependencies of useMemo Hook (at line 4) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own " +
              'useMemo() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Component() {
            const foo = {};
            useCallback(() => {
              console.log(foo);
            }, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' object makes the dependencies of useCallback Hook (at line 6) change on every render. " +
              "Move it inside the useCallback callback. Alternatively, wrap the initialization of 'foo' in its own " +
              'useMemo() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Component() {
            const foo = {};
            useEffect(() => {
              console.log(foo);
            }, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' object makes the dependencies of useEffect Hook (at line 6) change on every render. " +
              "Move it inside the useEffect callback. Alternatively, wrap the initialization of 'foo' in its own " +
              'useMemo() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Component() {
            const foo = {};
            useLayoutEffect(() => {
              console.log(foo);
            }, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' object makes the dependencies of useLayoutEffect Hook (at line 6) change on every render. " +
              "Move it inside the useLayoutEffect callback. Alternatively, wrap the initialization of 'foo' in its own " +
              'useMemo() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Component() {
            const foo = {};
            useImperativeHandle(
              ref,
              () => {
                 console.log(foo);
              },
              [foo]
            );
          }
        `,
        errors: [
          {
            message:
              "The 'foo' object makes the dependencies of useImperativeHandle Hook (at line 9) change on every render. " +
              "Move it inside the useImperativeHandle callback. Alternatively, wrap the initialization of 'foo' in its own " +
              'useMemo() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Foo(section) {
            const foo = section.section_components?.edges ?? [];
            useEffect(() => {
              console.log(foo);
            }, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' logical expression could make the dependencies of useEffect Hook (at line 6) change on every render. " +
              "Move it inside the useEffect callback. Alternatively, wrap the initialization of 'foo' in its own " +
              'useMemo() Hook.',
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Foo(section) {
            const foo = {};
            console.log(foo);
            useMemo(() => {
              console.log(foo);
            }, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' object makes the dependencies of useMemo Hook (at line 7) change on every render. " +
              "To fix this, wrap the initialization of 'foo' in its own useMemo() Hook.",
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Foo() {
            const foo = <>Hi!</>;
            useMemo(() => {
              console.log(foo);
            }, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' JSX fragment makes the dependencies of useMemo Hook (at line 6) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own useMemo() Hook.",
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Foo() {
            const foo = <div>Hi!</div>;
            useMemo(() => {
              console.log(foo);
            }, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' JSX element makes the dependencies of useMemo Hook (at line 6) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own useMemo() Hook.",
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Foo() {
            const foo = bar = {};
            useMemo(() => {
              console.log(foo);
            }, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' assignment expression makes the dependencies of useMemo Hook (at line 6) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own useMemo() Hook.",
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Foo() {
            const foo = new String('foo'); // Note 'foo' will be boxed, and thus an object and thus compared by reference.
            useMemo(() => {
              console.log(foo);
            }, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' object construction makes the dependencies of useMemo Hook (at line 6) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own useMemo() Hook.",
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Foo() {
            const foo = new Map([]);
            useMemo(() => {
              console.log(foo);
            }, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' object construction makes the dependencies of useMemo Hook (at line 6) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own useMemo() Hook.",
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Foo() {
            const foo = /reg/;
            useMemo(() => {
              console.log(foo);
            }, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' regular expression makes the dependencies of useMemo Hook (at line 6) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own useMemo() Hook.",
            suggestions: undefined,
          },
        ],
      },
  
      {
        code: normalizeIndent`
          function Foo() {
            class Bar {};
            useMemo(() => {
              console.log(new Bar());
            }, [Bar]);
          }
        `,
        errors: [
          {
            message:
              "The 'Bar' class makes the dependencies of useMemo Hook (at line 6) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'Bar' in its own useMemo() Hook.",
            suggestions: undefined,
          },
        ],
      },
      {
        code: normalizeIndent`
          function Foo() {
            const foo = {};
            useLayoutEffect(() => {
              console.log(foo);
            }, [foo]);
            useEffect(() => {
              console.log(foo);
            }, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' object makes the dependencies of useLayoutEffect Hook (at line 6) change on every render. " +
              "To fix this, wrap the initialization of 'foo' in its own useMemo() Hook.",
            suggestions: undefined,
          },
          {
            message:
              "The 'foo' object makes the dependencies of useEffect Hook (at line 9) change on every render. " +
              "To fix this, wrap the initialization of 'foo' in its own useMemo() Hook.",
            suggestions: undefined,
          },
        ],
      },
    ],
  };
  
  // Tests that are only valid/invalid across parsers supporting Flow
  const testsFlow = {
    valid: [
      // Ignore Generic Type Variables for arrow functions
      {
        code: normalizeIndent`
          function Example({ prop }) {
            const bar = useEffect(<T>(a: T): Hello => {
              prop();
            }, [prop]);
          }
        `,
      },
    ],
    invalid: [
      {
        code: normalizeIndent`
        function Foo() {
          const foo = ({}: any);
          useMemo(() => {
            console.log(foo);
          }, [foo]);
        }
      `,
        errors: [
          {
            message:
              "The 'foo' object makes the dependencies of useMemo Hook (at line 6) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own useMemo() Hook.",
            suggestions: undefined,
          },
        ],
      },
    ],
  };
  
  // Tests that are only valid/invalid across parsers supporting TypeScript
  const testsTypescript = {
    valid: [
      {
        // `ref` is still constant, despite the cast.
        code: normalizeIndent`
          function MyComponent() {
            const ref = useRef() as React.MutableRefObject<HTMLDivElement>;
            useEffect(() => {
              console.log(ref.current);
            }, []);
          }
        `,
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const [state, setState] = React.useState<number>(0);
  
            useEffect(() => {
              const someNumber: typeof state = 2;
              setState(prevState => prevState + someNumber);
            }, [])
          }
        `,
      },
      {
        code: normalizeIndent`
          function App() {
            const foo = {x: 1};
            React.useEffect(() => {
              const bar = {x: 2};
              const baz = bar as typeof foo;
              console.log(baz);
            }, []);
          }
        `,
      },
    ],
    invalid: [
      {
        // `local` is still non-constant, despite the cast.
        code: normalizeIndent`
          function MyComponent() {
            const local = {} as string;
            useEffect(() => {
              console.log(local);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'local'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [local]',
                output: normalizeIndent`
                  function MyComponent() {
                    const local = {} as string;
                    useEffect(() => {
                      console.log(local);
                    }, [local]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function App() {
            const foo = {x: 1};
            const bar = {x: 2};
            useEffect(() => {
              const baz = bar as typeof foo;
              console.log(baz);
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'bar'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [bar]',
                output: normalizeIndent`
                  function App() {
                    const foo = {x: 1};
                    const bar = {x: 2};
                    useEffect(() => {
                      const baz = bar as typeof foo;
                      console.log(baz);
                    }, [bar]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const pizza = {};
  
            useEffect(() => ({
              crust: pizza.crust,
              toppings: pizza?.toppings,
            }), []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has missing dependencies: 'pizza.crust' and 'pizza?.toppings'. " +
              'Either include them or remove the dependency array.',
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [pizza.crust, pizza?.toppings]',
                output: normalizeIndent`
                  function MyComponent() {
                    const pizza = {};
  
                    useEffect(() => ({
                      crust: pizza.crust,
                      toppings: pizza?.toppings,
                    }), [pizza.crust, pizza?.toppings]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const pizza = {};
  
            useEffect(() => ({
              crust: pizza?.crust,
              density: pizza.crust.density,
            }), []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'pizza.crust'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [pizza.crust]',
                output: normalizeIndent`
                  function MyComponent() {
                    const pizza = {};
  
                    useEffect(() => ({
                      crust: pizza?.crust,
                      density: pizza.crust.density,
                    }), [pizza.crust]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const pizza = {};
  
            useEffect(() => ({
              crust: pizza.crust,
              density: pizza?.crust.density,
            }), []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'pizza.crust'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [pizza.crust]',
                output: normalizeIndent`
                  function MyComponent() {
                    const pizza = {};
  
                    useEffect(() => ({
                      crust: pizza.crust,
                      density: pizza?.crust.density,
                    }), [pizza.crust]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const pizza = {};
  
            useEffect(() => ({
              crust: pizza?.crust,
              density: pizza?.crust.density,
            }), []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'pizza?.crust'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [pizza?.crust]',
                output: normalizeIndent`
                  function MyComponent() {
                    const pizza = {};
  
                    useEffect(() => ({
                      crust: pizza?.crust,
                      density: pizza?.crust.density,
                    }), [pizza?.crust]);
                  }
                `,
              },
            ],
          },
        ],
      },
      // Regression test.
      {
        code: normalizeIndent`
          function Example(props) {
            useEffect(() => {
              let topHeight = 0;
              topHeight = props.upperViewHeight;
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'props.upperViewHeight'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [props.upperViewHeight]',
                output: normalizeIndent`
                  function Example(props) {
                    useEffect(() => {
                      let topHeight = 0;
                      topHeight = props.upperViewHeight;
                    }, [props.upperViewHeight]);
                  }
                `,
              },
            ],
          },
        ],
      },
      // Regression test.
      {
        code: normalizeIndent`
          function Example(props) {
            useEffect(() => {
              let topHeight = 0;
              topHeight = props?.upperViewHeight;
            }, []);
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'props?.upperViewHeight'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc:
                  'Update the dependencies array to be: [props?.upperViewHeight]',
                output: normalizeIndent`
                  function Example(props) {
                    useEffect(() => {
                      let topHeight = 0;
                      topHeight = props?.upperViewHeight;
                    }, [props?.upperViewHeight]);
                  }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const [state, setState] = React.useState<number>(0);
  
            useEffect(() => {
              const someNumber: typeof state = 2;
              setState(prevState => prevState + someNumber + state);
            }, [])
          }
        `,
        errors: [
          {
            message:
              "React Hook useEffect has a missing dependency: 'state'. " +
              'Either include it or remove the dependency array. ' +
              `You can also do a functional update 'setState(s => ...)' ` +
              `if you only need 'state' in the 'setState' call.`,
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [state]',
                output: normalizeIndent`
                function MyComponent() {
                  const [state, setState] = React.useState<number>(0);
  
                  useEffect(() => {
                    const someNumber: typeof state = 2;
                    setState(prevState => prevState + someNumber + state);
                  }, [state])
                }
                `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function MyComponent() {
            const [state, setState] = React.useState<number>(0);
  
            useMemo(() => {
              const someNumber: typeof state = 2;
              console.log(someNumber);
            }, [state])
          }
        `,
        errors: [
          {
            message:
              "React Hook useMemo has an unnecessary dependency: 'state'. " +
              'Either exclude it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: []',
                output: normalizeIndent`
                  function MyComponent() {
                    const [state, setState] = React.useState<number>(0);
  
                    useMemo(() => {
                      const someNumber: typeof state = 2;
                      console.log(someNumber);
                    }, [])
                  }
                  `,
              },
            ],
          },
        ],
      },
      {
        code: normalizeIndent`
          function Foo() {
            const foo = {} as any;
            useMemo(() => {
              console.log(foo);
            }, [foo]);
          }
        `,
        errors: [
          {
            message:
              "The 'foo' object makes the dependencies of useMemo Hook (at line 6) change on every render. " +
              "Move it inside the useMemo callback. Alternatively, wrap the initialization of 'foo' in its own useMemo() Hook.",
            suggestions: undefined,
          },
        ],
      },
    ],
  };
  
  // Tests that are only valid/invalid for `@typescript-eslint/parser@4.x`
  const testsTypescriptEslintParserV4 = {
    valid: [],
    invalid: [
      // TODO: Should also be invalid as part of the JS test suite i.e. be invalid with babel eslint parsers.
      // It doesn't use any explicit types but any JS is still valid TS.
      {
        code: normalizeIndent`
          function Foo({ Component }) {
            React.useEffect(() => {
              console.log(<Component />);
            }, []);
          };
        `,
        errors: [
          {
            message:
              "React Hook React.useEffect has a missing dependency: 'Component'. " +
              'Either include it or remove the dependency array.',
            suggestions: [
              {
                desc: 'Update the dependencies array to be: [Component]',
                output: normalizeIndent`
                function Foo({ Component }) {
                  React.useEffect(() => {
                    console.log(<Component />);
                  }, [Component]);
                };
              `,
              },
            ],
          },
        ],
      },
    ],
  };
  
  // For easier local testing
  if (!process.env.CI) {
    let only = [];
    let skipped = [];
    [
      ...tests.valid,
      ...tests.invalid,
      ...testsFlow.valid,
      ...testsFlow.invalid,
      ...testsTypescript.valid,
      ...testsTypescript.invalid,
      ...testsTypescriptEslintParserV4.valid,
      ...testsTypescriptEslintParserV4.invalid,
    ].forEach(t => {
      if (t.skip) {
        delete t.skip;
        skipped.push(t);
      }
      if (t.only) {
        delete t.only;
        only.push(t);
      }
    });
    const predicate = t => {
      if (only.length > 0) {
        return only.indexOf(t) !== -1;
      }
      if (skipped.length > 0) {
        return skipped.indexOf(t) === -1;
      }
      return true;
    };
    tests.valid = tests.valid.filter(predicate);
    tests.invalid = tests.invalid.filter(predicate);
    testsFlow.valid = testsFlow.valid.filter(predicate);
    testsFlow.invalid = testsFlow.invalid.filter(predicate);
    testsTypescript.valid = testsTypescript.valid.filter(predicate);
    testsTypescript.invalid = testsTypescript.invalid.filter(predicate);
  }
  
  describe('react-hooks', () => {
    const parserOptions = {
      ecmaFeatures: {
        jsx: true,
      },
      ecmaVersion: 6,
      sourceType: 'module',
    };
  
    const testsBabelEslint = {
      valid: [...testsFlow.valid, ...tests.valid],
      invalid: [...testsFlow.invalid, ...tests.invalid],
    };
  
    new ESLintTester({
      parser: require.resolve('babel-eslint'),
      parserOptions,
    }).run('parser: babel-eslint', ReactHooksESLintRule, testsBabelEslint);
  
    new ESLintTester({
      parser: require.resolve('@babel/eslint-parser'),
      parserOptions,
    }).run(
      'parser: @babel/eslint-parser',
      ReactHooksESLintRule,
      testsBabelEslint
    );
  
    const testsTypescriptEslintParser = {
      valid: [...testsTypescript.valid, ...tests.valid],
      invalid: [...testsTypescript.invalid, ...tests.invalid],
    };
  
    new ESLintTester({
      parser: require.resolve('@typescript-eslint/parser-v2'),
      parserOptions,
    }).run(
      'parser: @typescript-eslint/parser@2.x',
      ReactHooksESLintRule,
      testsTypescriptEslintParser
    );
  
    new ESLintTester({
      parser: require.resolve('@typescript-eslint/parser-v3'),
      parserOptions,
    }).run(
      'parser: @typescript-eslint/parser@3.x',
      ReactHooksESLintRule,
      testsTypescriptEslintParser
    );
  
    new ESLintTester({
      parser: require.resolve('@typescript-eslint/parser-v4'),
      parserOptions,
    }).run('parser: @typescript-eslint/parser@4.x', ReactHooksESLintRule, {
      valid: [
        ...testsTypescriptEslintParserV4.valid,
        ...testsTypescriptEslintParser.valid,
      ],
      invalid: [
        ...testsTypescriptEslintParserV4.invalid,
        ...testsTypescriptEslintParser.invalid,
      ],
    });
  });
  