Use "manual" fixability for E731 in shadowed context (#5430)

## Summary

This PR makes E731 a "manual" fix in one other context: when the lambda
is shadowing another variable in the scope. Function declarations (with
shadowing) cause issues for type checkers, and so rewriting an
annotation, e.g., in branches of an `if` statement can lead to failures.

Closes https://github.com/astral-sh/ruff/issues/5421.
This commit is contained in:
Charlie Marsh 2023-06-28 22:00:06 -04:00 committed by GitHub
parent 72f7f11bac
commit aa887d5a1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 434 additions and 300 deletions

View File

@ -1,51 +1,135 @@
#: E731
f = lambda x: 2 * x
#: E731
f = lambda x: 2 * x
#: E731
while False:
this = lambda y, z: 2 * x
#: E731
f = lambda: (yield 1)
#: E731
f = lambda: (yield from g())
#: E731
class F:
def scope():
# E731
f = lambda x: 2 * x
f = object()
f.method = lambda: "Method"
f = {}
f["a"] = lambda x: x**2
f = []
f.append(lambda x: x**2)
f = g = lambda x: x**2
lambda: "no-op"
def scope():
# E731
f = lambda x: 2 * x
# Annotated
from typing import Callable, ParamSpec
P = ParamSpec("P")
def scope():
# E731
while False:
this = lambda y, z: 2 * x
def scope():
# E731
f = lambda: (yield 1)
def scope():
# E731
f = lambda: (yield from g())
def scope():
# OK
f = object()
f.method = lambda: "Method"
def scope():
# OK
f = {}
f["a"] = lambda x: x**2
def scope():
# OK
f = []
f.append(lambda x: x**2)
def scope():
# OK
f = g = lambda x: x**2
def scope():
# OK
lambda: "no-op"
class Scope:
# E731
f = lambda x: 2 * x
class Scope:
from typing import Callable
# E731
f: Callable[[int], int] = lambda x: 2 * x
def scope():
# E731
from typing import Callable
x: Callable[[int], int]
if True:
x = lambda: 1
else:
x = lambda: 2
return x
def scope():
# E731
from typing import Callable, ParamSpec
# ParamSpec cannot be used in this context, so do not preserve the annotation.
P = ParamSpec("P")
f: Callable[P, int] = lambda *args: len(args)
def scope():
# E731
from typing import Callable
f: Callable[[], None] = lambda: None
def scope():
# E731
from typing import Callable
f: Callable[..., None] = lambda a, b: None
def scope():
# E731
from typing import Callable
f: Callable[[int], int] = lambda x: 2 * x
# ParamSpec cannot be used in this context, so do not preserve the annotation.
f: Callable[P, int] = lambda *args: len(args)
f: Callable[[], None] = lambda: None
f: Callable[..., None] = lambda a, b: None
f: Callable[[int], int] = lambda x: 2 * x
# Let's use the `Callable` type from `collections.abc` instead.
from collections.abc import Callable
def scope():
# E731
f: Callable[[str, int], str] = lambda a, b: a * b
f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b)
f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b]
from collections.abc import Callable
f: Callable[[str, int], str] = lambda a, b: a * b
# Override `Callable`
class Callable:
pass
def scope():
# E731
from collections.abc import Callable
f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b)
# Do not copy the annotation from here on out.
f: Callable[[str, int], str] = lambda a, b: a * b
def scope():
# E731
from collections.abc import Callable
f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b]

View File

