@@ -1552,17 +1552,12 @@ def configure_base_classes(self,
15521552 elif isinstance (base , Instance ):
15531553 if base .type .is_newtype :
15541554 self .fail ('Cannot subclass "NewType"' , defn )
1555- if (
1556- base .type .is_enum
1557- and base .type .fullname not in ENUM_BASES
1558- and base .type .names
1559- and any (not isinstance (n .node , (FuncBase , Decorator ))
1560- for n in base .type .names .values ())
1561- ):
1555+ if self .enum_has_final_values (base ):
15621556 # This means that are trying to subclass a non-default
15631557 # Enum class, with defined members. This is not possible.
15641558 # In runtime, it will raise. We need to mark this type as final.
15651559 # However, methods can be defined on a type: only values can't.
1560+ # We also don't count values with annotations only.
15661561 base .type .is_final = True
15671562 base_types .append (base )
15681563 elif isinstance (base , AnyType ):
@@ -1601,6 +1596,25 @@ def configure_base_classes(self,
16011596 return
16021597 self .calculate_class_mro (defn , self .object_type )
16031598
1599+ def enum_has_final_values (self , base : Instance ) -> bool :
1600+ if (
1601+ base .type .is_enum
1602+ and base .type .fullname not in ENUM_BASES
1603+ and base .type .names
1604+ and base .type .defn
1605+ ):
1606+ for sym in base .type .names .values ():
1607+ if isinstance (sym .node , (FuncBase , Decorator )):
1608+ continue # A method
1609+ if not isinstance (sym .node , Var ):
1610+ return True # Can be a class
1611+ if self .is_stub_file or sym .node .has_explicit_value :
1612+ # Corner case: assignments like `x: int` are fine in `.py` files.
1613+ # But, not is `.pyi` files, because we don't know
1614+ # if there's aactually a value or not.
1615+ return True
1616+ return False
1617+
16041618 def configure_tuple_base_class (self ,
16051619 defn : ClassDef ,
16061620 base : TupleType ,
@@ -2040,7 +2054,7 @@ def visit_import_all(self, i: ImportAll) -> None:
20402054
20412055 def visit_assignment_expr (self , s : AssignmentExpr ) -> None :
20422056 s .value .accept (self )
2043- self .analyze_lvalue (s .target , escape_comprehensions = True )
2057+ self .analyze_lvalue (s .target , escape_comprehensions = True , has_explicit_value = True )
20442058
20452059 def visit_assignment_stmt (self , s : AssignmentStmt ) -> None :
20462060 self .statement = s
@@ -2333,10 +2347,20 @@ def analyze_lvalues(self, s: AssignmentStmt) -> None:
23332347 assert isinstance (s .unanalyzed_type , UnboundType )
23342348 if not s .unanalyzed_type .args :
23352349 explicit = False
2350+
2351+ if s .rvalue :
2352+ if isinstance (s .rvalue , TempNode ):
2353+ has_explicit_value = not s .rvalue .no_rhs
2354+ else :
2355+ has_explicit_value = True
2356+ else :
2357+ has_explicit_value = False
2358+
23362359 for lval in s .lvalues :
23372360 self .analyze_lvalue (lval ,
23382361 explicit_type = explicit ,
2339- is_final = s .is_final_def )
2362+ is_final = s .is_final_def ,
2363+ has_explicit_value = has_explicit_value )
23402364
23412365 def apply_dynamic_class_hook (self , s : AssignmentStmt ) -> None :
23422366 if not isinstance (s .rvalue , CallExpr ):
@@ -2776,7 +2800,8 @@ def analyze_lvalue(self,
27762800 nested : bool = False ,
27772801 explicit_type : bool = False ,
27782802 is_final : bool = False ,
2779- escape_comprehensions : bool = False ) -> None :
2803+ escape_comprehensions : bool = False ,
2804+ has_explicit_value : bool = False ) -> None :
27802805 """Analyze an lvalue or assignment target.
27812806
27822807 Args:
@@ -2790,7 +2815,11 @@ def analyze_lvalue(self,
27902815 if escape_comprehensions :
27912816 assert isinstance (lval , NameExpr ), "assignment expression target must be NameExpr"
27922817 if isinstance (lval , NameExpr ):
2793- self .analyze_name_lvalue (lval , explicit_type , is_final , escape_comprehensions )
2818+ self .analyze_name_lvalue (
2819+ lval , explicit_type , is_final ,
2820+ escape_comprehensions ,
2821+ has_explicit_value = has_explicit_value ,
2822+ )
27942823 elif isinstance (lval , MemberExpr ):
27952824 self .analyze_member_lvalue (lval , explicit_type , is_final )
27962825 if explicit_type and not self .is_self_member_ref (lval ):
@@ -2814,7 +2843,8 @@ def analyze_name_lvalue(self,
28142843 lvalue : NameExpr ,
28152844 explicit_type : bool ,
28162845 is_final : bool ,
2817- escape_comprehensions : bool ) -> None :
2846+ escape_comprehensions : bool ,
2847+ has_explicit_value : bool ) -> None :
28182848 """Analyze an lvalue that targets a name expression.
28192849
28202850 Arguments are similar to "analyze_lvalue".
@@ -2844,7 +2874,7 @@ def analyze_name_lvalue(self,
28442874
28452875 if (not existing or isinstance (existing .node , PlaceholderNode )) and not outer :
28462876 # Define new variable.
2847- var = self .make_name_lvalue_var (lvalue , kind , not explicit_type )
2877+ var = self .make_name_lvalue_var (lvalue , kind , not explicit_type , has_explicit_value )
28482878 added = self .add_symbol (name , var , lvalue , escape_comprehensions = escape_comprehensions )
28492879 # Only bind expression if we successfully added name to symbol table.
28502880 if added :
@@ -2895,7 +2925,9 @@ def is_alias_for_final_name(self, name: str) -> bool:
28952925 existing = self .globals .get (orig_name )
28962926 return existing is not None and is_final_node (existing .node )
28972927
2898- def make_name_lvalue_var (self , lvalue : NameExpr , kind : int , inferred : bool ) -> Var :
2928+ def make_name_lvalue_var (
2929+ self , lvalue : NameExpr , kind : int , inferred : bool , has_explicit_value : bool ,
2930+ ) -> Var :
28992931 """Return a Var node for an lvalue that is a name expression."""
29002932 v = Var (lvalue .name )
29012933 v .set_line (lvalue )
@@ -2910,6 +2942,7 @@ def make_name_lvalue_var(self, lvalue: NameExpr, kind: int, inferred: bool) -> V
29102942 # fullanme should never stay None
29112943 v ._fullname = lvalue .name
29122944 v .is_ready = False # Type not inferred yet
2945+ v .has_explicit_value = has_explicit_value
29132946 return v
29142947
29152948 def make_name_lvalue_point_to_existing_def (
@@ -2953,7 +2986,14 @@ def analyze_tuple_or_list_lvalue(self, lval: TupleExpr,
29532986 if len (star_exprs ) == 1 :
29542987 star_exprs [0 ].valid = True
29552988 for i in items :
2956- self .analyze_lvalue (i , nested = True , explicit_type = explicit_type )
2989+ self .analyze_lvalue (
2990+ lval = i ,
2991+ nested = True ,
2992+ explicit_type = explicit_type ,
2993+ # Lists and tuples always have explicit values defined:
2994+ # `a, b, c = value`
2995+ has_explicit_value = True ,
2996+ )
29572997
29582998 def analyze_member_lvalue (self , lval : MemberExpr , explicit_type : bool , is_final : bool ) -> None :
29592999 """Analyze lvalue that is a member expression.
0 commit comments