    def visit_If(self, if_node: ast.If):
        """
        Swap out the pattern `If(x): Raise(y)` with a ConditionalExceptionWrapper
        specialized for the specific exception y. The specialized
        ConditionalExceptionWrapper module will be added in the RewrittenModule.
        Only works with builtin Exceptions, as we assume the signature of the
        init for the Exception is a string.
        """
        raise_node = if_node.body[0]
        if not isinstance(raise_node, ast.Raise):
            return if_node

        # Don't handle orelse for now.
        # TODO: Move orelse to the body after calling ConditionalExceptionWrapper.
        if len(if_node.orelse) != 0:
            return if_node

        def _reuse_loc(node):
            return ast.copy_location(node, if_node)

        # If the exception has a message then we expect the raise's exc to be a
        # Call w/ a msg. Else if it's a exc Name then there's no msg to use.
        node_for_exc = raise_node.exc
        if isinstance(node_for_exc, ast.Name):
            # E.g. `raise AssertionError`, i.e. without an exc_msg.
            name_node_of_exc = node_for_exc
            exc_msg = _reuse_loc(ast.Constant(None))
        elif isinstance(node_for_exc, ast.Call):
            # E.g. `raise AssertionError("error message")`
            name_node_of_exc = node_for_exc.func  # type: ignore[assignment]
            if not isinstance(name_node_of_exc, ast.Name):
                return if_node
            # Most assertions just take a single string arg, but some may not; skip
            # handling such assertions for now.
            if len(node_for_exc.args) != 1:
                return if_node
            exc_msg = node_for_exc.args[0]
        else:
            return if_node

        # Convert what we expect is the name of the exception into its
        # associated python class.
        name_of_exc = name_node_of_exc.id
        try:
            exc_type = eval(name_of_exc)  # noqa P204
        except Exception:
            return if_node

        # Check that we actually have a builtin exception.
        if (
            not issubclass(exc_type, Exception)
            or getattr(getattr(exc_type, "__class__", None), "__module__", None)
            != "builtins"
        ):
            return if_node

        # We need a ConditionalExceptionWrapper specialized for every kind of
        # exception, so add it to exceptions_rewritten to remember for later to
        # add a specialized attr with it.
        self.exceptions_rewritten.add(exc_type)

        # From here we definitely should be able to do the replacement. Create a
        # Call node to the ConditionalExceptionWrapper module we're replacing
        # the If with, with args set as the If's condition and the string of the
        # exception. The call to the self._conditional_exception_wrapper_*Error
        # module is safe because the RewrittenModule will add it as an attr
        # based on the returned exceptions_rewritten, and we assume we are
        # currently modifying the AST of a method from a RewrittenModule.
        exc_wrapper_node = ast.parse(
            f"self.{_get_exception_wrapper_attr_name(exc_type)}()", mode="eval"
        )
        assert isinstance(exc_wrapper_node, ast.Expression)
        exc_wrapper_call_node = exc_wrapper_node.body
        assert isinstance(exc_wrapper_call_node, ast.Call)
        if isinstance(if_node.test, ast.BoolOp) and isinstance(
            if_node.test.op, ast.And
        ):
            self.exceptions_bool_rewritten.add(exc_type)
            bool_wrapper_node = ast.parse(
                f"self.{_get_exception_wrapper_attr_name(exc_type)}_bool()", mode="eval"
            )
            assert isinstance(exc_wrapper_node, ast.Expression)
            bool_wrapper_call_node = bool_wrapper_node.body
            assert isinstance(exc_wrapper_call_node, ast.Call)
            bool_wrapper_call_node.args = if_node.test.values
            exc_wrapper_call_node.args = [
                _reuse_loc(bool_wrapper_call_node),
                exc_msg,
            ]
        else:
            exc_wrapper_call_node.args = [if_node.test, exc_msg]

        # Ensure that the new node conforms to the Python AST grammar
        expr_wrapper = _reuse_loc(ast.Expr(_reuse_loc(exc_wrapper_call_node)))

        # Return the new node to signify that we want to use it as a replacement
        # for the original `If x: Raise y` pattern.
        return expr_wrapper
Swap out the pattern `If(x): Raise(y)` with a ConditionalExceptionWrapper
specialized for the specific exception y. The specialized
ConditionalExceptionWrapper module will be added in the RewrittenModule.
Only works with builtin Exceptions, as we assume the signature of the
init for the Exception is a string.
fx2ait/fx2ait/acc_tracer/acc_tracer.py