@ -63,8 +63,14 @@ pub(crate) fn lambda_assignment(
annotation: Option<&Expr>,
stmt: &Stmt,
) {
if let Expr::Name(ast::ExprName { id, .. }) = target {
if let Expr::Lambda(ast::ExprLambda { args, body, .. }) = value {
let Expr::Name(ast::ExprName { id, .. }) = target else {
return;
};
let Expr::Lambda(ast::ExprLambda { args, body, .. }) = value else {
return;
};
let mut diagnostic = Diagnostic::new(
LambdaAssignment {
name: id.to_string(),
@ -72,15 +78,8 @@ pub(crate) fn lambda_assignment(
stmt.range(),
);
// If the assignment is in a class body, it might not be safe
// to replace it because the assignment might be
// carrying a type annotation that will be used by some
// package like dataclasses, which wouldn't consider the
// rewritten function definition to be equivalent.
// See https://github.com/astral-sh/ruff/issues/3046
if checker.patch(diagnostic.kind.rule())
&& !checker.semantic().scope().kind.is_class()
&& !has_leading_content(stmt.start(), checker.locator)
if checker.patch(diagnostic.kind.rule()) {
if !has_leading_content(stmt.start(), checker.locator)
&& !has_trailing_content(stmt.end(), checker.locator)
{
let first_line = checker.locator.line(stmt.start());
@ -105,15 +104,28 @@ pub(crate) fn lambda_assignment(
indented.push_str(&line);
}
}
// If the assignment is in a class body, it might not be safe to replace it because the
// assignment might be carrying a type annotation that will be used by some package like
// dataclasses, which wouldn't consider the rewritten function definition to be
// equivalent. Similarly, if the lambda is shadowing a variable in the current scope,
// rewriting it as a function declaration may break type-checking.
// See: https://github.com/astral-sh/ruff/issues/3046
// See: https://github.com/astral-sh/ruff/issues/5421
if (annotation.is_some() && checker.semantic().scope().kind.is_class())
|| checker.semantic().scope().has(id)
{
diagnostic.set_fix(Fix::manual(Edit::range_replacement(indented, stmt.range())));
} else {
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
indented,
stmt.range(),
)));
}
}
}
checker.diagnostics.push(diagnostic);
}
}
}
/// Extract the argument types and return type from a `Callable` annotation.

View File

