diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py index 9f0751e93609..b50fee83ec9d 100644 --- a/mypy/dmypy/client.py +++ b/mypy/dmypy/client.py @@ -19,6 +19,7 @@ from mypy.dmypy_os import alive, kill from mypy.dmypy_util import DEFAULT_STATUS_FILE, receive, send from mypy.ipc import IPCClient, IPCException +from mypy.main import RECURSION_LIMIT from mypy.util import check_python_version, get_terminal_width, should_force_color from mypy.version import __version__ @@ -267,6 +268,10 @@ class BadStatus(Exception): def main(argv: list[str]) -> None: """The code is top-down.""" check_python_version("dmypy") + + # set recursion limit consistent with mypy/main.py + sys.setrecursionlimit(RECURSION_LIMIT) + args = parser.parse_args(argv) if not args.action: parser.print_usage() diff --git a/mypy/fastparse.py b/mypy/fastparse.py index abcce74c6064..5ad505f2b7e1 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -240,6 +240,29 @@ def parse( strip_function_bodies=strip_function_bodies, path=fnam, ).visit(ast) + + except RecursionError as e: + # For very complex expressions it is possible to hit recursion limit + # before reaching a leaf node. + # Should reject at top level instead at bottom, since bottom would already + # be at the threshold of the recursion limit, and may fail again later. + # E.G. x1+x2+x3+...+xn -> BinOp(left=BinOp(left=BinOp(left=... + try: + # But to prove that is the cause of this particular recursion error, + # try to walk the tree using builtin visitor + ast3.NodeVisitor().visit(ast) + except RecursionError: + errors.report( + -1, -1, "Source expression too complex to parse", blocker=False, code=codes.MISC + ) + + tree = MypyFile([], [], False, {}) + + else: + # re-raise original recursion error if it *can* be unparsed, + # maybe this is some other issue that shouldn't be silenced/misdirected + raise e + except SyntaxError as e: # alias to please mypyc is_py38_or_earlier = sys.version_info < (3, 9) @@ -414,6 +437,7 @@ def visit(self, node: AST | None) -> Any: method = "visit_" + node.__class__.__name__ visitor = getattr(self, method) self.visitor_cache[typeobj] = visitor + return visitor(node) def set_line(self, node: N, n: AstNode) -> N: diff --git a/mypy/main.py b/mypy/main.py index f177bb1c2062..bf34edefe25d 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -29,6 +29,7 @@ orig_stat: Final = os.stat MEM_PROFILE: Final = False # If True, dump memory profile +RECURSION_LIMIT: Final = 2**14 def stat_proxy(path: str) -> os.stat_result: @@ -63,7 +64,7 @@ def main( util.check_python_version("mypy") t0 = time.time() # To log stat() calls: os.stat = stat_proxy - sys.setrecursionlimit(2**14) + sys.setrecursionlimit(RECURSION_LIMIT) if args is None: args = sys.argv[1:]