(builder: IRBuilder, expr: Expression)
| 1280 | |
| 1281 | |
| 1282 | def get_expr_length(builder: IRBuilder, expr: Expression) -> int | None: |
| 1283 | folded = constant_fold_expr(builder, expr) |
| 1284 | if isinstance(folded, (str, bytes)): |
| 1285 | return len(folded) |
| 1286 | elif isinstance(expr, (ListExpr, TupleExpr)): |
| 1287 | # if there are no star expressions, or we know the length of them, |
| 1288 | # we know the length of the expression |
| 1289 | stars = [get_expr_length(builder, i) for i in expr.items if isinstance(i, StarExpr)] |
| 1290 | if None not in stars: |
| 1291 | other = sum(not isinstance(i, StarExpr) for i in expr.items) |
| 1292 | return other + sum(stars) # type: ignore [arg-type] |
| 1293 | elif isinstance(expr, StarExpr): |
| 1294 | return get_expr_length(builder, expr.expr) |
| 1295 | elif ( |
| 1296 | isinstance(expr, RefExpr) |
| 1297 | and isinstance(expr.node, Var) |
| 1298 | and expr.node.is_final |
| 1299 | and isinstance(expr.node.final_value, str) |
| 1300 | and expr.node.has_explicit_value |
| 1301 | ): |
| 1302 | return len(expr.node.final_value) |
| 1303 | elif ( |
| 1304 | isinstance(expr, CallExpr) |
| 1305 | and isinstance(callee := expr.callee, NameExpr) |
| 1306 | and all(kind == ARG_POS for kind in expr.arg_kinds) |
| 1307 | ): |
| 1308 | fullname = callee.fullname |
| 1309 | if ( |
| 1310 | fullname |
| 1311 | in ( |
| 1312 | "builtins.list", |
| 1313 | "builtins.tuple", |
| 1314 | "builtins.enumerate", |
| 1315 | "builtins.sorted", |
| 1316 | "builtins.reversed", |
| 1317 | ) |
| 1318 | and len(expr.args) == 1 |
| 1319 | ): |
| 1320 | return get_expr_length(builder, expr.args[0]) |
| 1321 | elif fullname == "builtins.map" and len(expr.args) == 2: |
| 1322 | return get_expr_length(builder, expr.args[1]) |
| 1323 | elif fullname == "builtins.zip" and expr.args: |
| 1324 | arg_lengths = [get_expr_length(builder, arg) for arg in expr.args] |
| 1325 | if all(arg is not None for arg in arg_lengths): |
| 1326 | return min(arg_lengths) # type: ignore [type-var] |
| 1327 | elif fullname == "builtins.range" and len(expr.args) <= 3: |
| 1328 | folded_args = [constant_fold_expr(builder, arg) for arg in expr.args] |
| 1329 | if all(isinstance(arg, int) for arg in folded_args): |
| 1330 | try: |
| 1331 | return len(range(*cast(list[int], folded_args))) |
| 1332 | except ValueError: # prevent crash if invalid args |
| 1333 | pass |
| 1334 | |
| 1335 | # TODO: extend this, passing length of listcomp and genexp should have worthwhile |
| 1336 | # performance boost and can be (sometimes) figured out pretty easily. set and dict |
| 1337 | # comps *can* be done as well but will need special logic to consider the possibility |
| 1338 | # of key conflicts. |
| 1339 |
no test coverage detected
searching dependent graphs…