From e046a54b4c51a5fe26c6bb8b2e5bc905eb2c6dbf Mon Sep 17 00:00:00 2001 From: A5rocks Date: Tue, 28 Jan 2025 19:29:56 +0900 Subject: [PATCH] Fix unsafe default return values in NodeVisitor methods (#18536) Raise NotImplementedError by default in NodeVisitor visit methods. --- mypy/checker.py | 13 ++++ mypy/semanal.py | 33 +++++++++ mypy/traverser.py | 82 +++++++++++++++++++++ mypy/visitor.py | 171 ++++++++++++++++++++++---------------------- mypy_self_check.ini | 4 -- 5 files changed, 212 insertions(+), 91 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 90b1e5e03d7e3..0194efe25799c 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -77,6 +77,7 @@ FuncBase, FuncDef, FuncItem, + GlobalDecl, IfStmt, Import, ImportAll, @@ -92,6 +93,7 @@ MypyFile, NameExpr, Node, + NonlocalDecl, OperatorAssignmentStmt, OpExpr, OverloadedFuncDef, @@ -7876,6 +7878,17 @@ def warn_deprecated_overload_item( if candidate == target: self.warn_deprecated(item.func, context) + # leafs + + def visit_pass_stmt(self, o: PassStmt, /) -> None: + return None + + def visit_nonlocal_decl(self, o: NonlocalDecl, /) -> None: + return None + + def visit_global_decl(self, o: GlobalDecl, /) -> None: + return None + class CollectArgTypeVarTypes(TypeTraverserVisitor): """Collects the non-nested argument types in a set.""" diff --git a/mypy/semanal.py b/mypy/semanal.py index f357813ff38b1..8463e07e61cbd 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -97,10 +97,12 @@ AwaitExpr, Block, BreakStmt, + BytesExpr, CallExpr, CastExpr, ClassDef, ComparisonExpr, + ComplexExpr, ConditionalExpr, Context, ContinueStmt, @@ -114,6 +116,7 @@ Expression, ExpressionStmt, FakeExpression, + FloatExpr, ForStmt, FuncBase, FuncDef, @@ -126,6 +129,7 @@ ImportBase, ImportFrom, IndexExpr, + IntExpr, LambdaExpr, ListComprehension, ListExpr, @@ -193,6 +197,7 @@ MappingPattern, OrPattern, SequencePattern, + SingletonPattern, StarredPattern, ValuePattern, ) @@ -7529,6 +7534,34 @@ def parse_dataclass_transform_field_specifiers(self, arg: Expression) -> tuple[s names.append(specifier.fullname) return tuple(names) + # leafs + def visit_int_expr(self, o: IntExpr, /) -> None: + return None + + def visit_str_expr(self, o: StrExpr, /) -> None: + return None + + def visit_bytes_expr(self, o: BytesExpr, /) -> None: + return None + + def visit_float_expr(self, o: FloatExpr, /) -> None: + return None + + def visit_complex_expr(self, o: ComplexExpr, /) -> None: + return None + + def visit_ellipsis(self, o: EllipsisExpr, /) -> None: + return None + + def visit_temp_node(self, o: TempNode, /) -> None: + return None + + def visit_pass_stmt(self, o: PassStmt, /) -> None: + return None + + def visit_singleton_pattern(self, o: SingletonPattern, /) -> None: + return None + def replace_implicit_first_type(sig: FunctionLike, new: Type) -> FunctionLike: if isinstance(sig, CallableType): diff --git a/mypy/traverser.py b/mypy/traverser.py index 2c8ea49491bc7..7d7794822396e 100644 --- a/mypy/traverser.py +++ b/mypy/traverser.py @@ -58,6 +58,7 @@ OverloadedFuncDef, ParamSpecExpr, PassStmt, + PromoteExpr, RaiseStmt, ReturnStmt, RevealExpr, @@ -67,6 +68,7 @@ StarExpr, StrExpr, SuperExpr, + TempNode, TryStmt, TupleExpr, TypeAlias, @@ -77,6 +79,7 @@ TypeVarExpr, TypeVarTupleExpr, UnaryExpr, + Var, WhileStmt, WithStmt, YieldExpr, @@ -415,6 +418,85 @@ def visit_import_from(self, o: ImportFrom, /) -> None: for a in o.assignments: a.accept(self) + # leaf nodes + def visit_name_expr(self, o: NameExpr, /) -> None: + return None + + def visit_str_expr(self, o: StrExpr, /) -> None: + return None + + def visit_int_expr(self, o: IntExpr, /) -> None: + return None + + def visit_float_expr(self, o: FloatExpr, /) -> None: + return None + + def visit_bytes_expr(self, o: BytesExpr, /) -> None: + return None + + def visit_ellipsis(self, o: EllipsisExpr, /) -> None: + return None + + def visit_var(self, o: Var, /) -> None: + return None + + def visit_continue_stmt(self, o: ContinueStmt, /) -> None: + return None + + def visit_pass_stmt(self, o: PassStmt, /) -> None: + return None + + def visit_break_stmt(self, o: BreakStmt, /) -> None: + return None + + def visit_temp_node(self, o: TempNode, /) -> None: + return None + + def visit_nonlocal_decl(self, o: NonlocalDecl, /) -> None: + return None + + def visit_global_decl(self, o: GlobalDecl, /) -> None: + return None + + def visit_import_all(self, o: ImportAll, /) -> None: + return None + + def visit_type_var_expr(self, o: TypeVarExpr, /) -> None: + return None + + def visit_paramspec_expr(self, o: ParamSpecExpr, /) -> None: + return None + + def visit_type_var_tuple_expr(self, o: TypeVarTupleExpr, /) -> None: + return None + + def visit_type_alias_expr(self, o: TypeAliasExpr, /) -> None: + return None + + def visit_type_alias(self, o: TypeAlias, /) -> None: + return None + + def visit_namedtuple_expr(self, o: NamedTupleExpr, /) -> None: + return None + + def visit_typeddict_expr(self, o: TypedDictExpr, /) -> None: + return None + + def visit_newtype_expr(self, o: NewTypeExpr, /) -> None: + return None + + def visit__promote_expr(self, o: PromoteExpr, /) -> None: + return None + + def visit_complex_expr(self, o: ComplexExpr, /) -> None: + return None + + def visit_enum_call_expr(self, o: EnumCallExpr, /) -> None: + return None + + def visit_singleton_pattern(self, o: SingletonPattern, /) -> None: + return None + class ExtendedTraverserVisitor(TraverserVisitor): """This is a more flexible traverser. diff --git a/mypy/visitor.py b/mypy/visitor.py index 6613b6cbb1444..d1b2ca4164101 100644 --- a/mypy/visitor.py +++ b/mypy/visitor.py @@ -356,273 +356,270 @@ class NodeVisitor(Generic[T], ExpressionVisitor[T], StatementVisitor[T], Pattern """Empty base class for parse tree node visitors. The T type argument specifies the return type of the visit - methods. As all methods defined here return None by default, + methods. As all methods defined here raise by default, subclasses do not always need to override all the methods. - - TODO: make the default return value explicit, then turn on - empty body checking in mypy_self_check.ini. """ # Not in superclasses: def visit_mypy_file(self, o: mypy.nodes.MypyFile, /) -> T: - pass + raise NotImplementedError() # TODO: We have a visit_var method, but no visit_typeinfo or any # other non-Statement SymbolNode (accepting those will raise a # runtime error). Maybe this should be resolved in some direction. def visit_var(self, o: mypy.nodes.Var, /) -> T: - pass + raise NotImplementedError() # Module structure def visit_import(self, o: mypy.nodes.Import, /) -> T: - pass + raise NotImplementedError() def visit_import_from(self, o: mypy.nodes.ImportFrom, /) -> T: - pass + raise NotImplementedError() def visit_import_all(self, o: mypy.nodes.ImportAll, /) -> T: - pass + raise NotImplementedError() # Definitions def visit_func_def(self, o: mypy.nodes.FuncDef, /) -> T: - pass + raise NotImplementedError() def visit_overloaded_func_def(self, o: mypy.nodes.OverloadedFuncDef, /) -> T: - pass + raise NotImplementedError() def visit_class_def(self, o: mypy.nodes.ClassDef, /) -> T: - pass + raise NotImplementedError() def visit_global_decl(self, o: mypy.nodes.GlobalDecl, /) -> T: - pass + raise NotImplementedError() def visit_nonlocal_decl(self, o: mypy.nodes.NonlocalDecl, /) -> T: - pass + raise NotImplementedError() def visit_decorator(self, o: mypy.nodes.Decorator, /) -> T: - pass + raise NotImplementedError() def visit_type_alias(self, o: mypy.nodes.TypeAlias, /) -> T: - pass + raise NotImplementedError() def visit_placeholder_node(self, o: mypy.nodes.PlaceholderNode, /) -> T: - pass + raise NotImplementedError() # Statements def visit_block(self, o: mypy.nodes.Block, /) -> T: - pass + raise NotImplementedError() def visit_expression_stmt(self, o: mypy.nodes.ExpressionStmt, /) -> T: - pass + raise NotImplementedError() def visit_assignment_stmt(self, o: mypy.nodes.AssignmentStmt, /) -> T: - pass + raise NotImplementedError() def visit_operator_assignment_stmt(self, o: mypy.nodes.OperatorAssignmentStmt, /) -> T: - pass + raise NotImplementedError() def visit_while_stmt(self, o: mypy.nodes.WhileStmt, /) -> T: - pass + raise NotImplementedError() def visit_for_stmt(self, o: mypy.nodes.ForStmt, /) -> T: - pass + raise NotImplementedError() def visit_return_stmt(self, o: mypy.nodes.ReturnStmt, /) -> T: - pass + raise NotImplementedError() def visit_assert_stmt(self, o: mypy.nodes.AssertStmt, /) -> T: - pass + raise NotImplementedError() def visit_del_stmt(self, o: mypy.nodes.DelStmt, /) -> T: - pass + raise NotImplementedError() def visit_if_stmt(self, o: mypy.nodes.IfStmt, /) -> T: - pass + raise NotImplementedError() def visit_break_stmt(self, o: mypy.nodes.BreakStmt, /) -> T: - pass + raise NotImplementedError() def visit_continue_stmt(self, o: mypy.nodes.ContinueStmt, /) -> T: - pass + raise NotImplementedError() def visit_pass_stmt(self, o: mypy.nodes.PassStmt, /) -> T: - pass + raise NotImplementedError() def visit_raise_stmt(self, o: mypy.nodes.RaiseStmt, /) -> T: - pass + raise NotImplementedError() def visit_try_stmt(self, o: mypy.nodes.TryStmt, /) -> T: - pass + raise NotImplementedError() def visit_with_stmt(self, o: mypy.nodes.WithStmt, /) -> T: - pass + raise NotImplementedError() def visit_match_stmt(self, o: mypy.nodes.MatchStmt, /) -> T: - pass + raise NotImplementedError() def visit_type_alias_stmt(self, o: mypy.nodes.TypeAliasStmt, /) -> T: - pass + raise NotImplementedError() # Expressions (default no-op implementation) def visit_int_expr(self, o: mypy.nodes.IntExpr, /) -> T: - pass + raise NotImplementedError() def visit_str_expr(self, o: mypy.nodes.StrExpr, /) -> T: - pass + raise NotImplementedError() def visit_bytes_expr(self, o: mypy.nodes.BytesExpr, /) -> T: - pass + raise NotImplementedError() def visit_float_expr(self, o: mypy.nodes.FloatExpr, /) -> T: - pass + raise NotImplementedError() def visit_complex_expr(self, o: mypy.nodes.ComplexExpr, /) -> T: - pass + raise NotImplementedError() def visit_ellipsis(self, o: mypy.nodes.EllipsisExpr, /) -> T: - pass + raise NotImplementedError() def visit_star_expr(self, o: mypy.nodes.StarExpr, /) -> T: - pass + raise NotImplementedError() def visit_name_expr(self, o: mypy.nodes.NameExpr, /) -> T: - pass + raise NotImplementedError() def visit_member_expr(self, o: mypy.nodes.MemberExpr, /) -> T: - pass + raise NotImplementedError() def visit_yield_from_expr(self, o: mypy.nodes.YieldFromExpr, /) -> T: - pass + raise NotImplementedError() def visit_yield_expr(self, o: mypy.nodes.YieldExpr, /) -> T: - pass + raise NotImplementedError() def visit_call_expr(self, o: mypy.nodes.CallExpr, /) -> T: - pass + raise NotImplementedError() def visit_op_expr(self, o: mypy.nodes.OpExpr, /) -> T: - pass + raise NotImplementedError() def visit_comparison_expr(self, o: mypy.nodes.ComparisonExpr, /) -> T: - pass + raise NotImplementedError() def visit_cast_expr(self, o: mypy.nodes.CastExpr, /) -> T: - pass + raise NotImplementedError() def visit_assert_type_expr(self, o: mypy.nodes.AssertTypeExpr, /) -> T: - pass + raise NotImplementedError() def visit_reveal_expr(self, o: mypy.nodes.RevealExpr, /) -> T: - pass + raise NotImplementedError() def visit_super_expr(self, o: mypy.nodes.SuperExpr, /) -> T: - pass + raise NotImplementedError() def visit_assignment_expr(self, o: mypy.nodes.AssignmentExpr, /) -> T: - pass + raise NotImplementedError() def visit_unary_expr(self, o: mypy.nodes.UnaryExpr, /) -> T: - pass + raise NotImplementedError() def visit_list_expr(self, o: mypy.nodes.ListExpr, /) -> T: - pass + raise NotImplementedError() def visit_dict_expr(self, o: mypy.nodes.DictExpr, /) -> T: - pass + raise NotImplementedError() def visit_tuple_expr(self, o: mypy.nodes.TupleExpr, /) -> T: - pass + raise NotImplementedError() def visit_set_expr(self, o: mypy.nodes.SetExpr, /) -> T: - pass + raise NotImplementedError() def visit_index_expr(self, o: mypy.nodes.IndexExpr, /) -> T: - pass + raise NotImplementedError() def visit_type_application(self, o: mypy.nodes.TypeApplication, /) -> T: - pass + raise NotImplementedError() def visit_lambda_expr(self, o: mypy.nodes.LambdaExpr, /) -> T: - pass + raise NotImplementedError() def visit_list_comprehension(self, o: mypy.nodes.ListComprehension, /) -> T: - pass + raise NotImplementedError() def visit_set_comprehension(self, o: mypy.nodes.SetComprehension, /) -> T: - pass + raise NotImplementedError() def visit_dictionary_comprehension(self, o: mypy.nodes.DictionaryComprehension, /) -> T: - pass + raise NotImplementedError() def visit_generator_expr(self, o: mypy.nodes.GeneratorExpr, /) -> T: - pass + raise NotImplementedError() def visit_slice_expr(self, o: mypy.nodes.SliceExpr, /) -> T: - pass + raise NotImplementedError() def visit_conditional_expr(self, o: mypy.nodes.ConditionalExpr, /) -> T: - pass + raise NotImplementedError() def visit_type_var_expr(self, o: mypy.nodes.TypeVarExpr, /) -> T: - pass + raise NotImplementedError() def visit_paramspec_expr(self, o: mypy.nodes.ParamSpecExpr, /) -> T: - pass + raise NotImplementedError() def visit_type_var_tuple_expr(self, o: mypy.nodes.TypeVarTupleExpr, /) -> T: - pass + raise NotImplementedError() def visit_type_alias_expr(self, o: mypy.nodes.TypeAliasExpr, /) -> T: - pass + raise NotImplementedError() def visit_namedtuple_expr(self, o: mypy.nodes.NamedTupleExpr, /) -> T: - pass + raise NotImplementedError() def visit_enum_call_expr(self, o: mypy.nodes.EnumCallExpr, /) -> T: - pass + raise NotImplementedError() def visit_typeddict_expr(self, o: mypy.nodes.TypedDictExpr, /) -> T: - pass + raise NotImplementedError() def visit_newtype_expr(self, o: mypy.nodes.NewTypeExpr, /) -> T: - pass + raise NotImplementedError() def visit__promote_expr(self, o: mypy.nodes.PromoteExpr, /) -> T: - pass + raise NotImplementedError() def visit_await_expr(self, o: mypy.nodes.AwaitExpr, /) -> T: - pass + raise NotImplementedError() def visit_temp_node(self, o: mypy.nodes.TempNode, /) -> T: - pass + raise NotImplementedError() # Patterns def visit_as_pattern(self, o: mypy.patterns.AsPattern, /) -> T: - pass + raise NotImplementedError() def visit_or_pattern(self, o: mypy.patterns.OrPattern, /) -> T: - pass + raise NotImplementedError() def visit_value_pattern(self, o: mypy.patterns.ValuePattern, /) -> T: - pass + raise NotImplementedError() def visit_singleton_pattern(self, o: mypy.patterns.SingletonPattern, /) -> T: - pass + raise NotImplementedError() def visit_sequence_pattern(self, o: mypy.patterns.SequencePattern, /) -> T: - pass + raise NotImplementedError() def visit_starred_pattern(self, o: mypy.patterns.StarredPattern, /) -> T: - pass + raise NotImplementedError() def visit_mapping_pattern(self, o: mypy.patterns.MappingPattern, /) -> T: - pass + raise NotImplementedError() def visit_class_pattern(self, o: mypy.patterns.ClassPattern, /) -> T: - pass + raise NotImplementedError() diff --git a/mypy_self_check.ini b/mypy_self_check.ini index f54c1f17f0254..7198a1f6f342d 100644 --- a/mypy_self_check.ini +++ b/mypy_self_check.ini @@ -12,7 +12,3 @@ exclude = mypy/typeshed/|mypyc/test-data/|mypyc/lib-rt/ enable_error_code = ignore-without-code,redundant-expr enable_incomplete_feature = PreciseTupleTypes show_error_code_links = True - -[mypy-mypy.visitor] -# See docstring for NodeVisitor for motivation. -disable_error_code = empty-body