8080 CallExpr ,
8181 ClassDef ,
8282 ComparisonExpr ,
83+ ComplexExpr ,
8384 Decorator ,
8485 DictExpr ,
8586 EllipsisExpr ,
@@ -1396,6 +1397,8 @@ def is_private_member(self, fullname: str) -> bool:
13961397 def get_str_type_of_node (
13971398 self , rvalue : Expression , can_infer_optional : bool = False , can_be_any : bool = True
13981399 ) -> str :
1400+ rvalue = self .maybe_unwrap_unary_expr (rvalue )
1401+
13991402 if isinstance (rvalue , IntExpr ):
14001403 return "int"
14011404 if isinstance (rvalue , StrExpr ):
@@ -1404,8 +1407,13 @@ def get_str_type_of_node(
14041407 return "bytes"
14051408 if isinstance (rvalue , FloatExpr ):
14061409 return "float"
1407- if isinstance (rvalue , UnaryExpr ) and isinstance (rvalue .expr , IntExpr ):
1408- return "int"
1410+ if isinstance (rvalue , ComplexExpr ): # 1j
1411+ return "complex"
1412+ if isinstance (rvalue , OpExpr ) and rvalue .op in ("-" , "+" ): # -1j + 1
1413+ if isinstance (self .maybe_unwrap_unary_expr (rvalue .left ), ComplexExpr ) or isinstance (
1414+ self .maybe_unwrap_unary_expr (rvalue .right ), ComplexExpr
1415+ ):
1416+ return "complex"
14091417 if isinstance (rvalue , NameExpr ) and rvalue .name in ("True" , "False" ):
14101418 return "bool"
14111419 if can_infer_optional and isinstance (rvalue , NameExpr ) and rvalue .name == "None" :
@@ -1417,6 +1425,40 @@ def get_str_type_of_node(
14171425 else :
14181426 return ""
14191427
1428+ def maybe_unwrap_unary_expr (self , expr : Expression ) -> Expression :
1429+ """Unwrap (possibly nested) unary expressions.
1430+
1431+ But, some unary expressions can change the type of expression.
1432+ While we want to preserve it. For example, `~True` is `int`.
1433+ So, we only allow a subset of unary expressions to be unwrapped.
1434+ """
1435+ if not isinstance (expr , UnaryExpr ):
1436+ return expr
1437+
1438+ # First, try to unwrap `[+-]+ (int|float|complex)` expr:
1439+ math_ops = ("+" , "-" )
1440+ if expr .op in math_ops :
1441+ while isinstance (expr , UnaryExpr ):
1442+ if expr .op not in math_ops or not isinstance (
1443+ expr .expr , (IntExpr , FloatExpr , ComplexExpr , UnaryExpr )
1444+ ):
1445+ break
1446+ expr = expr .expr
1447+ return expr
1448+
1449+ # Next, try `not bool` expr:
1450+ if expr .op == "not" :
1451+ while isinstance (expr , UnaryExpr ):
1452+ if expr .op != "not" or not isinstance (expr .expr , (NameExpr , UnaryExpr )):
1453+ break
1454+ if isinstance (expr .expr , NameExpr ) and expr .expr .name not in ("True" , "False" ):
1455+ break
1456+ expr = expr .expr
1457+ return expr
1458+
1459+ # This is some other unary expr, we cannot do anything with it (yet?).
1460+ return expr
1461+
14201462 def print_annotation (self , t : Type ) -> str :
14211463 printer = AnnotationPrinter (self )
14221464 return t .accept (printer )
0 commit comments