@@ -1750,6 +1750,34 @@ fn infer_type_from_expr_usage(param_name: &str, expr: &HirExpr) -> Option<Type>
17501750 }
17511751 }
17521752 }
1753+
1754+ // DEPYLER-0554: datetime.datetime.fromtimestamp(param) → param is f64
1755+ // datetime.datetime.now() doesn't have param, but fromtimestamp does
1756+ if module_name == "datetime" && method == "fromtimestamp" {
1757+ if let Some ( first_arg) = args. first ( ) {
1758+ if let HirExpr :: Var ( var_name) = first_arg {
1759+ if var_name == param_name {
1760+ return Some ( Type :: Float ) ;
1761+ }
1762+ }
1763+ }
1764+ }
1765+ }
1766+
1767+ // DEPYLER-0554: Handle datetime.datetime attribute access → fromtimestamp method
1768+ // Pattern: datetime.datetime.fromtimestamp(timestamp) where datetime.datetime is the object
1769+ if let HirExpr :: Attribute { value, attr } = object. as_ref ( ) {
1770+ if let HirExpr :: Var ( module_name) = value. as_ref ( ) {
1771+ if module_name == "datetime" && attr == "datetime" && method == "fromtimestamp" {
1772+ if let Some ( first_arg) = args. first ( ) {
1773+ if let HirExpr :: Var ( var_name) = first_arg {
1774+ if var_name == param_name {
1775+ return Some ( Type :: Float ) ;
1776+ }
1777+ }
1778+ }
1779+ }
1780+ }
17531781 }
17541782
17551783 // Methods that expect string arguments (for method calls on objects)
@@ -1841,11 +1869,39 @@ fn infer_type_from_expr_usage(param_name: &str, expr: &HirExpr) -> Option<Type>
18411869 HirExpr :: Binary { left, right, op, .. } => {
18421870 use crate :: hir:: BinOp ;
18431871
1872+ // DEPYLER-0554: Pattern: param == "literal" or param != "literal" → param is String
1873+ // Example: if algorithm == "md5": → algorithm must be String/&str
1874+ if matches ! ( op, BinOp :: Eq | BinOp :: NotEq ) {
1875+ // Check if param is compared to a string literal
1876+ if let HirExpr :: Var ( var_name) = left. as_ref ( ) {
1877+ if var_name == param_name {
1878+ if matches ! ( right. as_ref( ) , HirExpr :: Literal ( crate :: hir:: Literal :: String ( _) ) ) {
1879+ return Some ( Type :: String ) ;
1880+ }
1881+ }
1882+ }
1883+ // Also check the reverse: "literal" == param
1884+ if let HirExpr :: Var ( var_name) = right. as_ref ( ) {
1885+ if var_name == param_name {
1886+ if matches ! ( left. as_ref( ) , HirExpr :: Literal ( crate :: hir:: Literal :: String ( _) ) ) {
1887+ return Some ( Type :: String ) ;
1888+ }
1889+ }
1890+ }
1891+ }
1892+
18441893 // DEPYLER-0524: Pattern: param in string → param is String (substring check)
18451894 // Example: if pattern in line: → pattern must be String for .contains()
1846- if matches ! ( op, BinOp :: In ) {
1895+ // DEPYLER-0554: Pattern: param in ["a", "b"] or param not in [...] → param is String
1896+ if matches ! ( op, BinOp :: In | BinOp :: NotIn ) {
18471897 if let HirExpr :: Var ( var_name) = left. as_ref ( ) {
18481898 if var_name == param_name {
1899+ // Check if right side is a list of strings
1900+ if let HirExpr :: List ( elements) = right. as_ref ( ) {
1901+ if elements. iter ( ) . all ( |e| matches ! ( e, HirExpr :: Literal ( crate :: hir:: Literal :: String ( _) ) ) ) {
1902+ return Some ( Type :: String ) ;
1903+ }
1904+ }
18491905 // In Python, "x in y" where y is string → x is also string
18501906 return Some ( Type :: String ) ;
18511907 }
0 commit comments