@ -1,284 +1,322 @@
---
source: crates/ruff/src/rules/pycodestyle/mod.rs
---
E731.py:2:1: E731 [*] Do not assign a `lambda` expression, use a `def`
E731.py:3:5: E731 [*] Do not assign a `lambda` expression, use a `def`
|
1 | #: E731
2 | f = lambda x: 2 * x
1 | def scope():
2 | # E731
3 | f = lambda x: 2 * x
| ^^^^^^^^^^^^^^^^^^^ E731
3 | #: E731
4 | f = lambda x: 2 * x
|
= help: Rewrite `f` as a `def`
Suggested fix
1 1 | #: E731
2 |-f = lambda x: 2 * x
2 |+def f(x):
3 |+ return 2 * x
3 4 | #: E731
4 5 | f = lambda x: 2 * x
5 6 | #: E731
1 1 | def scope():
2 2 | # E731
3 |- f = lambda x: 2 * x
3 |+ def f(x):
4 |+ return 2 * x
4 5 |
5 6 |
6 7 | def scope():
E731.py:4:1: E731 [*] Do not assign a `lambda` expression, use a `def`
E731.py:8:5: E731 [*] Do not assign a `lambda` expression, use a `def`
|
2 | f = lambda x: 2 * x
3 | #: E731
4 | f = lambda x: 2 * x
6 | def scope():
7 | # E731
8 | f = lambda x: 2 * x
| ^^^^^^^^^^^^^^^^^^^ E731
5 | #: E731
6 | while False:
|
= help: Rewrite `f` as a `def`
Suggested fix
1 1 | #: E731
2 2 | f = lambda x: 2 * x
3 3 | #: E731
4 |-f = lambda x: 2 * x
4 |+def f(x):
5 |+ return 2 * x
5 6 | #: E731
6 7 | while False:
7 8 | this = lambda y, z: 2 * x
5 5 |
6 6 | def scope():
7 7 | # E731
8 |- f = lambda x: 2 * x
8 |+ def f(x):
9 |+ return 2 * x
9 10 |
10 11 |
11 12 | def scope():
E731.py:7:5: E731 [*] Do not assign a `lambda` expression, use a `def`
E731.py:14:9: E731 [*] Do not assign a `lambda` expression, use a `def`
|
5 | #: E731
6 | while False:
7 | this = lambda y, z: 2 * x
12 | # E731
13 | while False:
14 | this = lambda y, z: 2 * x
| ^^^^^^^^^^^^^^^^^^^^^^^^^ E731
8 | #: E731
9 | f = lambda: (yield 1)
|
= help: Rewrite `this` as a `def`
Suggested fix
4 4 | f = lambda x: 2 * x
5 5 | #: E731
6 6 | while False:
7 |- this = lambda y, z: 2 * x
7 |+ def this(y, z):
8 |+ return 2 * x
8 9 | #: E731
9 10 | f = lambda: (yield 1)
10 11 | #: E731
11 11 | def scope():
12 12 | # E731
13 13 | while False:
14 |- this = lambda y, z: 2 * x
14 |+ def this(y, z):
15 |+ return 2 * x
15 16 |
16 17 |
17 18 | def scope():
E731.py:9:1: E731 [*] Do not assign a `lambda` expression, use a `def`
E731.py:19:5: E731 [*] Do not assign a `lambda` expression, use a `def`
|
7 | this = lambda y, z: 2 * x
8 | #: E731
9 | f = lambda: (yield 1)
17 | def scope():
18 | # E731
19 | f = lambda: (yield 1)
| ^^^^^^^^^^^^^^^^^^^^^ E731
10 | #: E731
11 | f = lambda: (yield from g())
|
= help: Rewrite `f` as a `def`
Suggested fix
6 6 | while False:
7 7 | this = lambda y, z: 2 * x
8 8 | #: E731
9 |-f = lambda: (yield 1)
9 |+def f():
10 |+ return (yield 1)
10 11 | #: E731
11 12 | f = lambda: (yield from g())
12 13 | #: E731
16 16 |
17 17 | def scope():
18 18 | # E731
19 |- f = lambda: (yield 1)
19 |+ def f():
20 |+ return (yield 1)
20 21 |
21 22 |
22 23 | def scope():
E731.py:11:1: E731 [*] Do not assign a `lambda` expression, use a `def`
E731.py:24:5: E731 [*] Do not assign a `lambda` expression, use a `def`
|
9 | f = lambda: (yield 1)
10 | #: E731
11 | f = lambda: (yield from g())
22 | def scope():
23 | # E731
24 | f = lambda: (yield from g())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731
12 | #: E731
13 | class F:
|
= help: Rewrite `f` as a `def`
Suggested fix
8 8 | #: E731
9 9 | f = lambda: (yield 1)
10 10 | #: E731
11 |-f = lambda: (yield from g())
11 |+def f():
12 |+ return (yield from g())
12 13 | #: E731
13 14 | class F:
14 15 | f = lambda x: 2 * x
21 21 |
22 22 | def scope():
23 23 | # E731
24 |- f = lambda: (yield from g())
24 |+ def f():
25 |+ return (yield from g())
25 26 |
26 27 |
27 28 | def scope():
E731.py:14:5: E731 Do not assign a `lambda` expression, use a `def`
E731.py:57:5: E731 [*] Do not assign a `lambda` expression, use a `def`
|
12 | #: E731
13 | class F:
14 | f = lambda x: 2 * x
55 | class Scope:
56 | # E731
57 | f = lambda x: 2 * x
| ^^^^^^^^^^^^^^^^^^^ E731
|
= help: Rewrite `f` as a `def`
E731.py:32:1: E731 [*] Do not assign a `lambda` expression, use a `def`
|
31 | # ParamSpec cannot be used in this context, so do not preserve the annotation.
32 | f: Callable[P, int] = lambda *args: len(args)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731
33 | f: Callable[[], None] = lambda: None
34 | f: Callable[..., None] = lambda a, b: None
|
= help: Rewrite `f` as a `def`
Suggested fix
29 29 | P = ParamSpec("P")
30 30 |
31 31 | # ParamSpec cannot be used in this context, so do not preserve the annotation.
32 |-f: Callable[P, int] = lambda *args: len(args)
32 |+def f(*args):
33 |+ return len(args)
33 34 | f: Callable[[], None] = lambda: None
34 35 | f: Callable[..., None] = lambda a, b: None
35 36 | f: Callable[[int], int] = lambda x: 2 * x
54 54 |
55 55 | class Scope:
56 56 | # E731
57 |- f = lambda x: 2 * x
57 |+ def f(x):
58 |+ return 2 * x
58 59 |
59 60 |
60 61 | class Scope:
E731.py:33:1: E731 [*] Do not assign a `lambda` expression, use a `def`
E731.py:64:5: E731 [*] Do not assign a `lambda` expression, use a `def`
|
31 | # ParamSpec cannot be used in this context, so do not preserve the annotation.
32 | f: Callable[P, int] = lambda *args: len(args)
33 | f: Callable[[], None] = lambda: None
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731
34 | f: Callable[..., None] = lambda a, b: None
35 | f: Callable[[int], int] = lambda x: 2 * x
|
= help: Rewrite `f` as a `def`
Suggested fix
30 30 |
31 31 | # ParamSpec cannot be used in this context, so do not preserve the annotation.
32 32 | f: Callable[P, int] = lambda *args: len(args)
33 |-f: Callable[[], None] = lambda: None
33 |+def f() -> None:
34 |+ return None
34 35 | f: Callable[..., None] = lambda a, b: None
35 36 | f: Callable[[int], int] = lambda x: 2 * x
36 37 |
E731.py:34:1: E731 [*] Do not assign a `lambda` expression, use a `def`
|
32 | f: Callable[P, int] = lambda *args: len(args)
33 | f: Callable[[], None] = lambda: None
34 | f: Callable[..., None] = lambda a, b: None
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731
35 | f: Callable[[int], int] = lambda x: 2 * x
|
= help: Rewrite `f` as a `def`
Suggested fix
31 31 | # ParamSpec cannot be used in this context, so do not preserve the annotation.
32 32 | f: Callable[P, int] = lambda *args: len(args)
33 33 | f: Callable[[], None] = lambda: None
34 |-f: Callable[..., None] = lambda a, b: None
34 |+def f(a, b) -> None:
35 |+ return None
35 36 | f: Callable[[int], int] = lambda x: 2 * x
36 37 |
37 38 | # Let's use the `Callable` type from `collections.abc` instead.
E731.py:35:1: E731 [*] Do not assign a `lambda` expression, use a `def`
|
33 | f: Callable[[], None] = lambda: None
34 | f: Callable[..., None] = lambda a, b: None
35 | f: Callable[[int], int] = lambda x: 2 * x
63 | # E731
64 | f: Callable[[int], int] = lambda x: 2 * x
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731
36 |
37 | # Let's use the `Callable` type from `collections.abc` instead.
|
= help: Rewrite `f` as a `def`
Possible fix
61 61 | from typing import Callable
62 62 |
63 63 | # E731
64 |- f: Callable[[int], int] = lambda x: 2 * x
64 |+ def f(x: int) -> int:
65 |+ return 2 * x
65 66 |
66 67 |
67 68 | def scope():
E731.py:73:9: E731 [*] Do not assign a `lambda` expression, use a `def`
|
71 | x: Callable[[int], int]
72 | if True:
73 | x = lambda: 1
| ^^^^^^^^^^^^^ E731
74 | else:
75 | x = lambda: 2
|
= help: Rewrite `x` as a `def`
Possible fix
70 70 |
71 71 | x: Callable[[int], int]
72 72 | if True:
73 |- x = lambda: 1
73 |+ def x():
74 |+ return 1
74 75 | else:
75 76 | x = lambda: 2
76 77 | return x
E731.py:75:9: E731 [*] Do not assign a `lambda` expression, use a `def`
|
73 | x = lambda: 1
74 | else:
75 | x = lambda: 2
| ^^^^^^^^^^^^^ E731
76 | return x
|
= help: Rewrite `x` as a `def`
Possible fix
72 72 | if True:
73 73 | x = lambda: 1
74 74 | else:
75 |- x = lambda: 2
75 |+ def x():
76 |+ return 2
76 77 | return x
77 78 |
78 79 |
E731.py:86:5: E731 [*] Do not assign a `lambda` expression, use a `def`
|
84 | # ParamSpec cannot be used in this context, so do not preserve the annotation.
85 | P = ParamSpec("P")
86 | f: Callable[P, int] = lambda *args: len(args)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731
|
= help: Rewrite `f` as a `def`
Suggested fix
32 32 | f: Callable[P, int] = lambda *args: len(args)
33 33 | f: Callable[[], None] = lambda: None
34 34 | f: Callable[..., None] = lambda a, b: None
35 |-f: Callable[[int], int] = lambda x: 2 * x
35 |+def f(x: int) -> int:
36 |+ return 2 * x
36 37 |
37 38 | # Let's use the `Callable` type from `collections.abc` instead.
38 39 | from collections.abc import Callable
83 83 |
84 84 | # ParamSpec cannot be used in this context, so do not preserve the annotation.
85 85 | P = ParamSpec("P")
86 |- f: Callable[P, int] = lambda *args: len(args)
86 |+ def f(*args):
87 |+ return len(args)
87 88 |
88 89 |
89 90 | def scope():
E731.py:40:1: E731 [*] Do not assign a `lambda` expression, use a `def`
E731.py:94:5: E731 [*] Do not assign a `lambda` expression, use a `def`
|
38 | from collections.abc import Callable
39 |
40 | f: Callable[[str, int], str] = lambda a, b: a * b
92 | from typing import Callable
93 |
94 | f: Callable[[], None] = lambda: None
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731
|
= help: Rewrite `f` as a `def`
Suggested fix
91 91 |
92 92 | from typing import Callable
93 93 |
94 |- f: Callable[[], None] = lambda: None
94 |+ def f() -> None:
95 |+ return None
95 96 |
96 97 |
97 98 | def scope():
E731.py:102:5: E731 [*] Do not assign a `lambda` expression, use a `def`
|
100 | from typing import Callable
101 |
102 | f: Callable[..., None] = lambda a, b: None
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731
|
= help: Rewrite `f` as a `def`
Suggested fix
99 99 |
100 100 | from typing import Callable
101 101 |
102 |- f: Callable[..., None] = lambda a, b: None
102 |+ def f(a, b) -> None:
103 |+ return None
103 104 |
104 105 |
105 106 | def scope():
E731.py:110:5: E731 [*] Do not assign a `lambda` expression, use a `def`
|
108 | from typing import Callable
109 |
110 | f: Callable[[int], int] = lambda x: 2 * x
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731
|
= help: Rewrite `f` as a `def`
Suggested fix
107 107 |
108 108 | from typing import Callable
109 109 |
110 |- f: Callable[[int], int] = lambda x: 2 * x
110 |+ def f(x: int) -> int:
111 |+ return 2 * x
111 112 |
112 113 |
113 114 | # Let's use the `Callable` type from `collections.abc` instead.
E731.py:119:5: E731 [*] Do not assign a `lambda` expression, use a `def`
|
117 | from collections.abc import Callable
118 |
119 | f: Callable[[str, int], str] = lambda a, b: a * b
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731
41 | f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b)
42 | f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b]
|
= help: Rewrite `f` as a `def`
Suggested fix
37 37 | # Let's use the `Callable` type from `collections.abc` instead.
38 38 | from collections.abc import Callable
39 39 |
40 |-f: Callable[[str, int], str] = lambda a, b: a * b
40 |+def f(a: str, b: int) -> str:
41 |+ return a * b
41 42 | f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b)
42 43 | f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b]
43 44 |
116 116 |
117 117 | from collections.abc import Callable
118 118 |
119 |- f: Callable[[str, int], str] = lambda a, b: a * b
119 |+ def f(a: str, b: int) -> str:
120 |+ return a * b
120 121 |
121 122 |
122 123 | def scope():
E731.py:41:1: E731 [*] Do not assign a `lambda` expression, use a `def`
E731.py:127:5: E731 [*] Do not assign a `lambda` expression, use a `def`
|
40 | f: Callable[[str, int], str] = lambda a, b: a * b
41 | f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b)
125 | from collections.abc import Callable
126 |
127 | f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731
42 | f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b]
|
= help: Rewrite `f` as a `def`
Suggested fix
38 38 | from collections.abc import Callable
39 39 |
40 40 | f: Callable[[str, int], str] = lambda a, b: a * b
41 |-f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b)
41 |+def f(a: str, b: int) -> tuple[str, int]:
42 |+ return a, b
42 43 | f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b]
43 44 |
44 45 |
124 124 |
125 125 | from collections.abc import Callable
126 126 |
127 |- f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b)
127 |+ def f(a: str, b: int) -> tuple[str, int]:
128 |+ return a, b
128 129 |
129 130 |
130 131 | def scope():
E731.py:42:1: E731 [*] Do not assign a `lambda` expression, use a `def`
E731.py:135:5: E731 [*] Do not assign a `lambda` expression, use a `def`
|
40 | f: Callable[[str, int], str] = lambda a, b: a * b
41 | f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b)
42 | f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b]
133 | from collections.abc import Callable
134 |
135 | f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731
|
= help: Rewrite `f` as a `def`
Suggested fix
39 39 |
40 40 | f: Callable[[str, int], str] = lambda a, b: a * b
41 41 | f: Callable[[str, int], tuple[str, int]] = lambda a, b: (a, b)
42 |-f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b]
42 |+def f(a: str, b: int, /, c: list[str]) -> list[str]:
43 |+ return [*c, a * b]
43 44 |
44 45 |
45 46 | # Override `Callable`
E731.py:51:1: E731 [*] Do not assign a `lambda` expression, use a `def`
|
50 | # Do not copy the annotation from here on out.
51 | f: Callable[[str, int], str] = lambda a, b: a * b
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ E731
|
= help: Rewrite `f` as a `def`
Suggested fix
48 48 |
49 49 |
50 50 | # Do not copy the annotation from here on out.
51 |-f: Callable[[str, int], str] = lambda a, b: a * b
51 |+def f(a, b):
52 |+ return a * b
132 132 |
133 133 | from collections.abc import Callable
134 134 |
135 |- f: Callable[[str, int, list[str]], list[str]] = lambda a, b, /, c: [*c, a * b]
135 |+ def f(a: str, b: int, /, c: list[str]) -> list[str]:
136 |+ return [*c, a * b]