80
80
CallExpr ,
81
81
ClassDef ,
82
82
ComparisonExpr ,
83
+ ComplexExpr ,
83
84
Decorator ,
84
85
DictExpr ,
85
86
EllipsisExpr ,
@@ -1396,6 +1397,8 @@ def is_private_member(self, fullname: str) -> bool:
1396
1397
def get_str_type_of_node (
1397
1398
self , rvalue : Expression , can_infer_optional : bool = False , can_be_any : bool = True
1398
1399
) -> str :
1400
+ rvalue = self .maybe_unwrap_unary_expr (rvalue )
1401
+
1399
1402
if isinstance (rvalue , IntExpr ):
1400
1403
return "int"
1401
1404
if isinstance (rvalue , StrExpr ):
@@ -1404,8 +1407,13 @@ def get_str_type_of_node(
1404
1407
return "bytes"
1405
1408
if isinstance (rvalue , FloatExpr ):
1406
1409
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"
1409
1417
if isinstance (rvalue , NameExpr ) and rvalue .name in ("True" , "False" ):
1410
1418
return "bool"
1411
1419
if can_infer_optional and isinstance (rvalue , NameExpr ) and rvalue .name == "None" :
@@ -1417,6 +1425,40 @@ def get_str_type_of_node(
1417
1425
else :
1418
1426
return ""
1419
1427
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
+
1420
1462
def print_annotation (self , t : Type ) -> str :
1421
1463
printer = AnnotationPrinter (self )
1422
1464
return t .accept (printer )
0 commit comments