Consider single element subscript expr for implicit optional (#5717)

## Summary

Consider single element subscript expr for implicit optional.

On `main`, the cases where there is only a single element in the
subscript
list was giving false positives such as for the following:

```python
typing.Union[None]
typing.Literal[None]
```

## Test Plan

`cargo test`

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
This commit is contained in:
Dhruv Manilawala 2023-07-13 18:40:07 +05:30 committed by GitHub
parent f44acc047a
commit cf48ad7b21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 588 additions and 408 deletions

View File

@ -48,6 +48,10 @@ def f(arg: typing.Optional[int] = None):
# Union
def f(arg: Union[None] = None):
pass
def f(arg: Union[None, int] = None):
pass
@ -68,6 +72,10 @@ def f(arg: Union = None): # RUF013
pass
def f(arg: Union[int] = None): # RUF013
pass
def f(arg: Union[int, str] = None): # RUF013
pass
@ -106,10 +114,18 @@ def f(arg: None = None):
pass
def f(arg: Literal[None] = None):
pass
def f(arg: Literal[1, 2, None, 3] = None):
pass
def f(arg: Literal[1] = None): # RUF013
pass
def f(arg: Literal[1, "foo"] = None): # RUF013
pass

View File

@ -37,28 +37,46 @@ RUF013_0.py:25:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
27 27 |
28 28 |
RUF013_0.py:67:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:29:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
67 | def f(arg: Union = None): # RUF013
| ^^^^^ RUF013
68 | pass
29 | def f(arg: typing.List[str] = None): # RUF013
| ^^^^^^^^^^^^^^^^ RUF013
30 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
64 64 | pass
65 65 |
66 66 |
67 |-def f(arg: Union = None): # RUF013
67 |+def f(arg: Optional[Union] = None): # RUF013
68 68 | pass
69 69 |
70 70 |
26 26 | pass
27 27 |
28 28 |
29 |-def f(arg: typing.List[str] = None): # RUF013
29 |+def f(arg: Optional[typing.List[str]] = None): # RUF013
30 30 | pass
31 31 |
32 32 |
RUF013_0.py:33:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
33 | def f(arg: Tuple[str] = None): # RUF013
| ^^^^^^^^^^ RUF013
34 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
30 30 | pass
31 31 |
32 32 |
33 |-def f(arg: Tuple[str] = None): # RUF013
33 |+def f(arg: Optional[Tuple[str]] = None): # RUF013
34 34 | pass
35 35 |
36 36 |
RUF013_0.py:71:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
71 | def f(arg: Union[int, str] = None): # RUF013
| ^^^^^^^^^^^^^^^ RUF013
71 | def f(arg: Union = None): # RUF013
| ^^^^^ RUF013
72 | pass
|
= help: Convert to `Optional[T]`
@ -67,16 +85,16 @@ RUF013_0.py:71:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
68 68 | pass
69 69 |
70 70 |
71 |-def f(arg: Union[int, str] = None): # RUF013
71 |+def f(arg: Optional[Union[int, str]] = None): # RUF013
71 |-def f(arg: Union = None): # RUF013
71 |+def f(arg: Optional[Union] = None): # RUF013
72 72 | pass
73 73 |
74 74 |
RUF013_0.py:75:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
75 | def f(arg: typing.Union[int, str] = None): # RUF013
| ^^^^^^^^^^^^^^^^^^^^^^ RUF013
75 | def f(arg: Union[int] = None): # RUF013
| ^^^^^^^^^^ RUF013
76 | pass
|
= help: Convert to `Optional[T]`
@ -85,260 +103,314 @@ RUF013_0.py:75:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
72 72 | pass
73 73 |
74 74 |
75 |-def f(arg: typing.Union[int, str] = None): # RUF013
75 |+def f(arg: Optional[typing.Union[int, str]] = None): # RUF013
75 |-def f(arg: Union[int] = None): # RUF013
75 |+def f(arg: Optional[Union[int]] = None): # RUF013
76 76 | pass
77 77 |
78 78 |
RUF013_0.py:94:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:79:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
94 | def f(arg: int | float = None): # RUF013
| ^^^^^^^^^^^ RUF013
95 | pass
79 | def f(arg: Union[int, str] = None): # RUF013
| ^^^^^^^^^^^^^^^ RUF013
80 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
91 91 | pass
92 92 |
93 93 |
94 |-def f(arg: int | float = None): # RUF013
94 |+def f(arg: Optional[int | float] = None): # RUF013
95 95 | pass
96 96 |
97 97 |
76 76 | pass
77 77 |
78 78 |
79 |-def f(arg: Union[int, str] = None): # RUF013
79 |+def f(arg: Optional[Union[int, str]] = None): # RUF013
80 80 | pass
81 81 |
82 82 |
RUF013_0.py:98:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:83:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
98 | def f(arg: int | float | str | bytes = None): # RUF013
| ^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013
99 | pass
83 | def f(arg: typing.Union[int, str] = None): # RUF013
| ^^^^^^^^^^^^^^^^^^^^^^ RUF013
84 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
95 95 | pass
96 96 |
97 97 |
98 |-def f(arg: int | float | str | bytes = None): # RUF013
98 |+def f(arg: Optional[int | float | str | bytes] = None): # RUF013
99 99 | pass
80 80 | pass
81 81 |
82 82 |
83 |-def f(arg: typing.Union[int, str] = None): # RUF013
83 |+def f(arg: Optional[typing.Union[int, str]] = None): # RUF013
84 84 | pass
85 85 |
86 86 |
RUF013_0.py:102:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
102 | def f(arg: int | float = None): # RUF013
| ^^^^^^^^^^^ RUF013
103 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
99 99 | pass
100 100 |
101 101 |
102 |-def f(arg: int | float = None): # RUF013
102 |+def f(arg: Optional[int | float] = None): # RUF013
103 103 | pass
104 104 |
105 105 |
RUF013_0.py:113:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:106:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
113 | def f(arg: Literal[1, "foo"] = None): # RUF013
106 | def f(arg: int | float | str | bytes = None): # RUF013
| ^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013
107 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
103 103 | pass
104 104 |
105 105 |
106 |-def f(arg: int | float | str | bytes = None): # RUF013
106 |+def f(arg: Optional[int | float | str | bytes] = None): # RUF013
107 107 | pass
108 108 |
109 109 |
RUF013_0.py:125:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
125 | def f(arg: Literal[1] = None): # RUF013
| ^^^^^^^^^^ RUF013
126 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
122 122 | pass
123 123 |
124 124 |
125 |-def f(arg: Literal[1] = None): # RUF013
125 |+def f(arg: Optional[Literal[1]] = None): # RUF013
126 126 | pass
127 127 |
128 128 |
RUF013_0.py:129:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
129 | def f(arg: Literal[1, "foo"] = None): # RUF013
| ^^^^^^^^^^^^^^^^^ RUF013
114 | pass
130 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
110 110 | pass
111 111 |
112 112 |
113 |-def f(arg: Literal[1, "foo"] = None): # RUF013
113 |+def f(arg: Optional[Literal[1, "foo"]] = None): # RUF013
114 114 | pass
115 115 |
116 116 |
126 126 | pass
127 127 |
128 128 |
129 |-def f(arg: Literal[1, "foo"] = None): # RUF013
129 |+def f(arg: Optional[Literal[1, "foo"]] = None): # RUF013
130 130 | pass
131 131 |
132 132 |
RUF013_0.py:117:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:133:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
117 | def f(arg: typing.Literal[1, "foo", True] = None): # RUF013
133 | def f(arg: typing.Literal[1, "foo", True] = None): # RUF013
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013
118 | pass
134 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
114 114 | pass
115 115 |
116 116 |
117 |-def f(arg: typing.Literal[1, "foo", True] = None): # RUF013
117 |+def f(arg: Optional[typing.Literal[1, "foo", True]] = None): # RUF013
118 118 | pass
119 119 |
120 120 |
RUF013_0.py:136:22: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
136 | def f(arg: Annotated[int, ...] = None): # RUF013
| ^^^ RUF013
137 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
133 133 | pass
134 134 |
130 130 | pass
131 131 |
132 132 |
133 |-def f(arg: typing.Literal[1, "foo", True] = None): # RUF013
133 |+def f(arg: Optional[typing.Literal[1, "foo", True]] = None): # RUF013
134 134 | pass
135 135 |
136 |-def f(arg: Annotated[int, ...] = None): # RUF013
136 |+def f(arg: Annotated[Optional[int], ...] = None): # RUF013
137 137 | pass
138 138 |
139 139 |
136 136 |
RUF013_0.py:140:32: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:152:22: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
140 | def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013
152 | def f(arg: Annotated[int, ...] = None): # RUF013
| ^^^ RUF013
153 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
149 149 | pass
150 150 |
151 151 |
152 |-def f(arg: Annotated[int, ...] = None): # RUF013
152 |+def f(arg: Annotated[Optional[int], ...] = None): # RUF013
153 153 | pass
154 154 |
155 155 |
RUF013_0.py:156:32: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
156 | def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013
| ^^^^^^^^^ RUF013
141 | pass
157 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
137 137 | pass
138 138 |
139 139 |
140 |-def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013
140 |+def f(arg: Annotated[Annotated[Optional[int | str], ...], ...] = None): # RUF013
141 141 | pass
142 142 |
143 143 |
153 153 | pass
154 154 |
155 155 |
156 |-def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013
156 |+def f(arg: Annotated[Annotated[Optional[int | str], ...], ...] = None): # RUF013
157 157 | pass
158 158 |
159 159 |
RUF013_0.py:156:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:172:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
155 | def f(
156 | arg1: int = None, # RUF013
171 | def f(
172 | arg1: int = None, # RUF013
| ^^^ RUF013
157 | arg2: Union[int, float] = None, # RUF013
158 | arg3: Literal[1, 2, 3] = None, # RUF013
173 | arg2: Union[int, float] = None, # RUF013
174 | arg3: Literal[1, 2, 3] = None, # RUF013
|
= help: Convert to `Optional[T]`
Suggested fix
153 153 |
154 154 |
155 155 | def f(
156 |- arg1: int = None, # RUF013
156 |+ arg1: Optional[int] = None, # RUF013
157 157 | arg2: Union[int, float] = None, # RUF013
158 158 | arg3: Literal[1, 2, 3] = None, # RUF013
159 159 | ):
169 169 |
170 170 |
171 171 | def f(
172 |- arg1: int = None, # RUF013
172 |+ arg1: Optional[int] = None, # RUF013
173 173 | arg2: Union[int, float] = None, # RUF013
174 174 | arg3: Literal[1, 2, 3] = None, # RUF013
175 175 | ):
RUF013_0.py:157:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:173:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
155 | def f(
156 | arg1: int = None, # RUF013
157 | arg2: Union[int, float] = None, # RUF013
171 | def f(
172 | arg1: int = None, # RUF013
173 | arg2: Union[int, float] = None, # RUF013
| ^^^^^^^^^^^^^^^^^ RUF013
158 | arg3: Literal[1, 2, 3] = None, # RUF013
159 | ):
174 | arg3: Literal[1, 2, 3] = None, # RUF013
175 | ):
|
= help: Convert to `Optional[T]`
Suggested fix
154 154 |
155 155 | def f(
156 156 | arg1: int = None, # RUF013
157 |- arg2: Union[int, float] = None, # RUF013
157 |+ arg2: Optional[Union[int, float]] = None, # RUF013
158 158 | arg3: Literal[1, 2, 3] = None, # RUF013
159 159 | ):
160 160 | pass
170 170 |
171 171 | def f(
172 172 | arg1: int = None, # RUF013
173 |- arg2: Union[int, float] = None, # RUF013
173 |+ arg2: Optional[Union[int, float]] = None, # RUF013
174 174 | arg3: Literal[1, 2, 3] = None, # RUF013
175 175 | ):
176 176 | pass
RUF013_0.py:158:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:174:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
156 | arg1: int = None, # RUF013
157 | arg2: Union[int, float] = None, # RUF013
158 | arg3: Literal[1, 2, 3] = None, # RUF013
172 | arg1: int = None, # RUF013
173 | arg2: Union[int, float] = None, # RUF013
174 | arg3: Literal[1, 2, 3] = None, # RUF013
| ^^^^^^^^^^^^^^^^ RUF013
159 | ):
160 | pass
175 | ):
176 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
155 155 | def f(
156 156 | arg1: int = None, # RUF013
157 157 | arg2: Union[int, float] = None, # RUF013
158 |- arg3: Literal[1, 2, 3] = None, # RUF013
158 |+ arg3: Optional[Literal[1, 2, 3]] = None, # RUF013
159 159 | ):
160 160 | pass
161 161 |
171 171 | def f(
172 172 | arg1: int = None, # RUF013
173 173 | arg2: Union[int, float] = None, # RUF013
174 |- arg3: Literal[1, 2, 3] = None, # RUF013
174 |+ arg3: Optional[Literal[1, 2, 3]] = None, # RUF013
175 175 | ):
176 176 | pass
177 177 |
RUF013_0.py:186:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:202:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
186 | def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013
202 | def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013
187 | pass
203 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
183 183 | pass
184 184 |
185 185 |
186 |-def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013
186 |+def f(arg: Optional[Union[Annotated[int, ...], Union[str, bytes]]] = None): # RUF013
187 187 | pass
188 188 |
189 189 |
RUF013_0.py:193:13: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
193 | def f(arg: "int" = None): # RUF013
| ^^^ RUF013
194 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
190 190 | # Quoted
191 191 |
192 192 |
193 |-def f(arg: "int" = None): # RUF013
193 |+def f(arg: "Optional[int]" = None): # RUF013
194 194 | pass
195 195 |
196 196 |
RUF013_0.py:197:13: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
197 | def f(arg: "str" = None): # RUF013
| ^^^ RUF013
198 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
194 194 | pass
195 195 |
196 196 |
197 |-def f(arg: "str" = None): # RUF013
197 |+def f(arg: "Optional[str]" = None): # RUF013
198 198 | pass
199 199 |
199 199 | pass
200 200 |
201 201 |
202 |-def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013
202 |+def f(arg: Optional[Union[Annotated[int, ...], Union[str, bytes]]] = None): # RUF013
203 203 | pass
204 204 |
205 205 |
RUF013_0.py:201:12: RUF013 PEP 484 prohibits implicit `Optional`
RUF013_0.py:209:13: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
201 | def f(arg: "st" "r" = None): # RUF013
| ^^^^^^^^ RUF013
202 | pass
|
= help: Convert to `Optional[T]`
RUF013_0.py:209:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
209 | def f(arg: Union["int", "str"] = None): # RUF013
| ^^^^^^^^^^^^^^^^^^^ RUF013
209 | def f(arg: "int" = None): # RUF013
| ^^^ RUF013
210 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
206 206 | pass
206 206 | # Quoted
207 207 |
208 208 |
209 |-def f(arg: Union["int", "str"] = None): # RUF013
209 |+def f(arg: Optional[Union["int", "str"]] = None): # RUF013
209 |-def f(arg: "int" = None): # RUF013
209 |+def f(arg: "Optional[int]" = None): # RUF013
210 210 | pass
211 211 |
212 212 |
RUF013_0.py:213:13: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
213 | def f(arg: "str" = None): # RUF013
| ^^^ RUF013
214 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
210 210 | pass
211 211 |
212 212 |
213 |-def f(arg: "str" = None): # RUF013
213 |+def f(arg: "Optional[str]" = None): # RUF013
214 214 | pass
215 215 |
216 216 |
RUF013_0.py:217:12: RUF013 PEP 484 prohibits implicit `Optional`
|
217 | def f(arg: "st" "r" = None): # RUF013
| ^^^^^^^^ RUF013
218 | pass
|
= help: Convert to `Optional[T]`
RUF013_0.py:225:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
225 | def f(arg: Union["int", "str"] = None): # RUF013
| ^^^^^^^^^^^^^^^^^^^ RUF013
226 | pass
|
= help: Convert to `Optional[T]`
Suggested fix
222 222 | pass
223 223 |
224 224 |
225 |-def f(arg: Union["int", "str"] = None): # RUF013
225 |+def f(arg: Optional[Union["int", "str"]] = None): # RUF013
226 226 | pass
227 227 |
228 228 |

View File

@ -37,28 +37,46 @@ RUF013_0.py:25:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
27 27 |
28 28 |
RUF013_0.py:67:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:29:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
67 | def f(arg: Union = None): # RUF013
| ^^^^^ RUF013
68 | pass
29 | def f(arg: typing.List[str] = None): # RUF013
| ^^^^^^^^^^^^^^^^ RUF013
30 | pass
|
= help: Convert to `T | None`
Suggested fix
64 64 | pass
65 65 |
66 66 |
67 |-def f(arg: Union = None): # RUF013
67 |+def f(arg: Union | None = None): # RUF013
68 68 | pass
69 69 |
70 70 |
26 26 | pass
27 27 |
28 28 |
29 |-def f(arg: typing.List[str] = None): # RUF013
29 |+def f(arg: typing.List[str] | None = None): # RUF013
30 30 | pass
31 31 |
32 32 |
RUF013_0.py:33:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
33 | def f(arg: Tuple[str] = None): # RUF013
| ^^^^^^^^^^ RUF013
34 | pass
|
= help: Convert to `T | None`
Suggested fix
30 30 | pass
31 31 |
32 32 |
33 |-def f(arg: Tuple[str] = None): # RUF013
33 |+def f(arg: Tuple[str] | None = None): # RUF013
34 34 | pass
35 35 |
36 36 |
RUF013_0.py:71:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
71 | def f(arg: Union[int, str] = None): # RUF013
| ^^^^^^^^^^^^^^^ RUF013
71 | def f(arg: Union = None): # RUF013
| ^^^^^ RUF013
72 | pass
|
= help: Convert to `T | None`
@ -67,16 +85,16 @@ RUF013_0.py:71:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
68 68 | pass
69 69 |
70 70 |
71 |-def f(arg: Union[int, str] = None): # RUF013
71 |+def f(arg: Union[int, str] | None = None): # RUF013
71 |-def f(arg: Union = None): # RUF013
71 |+def f(arg: Union | None = None): # RUF013
72 72 | pass
73 73 |
74 74 |
RUF013_0.py:75:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
75 | def f(arg: typing.Union[int, str] = None): # RUF013
| ^^^^^^^^^^^^^^^^^^^^^^ RUF013
75 | def f(arg: Union[int] = None): # RUF013
| ^^^^^^^^^^ RUF013
76 | pass
|
= help: Convert to `T | None`
@ -85,260 +103,314 @@ RUF013_0.py:75:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
72 72 | pass
73 73 |
74 74 |
75 |-def f(arg: typing.Union[int, str] = None): # RUF013
75 |+def f(arg: typing.Union[int, str] | None = None): # RUF013
75 |-def f(arg: Union[int] = None): # RUF013
75 |+def f(arg: Union[int] | None = None): # RUF013
76 76 | pass
77 77 |
78 78 |
RUF013_0.py:94:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:79:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
94 | def f(arg: int | float = None): # RUF013
| ^^^^^^^^^^^ RUF013
95 | pass
79 | def f(arg: Union[int, str] = None): # RUF013
| ^^^^^^^^^^^^^^^ RUF013
80 | pass
|
= help: Convert to `T | None`
Suggested fix
91 91 | pass
92 92 |
93 93 |
94 |-def f(arg: int | float = None): # RUF013
94 |+def f(arg: int | float | None = None): # RUF013
95 95 | pass
96 96 |
97 97 |
76 76 | pass
77 77 |
78 78 |
79 |-def f(arg: Union[int, str] = None): # RUF013
79 |+def f(arg: Union[int, str] | None = None): # RUF013
80 80 | pass
81 81 |
82 82 |
RUF013_0.py:98:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:83:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
98 | def f(arg: int | float | str | bytes = None): # RUF013
| ^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013
99 | pass
83 | def f(arg: typing.Union[int, str] = None): # RUF013
| ^^^^^^^^^^^^^^^^^^^^^^ RUF013
84 | pass
|
= help: Convert to `T | None`
Suggested fix
95 95 | pass
96 96 |
97 97 |
98 |-def f(arg: int | float | str | bytes = None): # RUF013
98 |+def f(arg: int | float | str | bytes | None = None): # RUF013
99 99 | pass
80 80 | pass
81 81 |
82 82 |
83 |-def f(arg: typing.Union[int, str] = None): # RUF013
83 |+def f(arg: typing.Union[int, str] | None = None): # RUF013
84 84 | pass
85 85 |
86 86 |
RUF013_0.py:102:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
102 | def f(arg: int | float = None): # RUF013
| ^^^^^^^^^^^ RUF013
103 | pass
|
= help: Convert to `T | None`
Suggested fix
99 99 | pass
100 100 |
101 101 |
102 |-def f(arg: int | float = None): # RUF013
102 |+def f(arg: int | float | None = None): # RUF013
103 103 | pass
104 104 |
105 105 |
RUF013_0.py:113:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:106:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
113 | def f(arg: Literal[1, "foo"] = None): # RUF013
106 | def f(arg: int | float | str | bytes = None): # RUF013
| ^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013
107 | pass
|
= help: Convert to `T | None`
Suggested fix
103 103 | pass
104 104 |
105 105 |
106 |-def f(arg: int | float | str | bytes = None): # RUF013
106 |+def f(arg: int | float | str | bytes | None = None): # RUF013
107 107 | pass
108 108 |
109 109 |
RUF013_0.py:125:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
125 | def f(arg: Literal[1] = None): # RUF013
| ^^^^^^^^^^ RUF013
126 | pass
|
= help: Convert to `T | None`
Suggested fix
122 122 | pass
123 123 |
124 124 |
125 |-def f(arg: Literal[1] = None): # RUF013
125 |+def f(arg: Literal[1] | None = None): # RUF013
126 126 | pass
127 127 |
128 128 |
RUF013_0.py:129:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
129 | def f(arg: Literal[1, "foo"] = None): # RUF013
| ^^^^^^^^^^^^^^^^^ RUF013
114 | pass
130 | pass
|
= help: Convert to `T | None`
Suggested fix
110 110 | pass
111 111 |
112 112 |
113 |-def f(arg: Literal[1, "foo"] = None): # RUF013
113 |+def f(arg: Literal[1, "foo"] | None = None): # RUF013
114 114 | pass
115 115 |
116 116 |
126 126 | pass
127 127 |
128 128 |
129 |-def f(arg: Literal[1, "foo"] = None): # RUF013
129 |+def f(arg: Literal[1, "foo"] | None = None): # RUF013
130 130 | pass
131 131 |
132 132 |
RUF013_0.py:117:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:133:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
117 | def f(arg: typing.Literal[1, "foo", True] = None): # RUF013
133 | def f(arg: typing.Literal[1, "foo", True] = None): # RUF013
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013
118 | pass
134 | pass
|
= help: Convert to `T | None`
Suggested fix
114 114 | pass
115 115 |
116 116 |
117 |-def f(arg: typing.Literal[1, "foo", True] = None): # RUF013
117 |+def f(arg: typing.Literal[1, "foo", True] | None = None): # RUF013
118 118 | pass
119 119 |
120 120 |
RUF013_0.py:136:22: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
136 | def f(arg: Annotated[int, ...] = None): # RUF013
| ^^^ RUF013
137 | pass
|
= help: Convert to `T | None`
Suggested fix
133 133 | pass
134 134 |
130 130 | pass
131 131 |
132 132 |
133 |-def f(arg: typing.Literal[1, "foo", True] = None): # RUF013
133 |+def f(arg: typing.Literal[1, "foo", True] | None = None): # RUF013
134 134 | pass
135 135 |
136 |-def f(arg: Annotated[int, ...] = None): # RUF013
136 |+def f(arg: Annotated[int | None, ...] = None): # RUF013
137 137 | pass
138 138 |
139 139 |
136 136 |
RUF013_0.py:140:32: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:152:22: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
140 | def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013
152 | def f(arg: Annotated[int, ...] = None): # RUF013
| ^^^ RUF013
153 | pass
|
= help: Convert to `T | None`
Suggested fix
149 149 | pass
150 150 |
151 151 |
152 |-def f(arg: Annotated[int, ...] = None): # RUF013
152 |+def f(arg: Annotated[int | None, ...] = None): # RUF013
153 153 | pass
154 154 |
155 155 |
RUF013_0.py:156:32: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
156 | def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013
| ^^^^^^^^^ RUF013
141 | pass
157 | pass
|
= help: Convert to `T | None`
Suggested fix
137 137 | pass
138 138 |
139 139 |
140 |-def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013
140 |+def f(arg: Annotated[Annotated[int | str | None, ...], ...] = None): # RUF013
141 141 | pass
142 142 |
143 143 |
153 153 | pass
154 154 |
155 155 |
156 |-def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013
156 |+def f(arg: Annotated[Annotated[int | str | None, ...], ...] = None): # RUF013
157 157 | pass
158 158 |
159 159 |
RUF013_0.py:156:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:172:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
155 | def f(
156 | arg1: int = None, # RUF013
171 | def f(
172 | arg1: int = None, # RUF013
| ^^^ RUF013
157 | arg2: Union[int, float] = None, # RUF013
158 | arg3: Literal[1, 2, 3] = None, # RUF013
173 | arg2: Union[int, float] = None, # RUF013
174 | arg3: Literal[1, 2, 3] = None, # RUF013
|
= help: Convert to `T | None`
Suggested fix
153 153 |
154 154 |
155 155 | def f(
156 |- arg1: int = None, # RUF013
156 |+ arg1: int | None = None, # RUF013
157 157 | arg2: Union[int, float] = None, # RUF013
158 158 | arg3: Literal[1, 2, 3] = None, # RUF013
159 159 | ):
169 169 |
170 170 |
171 171 | def f(
172 |- arg1: int = None, # RUF013
172 |+ arg1: int | None = None, # RUF013
173 173 | arg2: Union[int, float] = None, # RUF013
174 174 | arg3: Literal[1, 2, 3] = None, # RUF013
175 175 | ):
RUF013_0.py:157:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:173:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
155 | def f(
156 | arg1: int = None, # RUF013
157 | arg2: Union[int, float] = None, # RUF013
171 | def f(
172 | arg1: int = None, # RUF013
173 | arg2: Union[int, float] = None, # RUF013
| ^^^^^^^^^^^^^^^^^ RUF013
158 | arg3: Literal[1, 2, 3] = None, # RUF013
159 | ):
174 | arg3: Literal[1, 2, 3] = None, # RUF013
175 | ):
|
= help: Convert to `T | None`
Suggested fix
154 154 |
155 155 | def f(
156 156 | arg1: int = None, # RUF013
157 |- arg2: Union[int, float] = None, # RUF013
157 |+ arg2: Union[int, float] | None = None, # RUF013
158 158 | arg3: Literal[1, 2, 3] = None, # RUF013
159 159 | ):
160 160 | pass
170 170 |
171 171 | def f(
172 172 | arg1: int = None, # RUF013
173 |- arg2: Union[int, float] = None, # RUF013
173 |+ arg2: Union[int, float] | None = None, # RUF013
174 174 | arg3: Literal[1, 2, 3] = None, # RUF013
175 175 | ):
176 176 | pass
RUF013_0.py:158:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:174:11: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
156 | arg1: int = None, # RUF013
157 | arg2: Union[int, float] = None, # RUF013
158 | arg3: Literal[1, 2, 3] = None, # RUF013
172 | arg1: int = None, # RUF013
173 | arg2: Union[int, float] = None, # RUF013
174 | arg3: Literal[1, 2, 3] = None, # RUF013
| ^^^^^^^^^^^^^^^^ RUF013
159 | ):
160 | pass
175 | ):
176 | pass
|
= help: Convert to `T | None`
Suggested fix
155 155 | def f(
156 156 | arg1: int = None, # RUF013
157 157 | arg2: Union[int, float] = None, # RUF013
158 |- arg3: Literal[1, 2, 3] = None, # RUF013
158 |+ arg3: Literal[1, 2, 3] | None = None, # RUF013
159 159 | ):
160 160 | pass
161 161 |
171 171 | def f(
172 172 | arg1: int = None, # RUF013
173 173 | arg2: Union[int, float] = None, # RUF013
174 |- arg3: Literal[1, 2, 3] = None, # RUF013
174 |+ arg3: Literal[1, 2, 3] | None = None, # RUF013
175 175 | ):
176 176 | pass
177 177 |
RUF013_0.py:186:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
RUF013_0.py:202:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
186 | def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013
202 | def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013
187 | pass
203 | pass
|
= help: Convert to `T | None`
Suggested fix
183 183 | pass
184 184 |
185 185 |
186 |-def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013
186 |+def f(arg: Union[Annotated[int, ...], Union[str, bytes]] | None = None): # RUF013
187 187 | pass
188 188 |
189 189 |
RUF013_0.py:193:13: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
193 | def f(arg: "int" = None): # RUF013
| ^^^ RUF013
194 | pass
|
= help: Convert to `T | None`
Suggested fix
190 190 | # Quoted
191 191 |
192 192 |
193 |-def f(arg: "int" = None): # RUF013
193 |+def f(arg: "int | None" = None): # RUF013
194 194 | pass
195 195 |
196 196 |
RUF013_0.py:197:13: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
197 | def f(arg: "str" = None): # RUF013
| ^^^ RUF013
198 | pass
|
= help: Convert to `T | None`
Suggested fix
194 194 | pass
195 195 |
196 196 |
197 |-def f(arg: "str" = None): # RUF013
197 |+def f(arg: "str | None" = None): # RUF013
198 198 | pass
199 199 |
199 199 | pass
200 200 |
201 201 |
202 |-def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013
202 |+def f(arg: Union[Annotated[int, ...], Union[str, bytes]] | None = None): # RUF013
203 203 | pass
204 204 |
205 205 |
RUF013_0.py:201:12: RUF013 PEP 484 prohibits implicit `Optional`
RUF013_0.py:209:13: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
201 | def f(arg: "st" "r" = None): # RUF013
| ^^^^^^^^ RUF013
202 | pass
|
= help: Convert to `T | None`
RUF013_0.py:209:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
209 | def f(arg: Union["int", "str"] = None): # RUF013
| ^^^^^^^^^^^^^^^^^^^ RUF013
209 | def f(arg: "int" = None): # RUF013
| ^^^ RUF013
210 | pass
|
= help: Convert to `T | None`
Suggested fix
206 206 | pass
206 206 | # Quoted
207 207 |
208 208 |
209 |-def f(arg: Union["int", "str"] = None): # RUF013
209 |+def f(arg: Union["int", "str"] | None = None): # RUF013
209 |-def f(arg: "int" = None): # RUF013
209 |+def f(arg: "int | None" = None): # RUF013
210 210 | pass
211 211 |
212 212 |
RUF013_0.py:213:13: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
213 | def f(arg: "str" = None): # RUF013
| ^^^ RUF013
214 | pass
|
= help: Convert to `T | None`
Suggested fix
210 210 | pass
211 211 |
212 212 |
213 |-def f(arg: "str" = None): # RUF013
213 |+def f(arg: "str | None" = None): # RUF013
214 214 | pass
215 215 |
216 216 |
RUF013_0.py:217:12: RUF013 PEP 484 prohibits implicit `Optional`
|
217 | def f(arg: "st" "r" = None): # RUF013
| ^^^^^^^^ RUF013
218 | pass
|
= help: Convert to `T | None`
RUF013_0.py:225:12: RUF013 [*] PEP 484 prohibits implicit `Optional`
|
225 | def f(arg: Union["int", "str"] = None): # RUF013
| ^^^^^^^^^^^^^^^^^^^ RUF013
226 | pass
|
= help: Convert to `T | None`
Suggested fix
222 222 | pass
223 223 |
224 224 |
225 |-def f(arg: Union["int", "str"] = None): # RUF013
225 |+def f(arg: Union["int", "str"] | None = None): # RUF013
226 226 | pass
227 227 |
228 228 |

View File

@ -1,3 +1,4 @@
use itertools::Either::{Left, Right};
use rustpython_parser::ast::{self, Constant, Expr, Operator};
use ruff_python_ast::call_path::CallPath;
@ -52,6 +53,15 @@ fn is_known_type(call_path: &CallPath, minor_version: u32) -> bool {
}
}
/// Returns an iterator over the expressions in a slice. If the slice is not a
/// tuple, the iterator will only yield the slice.
fn resolve_slice_value(slice: &Expr) -> impl Iterator<Item = &Expr> {
match slice {
Expr::Tuple(ast::ExprTuple { elts: elements, .. }) => Left(elements.iter()),
_ => Right(std::iter::once(slice)),
}
}
#[derive(Debug)]
enum TypingTarget<'a> {
/// Literal `None` type.
@ -66,12 +76,14 @@ enum TypingTarget<'a> {
/// Forward reference to a type e.g., `"List[str]"`.
ForwardReference(Expr),
/// A `typing.Union` type or `|` separated types e.g., `Union[int, str]`
/// or `int | str`.
Union(Vec<&'a Expr>),
/// A `typing.Union` type e.g., `Union[int, str]`.
Union(&'a Expr),
/// A PEP 604 union type e.g., `int | str`.
PEP604Union(&'a Expr),
/// A `typing.Literal` type e.g., `Literal[1, 2, 3]`.
Literal(Vec<&'a Expr>),
Literal(&'a Expr),
/// A `typing.Optional` type e.g., `Optional[int]`.
Optional(&'a Expr),
@ -99,17 +111,15 @@ impl<'a> TypingTarget<'a> {
match expr {
Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => {
if semantic.match_typing_expr(value, "Optional") {
return Some(TypingTarget::Optional(slice.as_ref()));
}
let Expr::Tuple(ast::ExprTuple { elts: elements, .. }) = slice.as_ref() else {
return None;
};
if semantic.match_typing_expr(value, "Literal") {
Some(TypingTarget::Literal(elements.iter().collect()))
Some(TypingTarget::Optional(slice.as_ref()))
} else if semantic.match_typing_expr(value, "Literal") {
Some(TypingTarget::Literal(slice))
} else if semantic.match_typing_expr(value, "Union") {
Some(TypingTarget::Union(elements.iter().collect()))
Some(TypingTarget::Union(slice))
} else if semantic.match_typing_expr(value, "Annotated") {
elements.first().map(TypingTarget::Annotated)
resolve_slice_value(slice.as_ref())
.next()
.map(TypingTarget::Annotated)
} else {
semantic.resolve_call_path(value).map_or(
// If we can't resolve the call path, it must be defined
@ -125,9 +135,7 @@ impl<'a> TypingTarget<'a> {
)
}
}
Expr::BinOp(..) => Some(TypingTarget::Union(
PEP604UnionIterator::new(expr).collect(),
)),
Expr::BinOp(..) => Some(TypingTarget::PEP604Union(expr)),
Expr::Constant(ast::ExprConstant {
value: Constant::None,
..
@ -172,7 +180,7 @@ impl<'a> TypingTarget<'a> {
| TypingTarget::Object
| TypingTarget::Unknown => true,
TypingTarget::Known => false,
TypingTarget::Literal(elements) => elements.iter().any(|element| {
TypingTarget::Literal(slice) => resolve_slice_value(slice).any(|element| {
// Literal can only contain `None`, a literal value, other `Literal`
// or an enum value.
match TypingTarget::try_from_expr(element, semantic, locator, minor_version) {
@ -183,17 +191,23 @@ impl<'a> TypingTarget<'a> {
_ => false,
}
}),
TypingTarget::Union(elements) => elements.iter().any(|element| {
TypingTarget::Union(slice) => resolve_slice_value(slice).any(|element| {
TypingTarget::try_from_expr(element, semantic, locator, minor_version)
.map_or(true, |new_target| {
new_target.contains_none(semantic, locator, minor_version)
})
}),
TypingTarget::Annotated(element) => {
TypingTarget::PEP604Union(expr) => PEP604UnionIterator::new(expr).any(|element| {
TypingTarget::try_from_expr(element, semantic, locator, minor_version)
.map_or(true, |new_target| {
new_target.contains_none(semantic, locator, minor_version)
})
}),
TypingTarget::Annotated(expr) => {
TypingTarget::try_from_expr(expr, semantic, locator, minor_version)
.map_or(true, |new_target| {
new_target.contains_none(semantic, locator, minor_version)
})
}
TypingTarget::ForwardReference(expr) => {
TypingTarget::try_from_expr(expr, semantic, locator, minor_version)
@ -219,17 +233,23 @@ impl<'a> TypingTarget<'a> {
| TypingTarget::Object
| TypingTarget::Known
| TypingTarget::Unknown => false,
TypingTarget::Union(elements) => elements.iter().any(|element| {
TypingTarget::Union(slice) => resolve_slice_value(slice).any(|element| {
TypingTarget::try_from_expr(element, semantic, locator, minor_version)
.map_or(true, |new_target| {
new_target.contains_any(semantic, locator, minor_version)
})
}),
TypingTarget::Annotated(element) | TypingTarget::Optional(element) => {
TypingTarget::PEP604Union(expr) => PEP604UnionIterator::new(expr).any(|element| {
TypingTarget::try_from_expr(element, semantic, locator, minor_version)
.map_or(true, |new_target| {
new_target.contains_any(semantic, locator, minor_version)
})
}),
TypingTarget::Annotated(expr) | TypingTarget::Optional(expr) => {
TypingTarget::try_from_expr(expr, semantic, locator, minor_version)
.map_or(true, |new_target| {
new_target.contains_any(semantic, locator, minor_version)
})
}
TypingTarget::ForwardReference(expr) => {
TypingTarget::try_from_expr(expr, semantic, locator, minor_version)

View File

@ -83,7 +83,7 @@ pub fn test_snippet(contents: &str, settings: &Settings) -> Vec<Message> {
}
thread_local! {
static MAX_ITERATIONS: std::cell::Cell<usize> = std::cell::Cell::new(20);
static MAX_ITERATIONS: std::cell::Cell<usize> = std::cell::Cell::new(30);
}
pub fn set_max_iterations(max: usize) {