rustpython_ruff_python_parser/semantic_errors.rs
1//! [`SemanticSyntaxChecker`] for AST-based syntax errors.
2//!
3//! This checker is not responsible for traversing the AST itself. Instead, its
4//! [`SemanticSyntaxChecker::visit_stmt`] and [`SemanticSyntaxChecker::visit_expr`] methods should
5//! be called in a parent `Visitor`'s `visit_stmt` and `visit_expr` methods, respectively.
6
7use ruff_python_ast::{
8 self as ast, Expr, ExprContext, IrrefutablePatternKind, Pattern, PythonVersion, Stmt, StmtExpr,
9 StmtFunctionDef, StmtImportFrom,
10 comparable::ComparableExpr,
11 helpers,
12 visitor::{Visitor, walk_expr, walk_stmt},
13};
14use ruff_text_size::{Ranged, TextRange, TextSize};
15use rustc_hash::{FxBuildHasher, FxHashSet};
16use std::fmt::Display;
17
18#[derive(Debug, Default)]
19pub struct SemanticSyntaxChecker {
20 /// The checker has traversed past the `__future__` import boundary.
21 ///
22 /// For example, the checker could be visiting `x` in:
23 ///
24 /// ```python
25 /// from __future__ import annotations
26 ///
27 /// import os
28 ///
29 /// x: int = 1
30 /// ```
31 ///
32 /// Python considers it a syntax error to import from `__future__` after any other
33 /// non-`__future__`-importing statements.
34 seen_futures_boundary: bool,
35
36 /// The checker has traversed past the module docstring boundary (i.e. seen any statement in the
37 /// module).
38 seen_module_docstring_boundary: bool,
39}
40
41impl SemanticSyntaxChecker {
42 pub fn new() -> Self {
43 Self::default()
44 }
45}
46
47impl SemanticSyntaxChecker {
48 fn add_error<Ctx: SemanticSyntaxContext>(
49 context: &Ctx,
50 kind: SemanticSyntaxErrorKind,
51 range: TextRange,
52 ) {
53 context.report_semantic_error(SemanticSyntaxError {
54 kind,
55 range,
56 python_version: context.python_version(),
57 });
58 }
59
60 fn check_lazy_import_context<Ctx: SemanticSyntaxContext>(
61 ctx: &Ctx,
62 range: TextRange,
63 kind: LazyImportKind,
64 ) -> bool {
65 if let Some(context) = ctx.lazy_import_context() {
66 Self::add_error(
67 ctx,
68 SemanticSyntaxErrorKind::LazyImportNotAllowed { context, kind },
69 range,
70 );
71 return true;
72 }
73 false
74 }
75
76 fn check_stmt<Ctx: SemanticSyntaxContext>(&mut self, stmt: &ast::Stmt, ctx: &Ctx) {
77 match stmt {
78 Stmt::ImportFrom(StmtImportFrom {
79 range,
80 module,
81 level,
82 names,
83 is_lazy,
84 ..
85 }) => {
86 let mut handled_lazy_error = false;
87
88 if *is_lazy {
89 // test_ok lazy_import_semantic_ok_py315
90 // # parse_options: {"target-version": "3.15"}
91 // import contextlib
92 // with contextlib.nullcontext():
93 // lazy import os
94 // with contextlib.nullcontext():
95 // lazy from sys import path
96
97 // test_err lazy_import_invalid_context_py315
98 // # parse_options: {"target-version": "3.15"}
99 // try:
100 // lazy import os
101 // except:
102 // pass
103 //
104 // try:
105 // x
106 // except* Exception:
107 // lazy import sys
108 //
109 // def func():
110 // lazy import math
111 //
112 // async def async_func():
113 // lazy from json import loads
114 //
115 // class MyClass:
116 // lazy import typing
117 //
118 // def outer():
119 // class Inner:
120 // lazy import json
121 if Self::check_lazy_import_context(ctx, *range, LazyImportKind::ImportFrom) {
122 handled_lazy_error = true;
123 } else if names.iter().any(|alias| alias.name.as_str() == "*") {
124 // test_err lazy_import_invalid_from_py315
125 // # parse_options: {"target-version": "3.15"}
126 // lazy from os import *
127 // lazy from __future__ import annotations
128 //
129 // def func():
130 // lazy from sys import *
131 Self::add_error(ctx, SemanticSyntaxErrorKind::LazyImportStar, *range);
132 handled_lazy_error = true;
133 } else if matches!(module.as_deref(), Some("__future__")) {
134 Self::add_error(ctx, SemanticSyntaxErrorKind::LazyFutureImport, *range);
135 handled_lazy_error = true;
136 }
137 }
138
139 if handled_lazy_error {
140 // Skip the regular `from`-import validations after reporting the lazy-specific
141 // syntax error with the highest precedence.
142 } else if matches!(module.as_deref(), Some("__future__")) {
143 for name in names {
144 if !is_known_future_feature(&name.name) {
145 // test_ok valid_future_feature
146 // from __future__ import annotations
147
148 // test_err invalid_future_feature
149 // from __future__ import invalid_feature
150 // from __future__ import annotations, invalid_feature
151 // from __future__ import invalid_feature_1, invalid_feature_2
152 Self::add_error(
153 ctx,
154 SemanticSyntaxErrorKind::FutureFeatureNotDefined(
155 name.name.to_string(),
156 ),
157 name.range,
158 );
159 }
160 }
161 if self.seen_futures_boundary {
162 Self::add_error(ctx, SemanticSyntaxErrorKind::LateFutureImport, *range);
163 }
164 }
165 for alias in names {
166 if alias.name.as_str() == "*" && !ctx.in_module_scope() {
167 // test_err import_from_star
168 // def f1():
169 // from module import *
170 // class C:
171 // from module import *
172 // def f2():
173 // from ..module import *
174 // def f3():
175 // from module import *, *
176
177 // test_ok import_from_star
178 // from module import *
179 Self::add_error(
180 ctx,
181 SemanticSyntaxErrorKind::NonModuleImportStar(
182 helpers::format_import_from(*level, module.as_deref()).to_string(),
183 ),
184 *range,
185 );
186 break;
187 }
188 }
189 }
190 Stmt::Import(ast::StmtImport {
191 range,
192 is_lazy: true,
193 ..
194 }) => {
195 Self::check_lazy_import_context(ctx, *range, LazyImportKind::Import);
196 }
197 Stmt::Match(match_stmt) => {
198 Self::irrefutable_match_case(match_stmt, ctx);
199 for case in &match_stmt.cases {
200 let mut visitor = MatchPatternVisitor {
201 names: FxHashSet::default(),
202 ctx,
203 };
204 visitor.visit_pattern(&case.pattern);
205 }
206 }
207 Stmt::FunctionDef(ast::StmtFunctionDef {
208 type_params,
209 parameters,
210 ..
211 }) => {
212 if let Some(type_params) = type_params {
213 Self::duplicate_type_parameter_name(type_params, ctx);
214 }
215 Self::duplicate_parameter_name(parameters, ctx);
216 }
217 Stmt::Global(ast::StmtGlobal { names, .. }) => {
218 for name in names {
219 if ctx.is_bound_parameter(name) {
220 Self::add_error(
221 ctx,
222 SemanticSyntaxErrorKind::GlobalParameter(name.to_string()),
223 name.range,
224 );
225 }
226 }
227 }
228 Stmt::ClassDef(ast::StmtClassDef {
229 type_params: Some(type_params),
230 ..
231 })
232 | Stmt::TypeAlias(ast::StmtTypeAlias {
233 type_params: Some(type_params),
234 ..
235 }) => {
236 Self::duplicate_type_parameter_name(type_params, ctx);
237 Self::type_parameter_default_order(type_params, ctx);
238 }
239 Stmt::Assign(ast::StmtAssign { targets, value, .. }) => {
240 if let [Expr::Starred(ast::ExprStarred { range, .. })] = targets.as_slice() {
241 // test_ok single_starred_assignment_target
242 // (*a,) = (1,)
243 // *a, = (1,)
244 // [*a] = (1,)
245
246 // test_err single_starred_assignment_target
247 // *a = (1,)
248 Self::add_error(
249 ctx,
250 SemanticSyntaxErrorKind::SingleStarredAssignment,
251 *range,
252 );
253 }
254
255 // test_ok assign_stmt_starred_expr_value
256 // _ = 4
257 // _ = [4]
258 // _ = (*[1],)
259 // _ = *[1],
260
261 // test_err assign_stmt_starred_expr_value
262 // _ = *[42]
263 // _ = *{42}
264 // _ = *list()
265 // _ = *(p + q)
266 Self::invalid_star_expression(value, ctx);
267 }
268 Stmt::Return(ast::StmtReturn {
269 value,
270 range,
271 node_index: _,
272 }) => {
273 if let Some(value) = value {
274 // test_err single_star_return
275 // def f(): return *x
276 Self::invalid_star_expression(value, ctx);
277 }
278 if !ctx.in_function_scope() {
279 Self::add_error(ctx, SemanticSyntaxErrorKind::ReturnOutsideFunction, *range);
280 }
281 }
282 Stmt::For(ast::StmtFor {
283 target,
284 iter,
285 is_async,
286 ..
287 }) => {
288 // test_err single_star_for
289 // for _ in *x: ...
290 // for *x in xs: ...
291 Self::invalid_star_expression(target, ctx);
292 Self::invalid_star_expression(iter, ctx);
293 if *is_async {
294 Self::await_outside_async_function(
295 ctx,
296 stmt,
297 AwaitOutsideAsyncFunctionKind::AsyncFor,
298 );
299 }
300 }
301 Stmt::With(ast::StmtWith { is_async: true, .. }) => {
302 Self::await_outside_async_function(
303 ctx,
304 stmt,
305 AwaitOutsideAsyncFunctionKind::AsyncWith,
306 );
307 }
308 Stmt::Nonlocal(ast::StmtNonlocal { names, range, .. }) => {
309 // test_ok nonlocal_declaration_at_module_level
310 // def _():
311 // nonlocal x
312
313 // test_err nonlocal_declaration_at_module_level
314 // nonlocal x
315 // nonlocal x, y
316 if ctx.in_module_scope() {
317 Self::add_error(
318 ctx,
319 SemanticSyntaxErrorKind::NonlocalDeclarationAtModuleLevel,
320 *range,
321 );
322 }
323
324 if !ctx.in_module_scope() {
325 for name in names {
326 if !ctx.has_nonlocal_binding(name) {
327 Self::add_error(
328 ctx,
329 SemanticSyntaxErrorKind::NonlocalWithoutBinding(name.to_string()),
330 name.range,
331 );
332 }
333 }
334 }
335 }
336 Stmt::Break(ast::StmtBreak { range, .. }) => {
337 if !ctx.in_loop_context() {
338 Self::add_error(ctx, SemanticSyntaxErrorKind::BreakOutsideLoop, *range);
339 }
340 }
341 Stmt::Continue(ast::StmtContinue { range, .. }) => {
342 if !ctx.in_loop_context() {
343 Self::add_error(ctx, SemanticSyntaxErrorKind::ContinueOutsideLoop, *range);
344 }
345 }
346 _ => {}
347 }
348
349 Self::debug_shadowing(stmt, ctx);
350 Self::check_annotation(stmt, ctx);
351 }
352
353 fn check_annotation<Ctx: SemanticSyntaxContext>(stmt: &ast::Stmt, ctx: &Ctx) {
354 match stmt {
355 Stmt::AnnAssign(ast::StmtAnnAssign {
356 target, annotation, ..
357 }) => {
358 if ctx.python_version() > PythonVersion::PY313 {
359 // test_ok valid_annotation_py313
360 // # parse_options: {"target-version": "3.13"}
361 // a: (x := 1)
362 // def outer():
363 // b: (yield 1)
364 // c: (yield from 1)
365 // async def outer():
366 // d: (await 1)
367
368 // test_err invalid_annotation_py314
369 // # parse_options: {"target-version": "3.14"}
370 // a: (x := 1)
371 // def outer():
372 // b: (yield 1)
373 // c: (yield from 1)
374 // async def outer():
375 // d: (await 1)
376 let mut visitor = InvalidExpressionVisitor {
377 position: InvalidExpressionPosition::TypeAnnotation,
378 ctx,
379 };
380 visitor.visit_expr(annotation);
381 }
382 if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() {
383 if let Some(global_stmt) = ctx.global(id.as_str()) {
384 let global_start = global_stmt.start();
385 if !ctx.in_module_scope() || target.start() < global_start {
386 Self::add_error(
387 ctx,
388 SemanticSyntaxErrorKind::AnnotatedGlobal(id.to_string()),
389 target.range(),
390 );
391 }
392 }
393 }
394 }
395 Stmt::FunctionDef(ast::StmtFunctionDef {
396 type_params,
397 parameters,
398 returns,
399 ..
400 }) => {
401 // test_ok valid_annotation_function_py313
402 // # parse_options: {"target-version": "3.13"}
403 // def f() -> (y := 3): ...
404 // def g(arg: (x := 1)): ...
405 // def outer():
406 // def i(x: (yield 1)): ...
407 // def k() -> (yield 1): ...
408 // def m(x: (yield from 1)): ...
409 // def o() -> (yield from 1): ...
410 // async def outer():
411 // def f() -> (await 1): ...
412 // def g(arg: (await 1)): ...
413
414 // test_err invalid_annotation_function_py314
415 // # parse_options: {"target-version": "3.14"}
416 // def f() -> (y := 3): ...
417 // def g(arg: (x := 1)): ...
418 // def outer():
419 // def i(x: (yield 1)): ...
420 // def k() -> (yield 1): ...
421 // def m(x: (yield from 1)): ...
422 // def o() -> (yield from 1): ...
423 // async def outer():
424 // def f() -> (await 1): ...
425 // def g(arg: (await 1)): ...
426
427 // test_err invalid_annotation_function
428 // def d[T]() -> (await 1): ...
429 // def e[T](arg: (await 1)): ...
430 // def f[T]() -> (y := 3): ...
431 // def g[T](arg: (x := 1)): ...
432 // def h[T](x: (yield 1)): ...
433 // def j[T]() -> (yield 1): ...
434 // def l[T](x: (yield from 1)): ...
435 // def n[T]() -> (yield from 1): ...
436 // def p[T: (yield 1)](): ... # yield in TypeVar bound
437 // def q[T = (yield 1)](): ... # yield in TypeVar default
438 // def r[*Ts = (yield 1)](): ... # yield in TypeVarTuple default
439 // def s[**Ts = (yield 1)](): ... # yield in ParamSpec default
440 // def t[T: (x := 1)](): ... # named expr in TypeVar bound
441 // def u[T = (x := 1)](): ... # named expr in TypeVar default
442 // def v[*Ts = (x := 1)](): ... # named expr in TypeVarTuple default
443 // def w[**Ts = (x := 1)](): ... # named expr in ParamSpec default
444 // def t[T: (await 1)](): ... # await in TypeVar bound
445 // def u[T = (await 1)](): ... # await in TypeVar default
446 // def v[*Ts = (await 1)](): ... # await in TypeVarTuple default
447 // def w[**Ts = (await 1)](): ... # await in ParamSpec default
448 let mut visitor = InvalidExpressionVisitor {
449 position: InvalidExpressionPosition::TypeAnnotation,
450 ctx,
451 };
452 if let Some(type_params) = type_params {
453 visitor.visit_type_params(type_params);
454 }
455 // the __future__ annotation error takes precedence over the generic error
456 if ctx.future_annotations_or_stub() || ctx.python_version() > PythonVersion::PY313 {
457 visitor.position = InvalidExpressionPosition::TypeAnnotation;
458 } else if type_params.is_some() {
459 visitor.position = InvalidExpressionPosition::GenericDefinition;
460 } else {
461 return;
462 }
463 for param in parameters
464 .iter()
465 .filter_map(ast::AnyParameterRef::annotation)
466 {
467 visitor.visit_expr(param);
468 }
469 if let Some(returns) = returns {
470 visitor.visit_expr(returns);
471 }
472 }
473 Stmt::ClassDef(ast::StmtClassDef {
474 type_params: Some(type_params),
475 arguments,
476 ..
477 }) => {
478 // test_ok valid_annotation_class
479 // class F(y := list): ...
480 // def f():
481 // class G((yield 1)): ...
482 // class H((yield from 1)): ...
483 // async def f():
484 // class G((await 1)): ...
485
486 // test_err invalid_annotation_class
487 // class F[T](y := list): ...
488 // class I[T]((yield 1)): ...
489 // class J[T]((yield from 1)): ...
490 // class K[T: (yield 1)]: ... # yield in TypeVar
491 // class L[T: (x := 1)]: ... # named expr in TypeVar
492 // class M[T]((await 1)): ...
493 // class N[T: (await 1)]: ...
494 let mut visitor = InvalidExpressionVisitor {
495 position: InvalidExpressionPosition::TypeAnnotation,
496 ctx,
497 };
498 visitor.visit_type_params(type_params);
499 if let Some(arguments) = arguments {
500 visitor.position = InvalidExpressionPosition::GenericDefinition;
501 visitor.visit_arguments(arguments);
502 }
503 }
504 Stmt::TypeAlias(ast::StmtTypeAlias {
505 type_params, value, ..
506 }) => {
507 // test_err invalid_annotation_type_alias
508 // type X[T: (yield 1)] = int # TypeVar bound
509 // type X[T = (yield 1)] = int # TypeVar default
510 // type X[*Ts = (yield 1)] = int # TypeVarTuple default
511 // type X[**Ts = (yield 1)] = int # ParamSpec default
512 // type Y = (yield 1) # yield in value
513 // type Y = (x := 1) # named expr in value
514 // type Y[T: (await 1)] = int # await in bound
515 // type Y = (await 1) # await in value
516 let mut visitor = InvalidExpressionVisitor {
517 position: InvalidExpressionPosition::TypeAlias,
518 ctx,
519 };
520 visitor.visit_expr(value);
521 if let Some(type_params) = type_params {
522 visitor.visit_type_params(type_params);
523 }
524 }
525 _ => {}
526 }
527 }
528
529 /// Emit a [`SemanticSyntaxErrorKind::InvalidStarExpression`] if `expr` is starred.
530 fn invalid_star_expression<Ctx: SemanticSyntaxContext>(expr: &Expr, ctx: &Ctx) {
531 // test_ok single_star_in_tuple
532 // def f(): yield (*x,)
533 // def f(): return (*x,)
534 // for _ in (*x,): ...
535 // for (*x,) in xs: ...
536 if expr.is_starred_expr() {
537 Self::add_error(
538 ctx,
539 SemanticSyntaxErrorKind::InvalidStarExpression,
540 expr.range(),
541 );
542 }
543 }
544
545 fn multiple_star_expression<Ctx: SemanticSyntaxContext>(
546 ctx: &Ctx,
547 expr_ctx: ExprContext,
548 elts: &[Expr],
549 range: TextRange,
550 ) {
551 if expr_ctx.is_store() {
552 let mut has_starred = false;
553 for elt in elts {
554 if elt.is_starred_expr() {
555 if has_starred {
556 // test_err multiple_starred_assignment_target
557 // (*a, *b) = (1, 2)
558 // [*a, *b] = (1, 2)
559 // (*a, *b, c) = (1, 2, 3)
560 // [*a, *b, c] = (1, 2, 3)
561 // (*a, *b, (*c, *d)) = (1, 2)
562
563 // test_ok multiple_starred_assignment_target
564 // (*a, b) = (1, 2)
565 // (*_, normed), *_ = [(1,), 2]
566 Self::add_error(
567 ctx,
568 SemanticSyntaxErrorKind::MultipleStarredExpressions,
569 range,
570 );
571 return;
572 }
573 has_starred = true;
574 }
575 }
576 }
577 }
578
579 /// Check for [`SemanticSyntaxErrorKind::WriteToDebug`] in `stmt`.
580 fn debug_shadowing<Ctx: SemanticSyntaxContext>(stmt: &ast::Stmt, ctx: &Ctx) {
581 match stmt {
582 Stmt::FunctionDef(ast::StmtFunctionDef {
583 name,
584 type_params,
585 parameters,
586 ..
587 }) => {
588 // test_err debug_shadow_function
589 // def __debug__(): ... # function name
590 // def f[__debug__](): ... # type parameter name
591 // def f(__debug__): ... # parameter name
592 Self::check_identifier(name, ctx);
593 if let Some(type_params) = type_params {
594 for type_param in type_params.iter() {
595 Self::check_identifier(type_param.name(), ctx);
596 }
597 }
598 for parameter in parameters {
599 Self::check_identifier(parameter.name(), ctx);
600 }
601 }
602 Stmt::ClassDef(ast::StmtClassDef {
603 name, type_params, ..
604 }) => {
605 // test_err debug_shadow_class
606 // class __debug__: ... # class name
607 // class C[__debug__]: ... # type parameter name
608 Self::check_identifier(name, ctx);
609 if let Some(type_params) = type_params {
610 for type_param in type_params.iter() {
611 Self::check_identifier(type_param.name(), ctx);
612 }
613 }
614 }
615 Stmt::TypeAlias(ast::StmtTypeAlias {
616 type_params: Some(type_params),
617 ..
618 }) => {
619 // test_err debug_shadow_type_alias
620 // type __debug__ = list[int] # visited as an Expr but still flagged
621 // type Debug[__debug__] = str
622 for type_param in type_params.iter() {
623 Self::check_identifier(type_param.name(), ctx);
624 }
625 }
626 Stmt::Import(ast::StmtImport { names, .. })
627 | Stmt::ImportFrom(ast::StmtImportFrom { names, .. }) => {
628 // test_err debug_shadow_import
629 // import __debug__
630 // import debug as __debug__
631 // from x import __debug__
632 // from x import debug as __debug__
633
634 // test_ok debug_rename_import
635 // import __debug__ as debug
636 // from __debug__ import Some
637 // from x import __debug__ as debug
638 for name in names {
639 match &name.asname {
640 Some(asname) => Self::check_identifier(asname, ctx),
641 None => Self::check_identifier(&name.name, ctx),
642 }
643 }
644 }
645 Stmt::Try(ast::StmtTry { handlers, .. }) => {
646 // test_err debug_shadow_try
647 // try: ...
648 // except Exception as __debug__: ...
649 for handler in handlers
650 .iter()
651 .filter_map(ast::ExceptHandler::as_except_handler)
652 {
653 if let Some(name) = &handler.name {
654 Self::check_identifier(name, ctx);
655 }
656 }
657 }
658 // test_err debug_shadow_with
659 // with open("foo.txt") as __debug__: ...
660 _ => {}
661 }
662 }
663
664 /// Check if `ident` is equal to `__debug__` and emit a
665 /// [`SemanticSyntaxErrorKind::WriteToDebug`] if so.
666 fn check_identifier<Ctx: SemanticSyntaxContext>(ident: &ast::Identifier, ctx: &Ctx) {
667 if ident.id == "__debug__" {
668 Self::add_error(
669 ctx,
670 SemanticSyntaxErrorKind::WriteToDebug(WriteToDebugKind::Store),
671 ident.range,
672 );
673 }
674 }
675
676 fn duplicate_type_parameter_name<Ctx: SemanticSyntaxContext>(
677 type_params: &ast::TypeParams,
678 ctx: &Ctx,
679 ) {
680 if type_params.len() < 2 {
681 return;
682 }
683
684 for (i, type_param) in type_params.iter().enumerate() {
685 if type_params
686 .iter()
687 .take(i)
688 .any(|t| t.name().id == type_param.name().id)
689 {
690 // test_ok non_duplicate_type_parameter_names
691 // type Alias[T] = list[T]
692 // def f[T](t: T): ...
693 // class C[T]: ...
694 // class C[T, U, V]: ...
695 // type Alias[T, U: str, V: (str, bytes), *Ts, **P, D = default] = ...
696
697 // test_err duplicate_type_parameter_names
698 // type Alias[T, T] = ...
699 // def f[T, T](t: T): ...
700 // class C[T, T]: ...
701 // type Alias[T, U: str, V: (str, bytes), *Ts, **P, T = default] = ...
702 // def f[T, T, T](): ... # two errors
703 // def f[T, *T](): ... # star is still duplicate
704 // def f[T, **T](): ... # as is double star
705 Self::add_error(
706 ctx,
707 SemanticSyntaxErrorKind::DuplicateTypeParameter,
708 type_param.range(),
709 );
710 }
711 }
712 }
713
714 fn type_parameter_default_order<Ctx: SemanticSyntaxContext>(
715 type_params: &ast::TypeParams,
716 ctx: &Ctx,
717 ) {
718 let mut seen_default = false;
719 for type_param in type_params {
720 let has_default = match type_param {
721 ast::TypeParam::TypeVar(ast::TypeParamTypeVar { default, .. })
722 | ast::TypeParam::TypeVarTuple(ast::TypeParamTypeVarTuple { default, .. })
723 | ast::TypeParam::ParamSpec(ast::TypeParamParamSpec { default, .. }) => {
724 default.is_some()
725 }
726 };
727
728 if seen_default && !has_default {
729 // test_err type_parameter_default_order
730 // class C[T = int, U]: ...
731 // class C[T1, T2 = int, T3, T4]: ...
732 // type Alias[T = int, U] = ...
733 Self::add_error(
734 ctx,
735 SemanticSyntaxErrorKind::TypeParameterDefaultOrder(
736 type_param.name().id.to_string(),
737 ),
738 type_param.range(),
739 );
740 }
741 if has_default {
742 seen_default = true;
743 }
744 }
745 }
746
747 fn duplicate_parameter_name<Ctx: SemanticSyntaxContext>(
748 parameters: &ast::Parameters,
749 ctx: &Ctx,
750 ) {
751 if parameters.len() < 2 {
752 return;
753 }
754
755 let mut all_arg_names =
756 FxHashSet::with_capacity_and_hasher(parameters.len(), FxBuildHasher);
757
758 for parameter in parameters {
759 let range = parameter.name().range();
760 let param_name = parameter.name().as_str();
761 if !all_arg_names.insert(param_name) {
762 // test_err params_duplicate_names
763 // def foo(a, a=10, *a, a, a: str, **a): ...
764 Self::add_error(
765 ctx,
766 SemanticSyntaxErrorKind::DuplicateParameter(param_name.to_string()),
767 range,
768 );
769 }
770 }
771 }
772
773 fn irrefutable_match_case<Ctx: SemanticSyntaxContext>(stmt: &ast::StmtMatch, ctx: &Ctx) {
774 // test_ok irrefutable_case_pattern_at_end
775 // match x:
776 // case 2: ...
777 // case var: ...
778 // match x:
779 // case 2: ...
780 // case _: ...
781 // match x:
782 // case var if True: ... # don't try to refute a guarded pattern
783 // case 2: ...
784
785 // test_err irrefutable_case_pattern
786 // match x:
787 // case var: ... # capture pattern
788 // case 2: ...
789 // match x:
790 // case _: ...
791 // case 2: ... # wildcard pattern
792 // match x:
793 // case var1 as var2: ... # as pattern with irrefutable left-hand side
794 // case 2: ...
795 // match x:
796 // case enum.variant | var: ... # or pattern with irrefutable part
797 // case 2: ...
798 for case in stmt
799 .cases
800 .iter()
801 .rev()
802 .skip(1)
803 .filter_map(|case| match case.guard {
804 Some(_) => None,
805 None => case.pattern.irrefutable_pattern(),
806 })
807 {
808 Self::add_error(
809 ctx,
810 SemanticSyntaxErrorKind::IrrefutableCasePattern(case.kind),
811 case.range,
812 );
813 }
814 }
815
816 /// Check `stmt` for semantic syntax errors and update the checker's internal state.
817 ///
818 /// Note that this method should only be called when traversing `stmt` *and* its children. For
819 /// example, if traversal of function bodies needs to be deferred, avoid calling `visit_stmt` on
820 /// the function itself until the deferred body is visited too. Failing to defer `visit_stmt` in
821 /// this case will break any internal state that depends on function scopes, such as `async`
822 /// context detection.
823 pub fn visit_stmt<Ctx: SemanticSyntaxContext>(&mut self, stmt: &ast::Stmt, ctx: &Ctx) {
824 // check for errors
825 self.check_stmt(stmt, ctx);
826
827 // update internal state
828 match stmt {
829 Stmt::Expr(StmtExpr { value, .. })
830 if !self.seen_module_docstring_boundary && value.is_string_literal_expr() => {}
831 Stmt::ImportFrom(StmtImportFrom {
832 module, is_lazy, ..
833 }) => {
834 // Allow eager `__future__` imports until we see any other import. Lazy imports,
835 // including `lazy from __future__ import ...`, always close the boundary.
836 if *is_lazy || !matches!(module.as_deref(), Some("__future__")) {
837 self.seen_futures_boundary = true;
838 }
839 }
840 Stmt::FunctionDef(StmtFunctionDef { is_async, body, .. }) => {
841 if *is_async {
842 let mut visitor = ReturnVisitor::default();
843 visitor.visit_body(body);
844
845 if visitor.has_yield {
846 if let Some(return_range) = visitor.return_range {
847 Self::add_error(
848 ctx,
849 SemanticSyntaxErrorKind::ReturnInGenerator,
850 return_range,
851 );
852 }
853 }
854 }
855 self.seen_futures_boundary = true;
856 }
857 _ => {
858 self.seen_futures_boundary = true;
859 }
860 }
861
862 self.seen_module_docstring_boundary = true;
863 }
864
865 /// Check `expr` for semantic syntax errors and update the checker's internal state.
866 pub fn visit_expr<Ctx: SemanticSyntaxContext>(&mut self, expr: &Expr, ctx: &Ctx) {
867 match expr {
868 Expr::ListComp(ast::ExprListComp {
869 elt, generators, ..
870 })
871 | Expr::SetComp(ast::ExprSetComp {
872 elt, generators, ..
873 }) => {
874 Self::check_generator_expr(elt, generators, ctx);
875 Self::async_comprehension_in_sync_comprehension(ctx, generators);
876 for generator in generators.iter().filter(|g| g.is_async) {
877 Self::await_outside_async_function(
878 ctx,
879 generator,
880 AwaitOutsideAsyncFunctionKind::AsyncComprehension,
881 );
882 }
883 }
884 Expr::DictComp(ast::ExprDictComp {
885 key,
886 value,
887 generators,
888 ..
889 }) => {
890 Self::check_generator_expr(key, generators, ctx);
891 Self::check_generator_expr(value, generators, ctx);
892 Self::async_comprehension_in_sync_comprehension(ctx, generators);
893 for generator in generators.iter().filter(|g| g.is_async) {
894 Self::await_outside_async_function(
895 ctx,
896 generator,
897 AwaitOutsideAsyncFunctionKind::AsyncComprehension,
898 );
899 }
900 }
901 Expr::Generator(ast::ExprGenerator {
902 elt, generators, ..
903 }) => {
904 Self::check_generator_expr(elt, generators, ctx);
905 // Note that `await_outside_async_function` is not called here because generators
906 // are evaluated lazily. See the note in the function for more details.
907 }
908 Expr::Name(ast::ExprName {
909 range,
910 id,
911 ctx: expr_ctx,
912 node_index: _,
913 }) => {
914 // test_err write_to_debug_expr
915 // del __debug__
916 // del x, y, __debug__, z
917 // __debug__ = 1
918 // x, y, __debug__, z = 1, 2, 3, 4
919
920 // test_err del_debug_py39
921 // # parse_options: {"target-version": "3.9"}
922 // del __debug__
923
924 // test_ok del_debug_py38
925 // # parse_options: {"target-version": "3.8"}
926 // del __debug__
927
928 // test_ok read_from_debug
929 // if __debug__: ...
930 // x = __debug__
931 if id == "__debug__" {
932 match expr_ctx {
933 ExprContext::Store => Self::add_error(
934 ctx,
935 SemanticSyntaxErrorKind::WriteToDebug(WriteToDebugKind::Store),
936 *range,
937 ),
938 ExprContext::Del => {
939 let version = ctx.python_version();
940 if version >= PythonVersion::PY39 {
941 Self::add_error(
942 ctx,
943 SemanticSyntaxErrorKind::WriteToDebug(
944 WriteToDebugKind::Delete(version),
945 ),
946 *range,
947 );
948 }
949 }
950 _ => {}
951 }
952 }
953
954 // PLE0118
955 if let Some(stmt) = ctx.global(id) {
956 let start = stmt.start();
957 if expr.start() < start {
958 Self::add_error(
959 ctx,
960 SemanticSyntaxErrorKind::LoadBeforeGlobalDeclaration {
961 name: id.to_string(),
962 start,
963 },
964 expr.range(),
965 );
966 }
967 }
968 }
969 Expr::Yield(ast::ExprYield { value, .. }) => {
970 if let Some(value) = value {
971 // test_err single_star_yield
972 // def f(): yield *x
973 Self::invalid_star_expression(value, ctx);
974 }
975 Self::yield_outside_function(ctx, expr, YieldOutsideFunctionKind::Yield);
976 }
977 Expr::YieldFrom(_) => {
978 Self::yield_outside_function(ctx, expr, YieldOutsideFunctionKind::YieldFrom);
979 if ctx.in_function_scope() && ctx.in_async_context() {
980 // test_err yield_from_in_async_function
981 // async def f(): yield from x
982
983 Self::add_error(
984 ctx,
985 SemanticSyntaxErrorKind::YieldFromInAsyncFunction,
986 expr.range(),
987 );
988 }
989 }
990 Expr::Await(_) => {
991 Self::yield_outside_function(ctx, expr, YieldOutsideFunctionKind::Await);
992 Self::await_outside_async_function(ctx, expr, AwaitOutsideAsyncFunctionKind::Await);
993 }
994 Expr::Tuple(ast::ExprTuple {
995 elts,
996 ctx: expr_ctx,
997 range,
998 ..
999 })
1000 | Expr::List(ast::ExprList {
1001 elts,
1002 ctx: expr_ctx,
1003 range,
1004 ..
1005 }) => {
1006 Self::multiple_star_expression(ctx, *expr_ctx, elts, *range);
1007 }
1008 Expr::Lambda(ast::ExprLambda {
1009 parameters: Some(parameters),
1010 ..
1011 }) => {
1012 Self::duplicate_parameter_name(parameters, ctx);
1013 }
1014 _ => {}
1015 }
1016 }
1017
1018 /// PLE1142
1019 fn await_outside_async_function<Ctx: SemanticSyntaxContext, Node: Ranged>(
1020 ctx: &Ctx,
1021 node: Node,
1022 kind: AwaitOutsideAsyncFunctionKind,
1023 ) {
1024 if ctx.in_async_context() {
1025 return;
1026 }
1027 // `await` is allowed at the top level of a Jupyter notebook.
1028 // See: https://ipython.readthedocs.io/en/stable/interactive/autoawait.html.
1029 if ctx.in_module_scope() && ctx.in_notebook() {
1030 return;
1031 }
1032 // Generators are evaluated lazily, so you can use `await` in them. For example:
1033 //
1034 // ```python
1035 // # This is valid
1036 // def f():
1037 // (await x for x in y)
1038 // (x async for x in y)
1039 //
1040 // # This is invalid
1041 // def f():
1042 // (x for x in await y)
1043 // [await x for x in y]
1044 // ```
1045 //
1046 // This check is required in addition to avoiding calling this function in `visit_expr`
1047 // because the generator scope applies to nested parts of the `Expr::Generator` that are
1048 // visited separately.
1049 if ctx.in_generator_context() {
1050 return;
1051 }
1052 Self::add_error(
1053 ctx,
1054 SemanticSyntaxErrorKind::AwaitOutsideAsyncFunction(kind),
1055 node.range(),
1056 );
1057 }
1058
1059 /// F704
1060 fn yield_outside_function<Ctx: SemanticSyntaxContext>(
1061 ctx: &Ctx,
1062 expr: &Expr,
1063 kind: YieldOutsideFunctionKind,
1064 ) {
1065 // We are intentionally not inspecting the async status of the scope for now to mimic F704.
1066 // await-outside-async is PLE1142 instead, so we'll end up emitting both syntax errors for
1067 // cases that trigger F704
1068
1069 if ctx.in_function_scope() {
1070 return;
1071 }
1072
1073 if kind.is_await() {
1074 // `await` is allowed at the top level of a Jupyter notebook.
1075 // See: https://ipython.readthedocs.io/en/stable/interactive/autoawait.html.
1076 if ctx.in_module_scope() && ctx.in_notebook() {
1077 return;
1078 }
1079 if ctx.in_await_allowed_context() {
1080 return;
1081 }
1082 } else if ctx.in_yield_allowed_context() {
1083 return;
1084 }
1085
1086 Self::add_error(
1087 ctx,
1088 SemanticSyntaxErrorKind::YieldOutsideFunction(kind),
1089 expr.range(),
1090 );
1091 }
1092
1093 /// Add a [`SyntaxErrorKind::ReboundComprehensionVariable`] if `expr` rebinds an iteration
1094 /// variable in `generators`.
1095 fn check_generator_expr<Ctx: SemanticSyntaxContext>(
1096 expr: &Expr,
1097 comprehensions: &[ast::Comprehension],
1098 ctx: &Ctx,
1099 ) {
1100 let rebound_variables = {
1101 let mut visitor = ReboundComprehensionVisitor {
1102 comprehensions,
1103 rebound_variables: Vec::new(),
1104 };
1105 visitor.visit_expr(expr);
1106 visitor.rebound_variables
1107 };
1108
1109 // TODO(brent) with multiple diagnostic ranges, we could mark both the named expr (current)
1110 // and the name expr being rebound
1111 for range in rebound_variables {
1112 // test_err rebound_comprehension_variable
1113 // [(a := 0) for a in range(0)]
1114 // {(a := 0) for a in range(0)}
1115 // {(a := 0): val for a in range(0)}
1116 // {key: (a := 0) for a in range(0)}
1117 // ((a := 0) for a in range(0))
1118 // [[(a := 0)] for a in range(0)]
1119 // [(a := 0) for b in range (0) for a in range(0)]
1120 // [(a := 0) for a in range (0) for b in range(0)]
1121 // [((a := 0), (b := 1)) for a in range (0) for b in range(0)]
1122
1123 // test_ok non_rebound_comprehension_variable
1124 // [a := 0 for x in range(0)]
1125 Self::add_error(
1126 ctx,
1127 SemanticSyntaxErrorKind::ReboundComprehensionVariable,
1128 range,
1129 );
1130 }
1131 }
1132
1133 fn async_comprehension_in_sync_comprehension<Ctx: SemanticSyntaxContext>(
1134 ctx: &Ctx,
1135 generators: &[ast::Comprehension],
1136 ) {
1137 let python_version = ctx.python_version();
1138 if python_version >= PythonVersion::PY311 {
1139 return;
1140 }
1141 // async allowed at notebook top-level
1142 if ctx.in_notebook() && ctx.in_module_scope() {
1143 return;
1144 }
1145 if !ctx.in_sync_comprehension() {
1146 return;
1147 }
1148 for generator in generators.iter().filter(|generator| generator.is_async) {
1149 // test_ok nested_async_comprehension_py311
1150 // # parse_options: {"target-version": "3.11"}
1151 // async def f(): return [[x async for x in foo(n)] for n in range(3)] # list
1152 // async def g(): return [{x: 1 async for x in foo(n)} for n in range(3)] # dict
1153 // async def h(): return [{x async for x in foo(n)} for n in range(3)] # set
1154
1155 // test_ok nested_async_comprehension_py310
1156 // # parse_options: {"target-version": "3.10"}
1157 // async def f():
1158 // [_ for n in range(3)]
1159 // [_ async for n in range(3)]
1160 // async def f():
1161 // def g(): ...
1162 // [_ async for n in range(3)]
1163
1164 // test_ok all_async_comprehension_py310
1165 // # parse_options: {"target-version": "3.10"}
1166 // async def test(): return [[x async for x in elements(n)] async for n in range(3)]
1167
1168 // test_err nested_async_comprehension_py310
1169 // # parse_options: {"target-version": "3.10"}
1170 // async def f(): return [[x async for x in foo(n)] for n in range(3)] # list
1171 // async def g(): return [{x: 1 async for x in foo(n)} for n in range(3)] # dict
1172 // async def h(): return [{x async for x in foo(n)} for n in range(3)] # set
1173 // async def i(): return [([y async for y in range(1)], [z for z in range(2)]) for x in range(5)]
1174 // async def j(): return [([y for y in range(1)], [z async for z in range(2)]) for x in range(5)]
1175 Self::add_error(
1176 ctx,
1177 SemanticSyntaxErrorKind::AsyncComprehensionInSyncComprehension(python_version),
1178 generator.range,
1179 );
1180 }
1181 }
1182}
1183
1184fn is_known_future_feature(name: &str) -> bool {
1185 matches!(
1186 name,
1187 "nested_scopes"
1188 | "generators"
1189 | "division"
1190 | "absolute_import"
1191 | "with_statement"
1192 | "print_function"
1193 | "unicode_literals"
1194 | "barry_as_FLUFL"
1195 | "generator_stop"
1196 | "annotations"
1197 )
1198}
1199
1200#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
1201pub enum LazyImportKind {
1202 Import,
1203 ImportFrom,
1204}
1205
1206#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
1207pub enum LazyImportContext {
1208 Function,
1209 Class,
1210 TryExceptBlocks,
1211}
1212
1213#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
1214pub struct SemanticSyntaxError {
1215 pub kind: SemanticSyntaxErrorKind,
1216 pub range: TextRange,
1217 pub python_version: PythonVersion,
1218}
1219
1220impl Display for SemanticSyntaxError {
1221 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1222 match &self.kind {
1223 SemanticSyntaxErrorKind::LateFutureImport => {
1224 f.write_str("__future__ imports must be at the top of the file")
1225 }
1226 SemanticSyntaxErrorKind::ReboundComprehensionVariable => {
1227 f.write_str("assignment expression cannot rebind comprehension variable")
1228 }
1229 SemanticSyntaxErrorKind::DuplicateTypeParameter => {
1230 f.write_str("duplicate type parameter")
1231 }
1232 SemanticSyntaxErrorKind::TypeParameterDefaultOrder(name) => {
1233 write!(
1234 f,
1235 "non default type parameter `{name}` follows default type parameter"
1236 )
1237 }
1238 SemanticSyntaxErrorKind::MultipleCaseAssignment(name) => {
1239 write!(f, "multiple assignments to name `{name}` in pattern")
1240 }
1241 SemanticSyntaxErrorKind::IrrefutableCasePattern(kind) => match kind {
1242 // These error messages are taken from CPython's syntax errors
1243 IrrefutablePatternKind::Name(name) => {
1244 write!(
1245 f,
1246 "name capture `{name}` makes remaining patterns unreachable"
1247 )
1248 }
1249 IrrefutablePatternKind::Wildcard => {
1250 f.write_str("wildcard makes remaining patterns unreachable")
1251 }
1252 },
1253 SemanticSyntaxErrorKind::SingleStarredAssignment => {
1254 f.write_str("starred assignment target must be in a list or tuple")
1255 }
1256 SemanticSyntaxErrorKind::WriteToDebug(kind) => match kind {
1257 WriteToDebugKind::Store => f.write_str("cannot assign to `__debug__`"),
1258 WriteToDebugKind::Delete(python_version) => {
1259 write!(
1260 f,
1261 "cannot delete `__debug__` on Python {python_version} (syntax was removed in 3.9)"
1262 )
1263 }
1264 },
1265 SemanticSyntaxErrorKind::InvalidExpression(kind, position) => {
1266 write!(f, "{kind} cannot be used within a {position}")
1267 }
1268 SemanticSyntaxErrorKind::DuplicateMatchKey(key) => {
1269 write!(
1270 f,
1271 "mapping pattern checks duplicate key `{}`",
1272 EscapeDefault(key)
1273 )
1274 }
1275 SemanticSyntaxErrorKind::DuplicateMatchClassAttribute(name) => {
1276 write!(f, "attribute name `{name}` repeated in class pattern",)
1277 }
1278 SemanticSyntaxErrorKind::LoadBeforeGlobalDeclaration { name, start: _ } => {
1279 write!(f, "name `{name}` is used prior to global declaration")
1280 }
1281 SemanticSyntaxErrorKind::LoadBeforeNonlocalDeclaration { name, start: _ } => {
1282 write!(f, "name `{name}` is used prior to nonlocal declaration")
1283 }
1284 SemanticSyntaxErrorKind::InvalidStarExpression => {
1285 f.write_str("Starred expression cannot be used here")
1286 }
1287 SemanticSyntaxErrorKind::AsyncComprehensionInSyncComprehension(python_version) => {
1288 write!(
1289 f,
1290 "cannot use an asynchronous comprehension inside of a synchronous comprehension \
1291 on Python {python_version} (syntax was added in 3.11)",
1292 )
1293 }
1294 SemanticSyntaxErrorKind::YieldOutsideFunction(kind) => {
1295 write!(f, "`{kind}` statement outside of a function")
1296 }
1297 SemanticSyntaxErrorKind::ReturnOutsideFunction => {
1298 f.write_str("`return` statement outside of a function")
1299 }
1300 SemanticSyntaxErrorKind::AwaitOutsideAsyncFunction(kind) => {
1301 write!(f, "{kind} outside of an asynchronous function")
1302 }
1303 SemanticSyntaxErrorKind::DuplicateParameter(name) => {
1304 write!(f, r#"Duplicate parameter "{name}""#)
1305 }
1306 SemanticSyntaxErrorKind::NonlocalDeclarationAtModuleLevel => {
1307 write!(f, "nonlocal declaration not allowed at module level")
1308 }
1309 SemanticSyntaxErrorKind::NonlocalAndGlobal(name) => {
1310 write!(f, "name `{name}` is nonlocal and global")
1311 }
1312 SemanticSyntaxErrorKind::AnnotatedGlobal(name) => {
1313 write!(f, "annotated name `{name}` can't be global")
1314 }
1315 SemanticSyntaxErrorKind::AnnotatedNonlocal(name) => {
1316 write!(f, "annotated name `{name}` can't be nonlocal")
1317 }
1318 SemanticSyntaxErrorKind::YieldFromInAsyncFunction => {
1319 f.write_str("`yield from` statement in async function; use `async for` instead")
1320 }
1321 SemanticSyntaxErrorKind::NonModuleImportStar(name) => {
1322 write!(f, "`from {name} import *` only allowed at module level")
1323 }
1324 SemanticSyntaxErrorKind::MultipleStarredExpressions => {
1325 write!(f, "Two starred expressions in assignment")
1326 }
1327 SemanticSyntaxErrorKind::FutureFeatureNotDefined(name) => {
1328 write!(f, "Future feature `{name}` is not defined")
1329 }
1330 SemanticSyntaxErrorKind::LazyImportNotAllowed { context, kind } => {
1331 let statement = match kind {
1332 LazyImportKind::Import => "lazy import",
1333 LazyImportKind::ImportFrom => "lazy from ... import",
1334 };
1335 let location = match context {
1336 LazyImportContext::Function => "functions",
1337 LazyImportContext::Class => "classes",
1338 LazyImportContext::TryExceptBlocks => "try/except blocks",
1339 };
1340 write!(f, "{statement} not allowed inside {location}")
1341 }
1342 SemanticSyntaxErrorKind::LazyImportStar => {
1343 f.write_str("lazy from ... import * is not allowed")
1344 }
1345 SemanticSyntaxErrorKind::LazyFutureImport => {
1346 f.write_str("lazy from __future__ import is not allowed")
1347 }
1348 SemanticSyntaxErrorKind::BreakOutsideLoop => f.write_str("`break` outside loop"),
1349 SemanticSyntaxErrorKind::ContinueOutsideLoop => f.write_str("`continue` outside loop"),
1350 SemanticSyntaxErrorKind::GlobalParameter(name) => {
1351 write!(f, "name `{name}` is parameter and global")
1352 }
1353 SemanticSyntaxErrorKind::DifferentMatchPatternBindings => {
1354 write!(f, "alternative patterns bind different names")
1355 }
1356 SemanticSyntaxErrorKind::NonlocalWithoutBinding(name) => {
1357 write!(f, "no binding for nonlocal `{name}` found")
1358 }
1359 SemanticSyntaxErrorKind::ReturnInGenerator => {
1360 write!(f, "`return` with value in async generator")
1361 }
1362 }
1363 }
1364}
1365
1366impl Ranged for SemanticSyntaxError {
1367 fn range(&self) -> TextRange {
1368 self.range
1369 }
1370}
1371
1372#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
1373pub enum SemanticSyntaxErrorKind {
1374 /// Represents a `lazy` import statement in an invalid context.
1375 LazyImportNotAllowed {
1376 context: LazyImportContext,
1377 kind: LazyImportKind,
1378 },
1379
1380 /// Represents the use of `lazy from ... import *`.
1381 LazyImportStar,
1382
1383 /// Represents the use of `lazy from __future__ import ...`.
1384 LazyFutureImport,
1385
1386 /// Represents the use of a `__future__` import after the beginning of a file.
1387 ///
1388 /// ## Examples
1389 ///
1390 /// ```python
1391 /// from pathlib import Path
1392 ///
1393 /// from __future__ import annotations
1394 /// ```
1395 ///
1396 /// This corresponds to the [`late-future-import`] (`F404`) rule in ruff.
1397 ///
1398 /// [`late-future-import`]: https://docs.astral.sh/ruff/rules/late-future-import/
1399 LateFutureImport,
1400
1401 /// Represents the rebinding of the iteration variable of a list, set, or dict comprehension or
1402 /// a generator expression.
1403 ///
1404 /// ## Examples
1405 ///
1406 /// ```python
1407 /// [(a := 0) for a in range(0)]
1408 /// {(a := 0) for a in range(0)}
1409 /// {(a := 0): val for a in range(0)}
1410 /// {key: (a := 0) for a in range(0)}
1411 /// ((a := 0) for a in range(0))
1412 /// ```
1413 ReboundComprehensionVariable,
1414
1415 /// Represents a duplicate type parameter name in a function definition, class definition, or
1416 /// type alias statement.
1417 ///
1418 /// ## Examples
1419 ///
1420 /// ```python
1421 /// type Alias[T, T] = ...
1422 /// def f[T, T](t: T): ...
1423 /// class C[T, T]: ...
1424 /// ```
1425 DuplicateTypeParameter,
1426
1427 /// Represents a duplicate binding in a `case` pattern of a `match` statement.
1428 ///
1429 /// ## Examples
1430 ///
1431 /// ```python
1432 /// match x:
1433 /// case [x, y, x]: ...
1434 /// case x as x: ...
1435 /// case Class(x=1, x=2): ...
1436 /// ```
1437 MultipleCaseAssignment(ast::name::Name),
1438
1439 /// Represents an irrefutable `case` pattern before the last `case` in a `match` statement.
1440 ///
1441 /// According to the [Python reference], "a match statement may have at most one irrefutable
1442 /// case block, and it must be last."
1443 ///
1444 /// ## Examples
1445 ///
1446 /// ```python
1447 /// match x:
1448 /// case value: ... # irrefutable capture pattern
1449 /// case other: ...
1450 ///
1451 /// match x:
1452 /// case _: ... # irrefutable wildcard pattern
1453 /// case other: ...
1454 /// ```
1455 ///
1456 /// [Python reference]: https://docs.python.org/3/reference/compound_stmts.html#irrefutable-case-blocks
1457 IrrefutableCasePattern(IrrefutablePatternKind),
1458
1459 /// Represents a single starred assignment target outside of a tuple or list.
1460 ///
1461 /// ## Examples
1462 ///
1463 /// ```python
1464 /// *a = (1,) # SyntaxError
1465 /// ```
1466 ///
1467 /// A starred assignment target can only occur within a tuple or list:
1468 ///
1469 /// ```python
1470 /// b, *a = 1, 2, 3
1471 /// (*a,) = 1, 2, 3
1472 /// [*a] = 1, 2, 3
1473 /// ```
1474 SingleStarredAssignment,
1475
1476 /// Represents a write to `__debug__`. This includes simple assignments and deletions as well
1477 /// other kinds of statements that can introduce bindings, such as type parameters in functions,
1478 /// classes, and aliases, `match` arms, and imports, among others.
1479 ///
1480 /// ## Examples
1481 ///
1482 /// ```python
1483 /// del __debug__
1484 /// __debug__ = False
1485 /// def f(__debug__): ...
1486 /// class C[__debug__]: ...
1487 /// ```
1488 ///
1489 /// See [BPO 45000] for more information.
1490 ///
1491 /// [BPO 45000]: https://github.com/python/cpython/issues/89163
1492 WriteToDebug(WriteToDebugKind),
1493
1494 /// Represents the use of an invalid expression kind in one of several locations.
1495 ///
1496 /// The kinds include `yield` and `yield from` expressions and named expressions, and locations
1497 /// include type parameter bounds and defaults, type annotations, type aliases, and base class
1498 /// lists.
1499 ///
1500 /// ## Examples
1501 ///
1502 /// ```python
1503 /// type X[T: (yield 1)] = int
1504 /// type Y = (yield 1)
1505 /// def f[T](x: int) -> (y := 3): return x
1506 /// ```
1507 InvalidExpression(InvalidExpressionKind, InvalidExpressionPosition),
1508
1509 /// Represents a duplicate key in a `match` mapping pattern.
1510 ///
1511 /// The [CPython grammar] allows keys in mapping patterns to be literals or attribute accesses:
1512 ///
1513 /// ```text
1514 /// key_value_pattern:
1515 /// | (literal_expr | attr) ':' pattern
1516 /// ```
1517 ///
1518 /// But only literals are checked for duplicates:
1519 ///
1520 /// ```pycon
1521 /// >>> match x:
1522 /// ... case {"x": 1, "x": 2}: ...
1523 /// ...
1524 /// File "<python-input-160>", line 2
1525 /// case {"x": 1, "x": 2}: ...
1526 /// ^^^^^^^^^^^^^^^^
1527 /// SyntaxError: mapping pattern checks duplicate key ('x')
1528 /// >>> match x:
1529 /// ... case {x.a: 1, x.a: 2}: ...
1530 /// ...
1531 /// >>>
1532 /// ```
1533 ///
1534 /// ## Examples
1535 ///
1536 /// ```python
1537 /// match x:
1538 /// case {"x": 1, "x": 2}: ...
1539 /// ```
1540 ///
1541 /// [CPython grammar]: https://docs.python.org/3/reference/grammar.html
1542 DuplicateMatchKey(String),
1543
1544 /// Represents a duplicate attribute name in a `match` class pattern.
1545 ///
1546 /// ## Examples
1547 ///
1548 /// ```python
1549 /// match x:
1550 /// case Class(x=1, x=2): ...
1551 /// ```
1552 DuplicateMatchClassAttribute(ast::name::Name),
1553
1554 /// Represents the use of a `global` variable before its `global` declaration.
1555 ///
1556 /// ## Examples
1557 ///
1558 /// ```python
1559 /// counter = 1
1560 /// def increment():
1561 /// print(f"Adding 1 to {counter}")
1562 /// global counter
1563 /// counter += 1
1564 /// ```
1565 ///
1566 /// ## Known Issues
1567 ///
1568 /// Note that the order in which the parts of a `try` statement are visited was changed in 3.13,
1569 /// as tracked in Python issue [#111123]. For example, this code was valid on Python 3.12:
1570 ///
1571 /// ```python
1572 /// a = 10
1573 /// def g():
1574 /// try:
1575 /// 1 / 0
1576 /// except:
1577 /// a = 1
1578 /// else:
1579 /// global a
1580 /// ```
1581 ///
1582 /// While this more intuitive behavior aligned with the textual order was a syntax error:
1583 ///
1584 /// ```python
1585 /// a = 10
1586 /// def f():
1587 /// try:
1588 /// pass
1589 /// except:
1590 /// global a
1591 /// else:
1592 /// a = 1 # SyntaxError: name 'a' is assigned to before global declaration
1593 /// ```
1594 ///
1595 /// This was reversed in version 3.13 to make the second case valid and the first case a syntax
1596 /// error. We intentionally enforce the 3.13 ordering, regardless of the Python version, which
1597 /// will lead to both false positives and false negatives on 3.12 code that takes advantage of
1598 /// the old behavior. However, as mentioned in the Python issue, we expect code relying on this
1599 /// to be very rare and not worth the additional complexity to detect.
1600 ///
1601 /// [#111123]: https://github.com/python/cpython/issues/111123
1602 LoadBeforeGlobalDeclaration { name: String, start: TextSize },
1603
1604 /// Represents the use of a `nonlocal` variable before its `nonlocal` declaration.
1605 ///
1606 /// ## Examples
1607 ///
1608 /// ```python
1609 /// def f():
1610 /// counter = 0
1611 /// def increment():
1612 /// print(f"Adding 1 to {counter}")
1613 /// nonlocal counter # SyntaxError: name 'counter' is used prior to nonlocal declaration
1614 /// counter += 1
1615 /// ```
1616 ///
1617 /// ## Known Issues
1618 ///
1619 /// See [`LoadBeforeGlobalDeclaration`][Self::LoadBeforeGlobalDeclaration].
1620 LoadBeforeNonlocalDeclaration { name: String, start: TextSize },
1621
1622 /// Represents the use of a starred expression in an invalid location, such as a `return` or
1623 /// `yield` statement.
1624 ///
1625 /// ## Examples
1626 ///
1627 /// ```python
1628 /// def f(): return *x
1629 /// def f(): yield *x
1630 /// for _ in *x: ...
1631 /// for *x in xs: ...
1632 /// ```
1633 InvalidStarExpression,
1634
1635 /// Represents the use of an asynchronous comprehension inside of a synchronous comprehension
1636 /// before Python 3.11.
1637 ///
1638 /// ## Examples
1639 ///
1640 /// Before Python 3.11, code like this produces a syntax error because of the implicit function
1641 /// scope introduced by the outer comprehension:
1642 ///
1643 /// ```python
1644 /// async def elements(n): yield n
1645 ///
1646 /// async def test(): return { n: [x async for x in elements(n)] for n in range(3)}
1647 /// ```
1648 ///
1649 /// This was discussed in [BPO 33346] and fixed in Python 3.11.
1650 ///
1651 /// [BPO 33346]: https://github.com/python/cpython/issues/77527
1652 AsyncComprehensionInSyncComprehension(PythonVersion),
1653
1654 /// Represents the use of `yield`, `yield from`, or `await` outside of a function scope.
1655 ///
1656 ///
1657 /// ## Examples
1658 ///
1659 /// `yield` and `yield from` are only allowed if the immediately-enclosing scope is a function
1660 /// or lambda and not allowed otherwise:
1661 ///
1662 /// ```python
1663 /// yield 1 # error
1664 ///
1665 /// def f():
1666 /// [(yield 1) for x in y] # error
1667 /// ```
1668 ///
1669 /// `await` is additionally allowed in comprehensions, if the comprehension itself is in a
1670 /// function scope:
1671 ///
1672 /// ```python
1673 /// await 1 # error
1674 ///
1675 /// async def f():
1676 /// await 1 # okay
1677 /// [await 1 for x in y] # also okay
1678 /// ```
1679 ///
1680 /// This last case _is_ an error, but it has to do with the lambda not being an async function.
1681 /// For the sake of this error kind, this is okay.
1682 ///
1683 /// ## References
1684 ///
1685 /// See [PEP 255] for details on `yield`, [PEP 380] for the extension to `yield from`, [PEP 492]
1686 /// for async-await syntax, and [PEP 530] for async comprehensions.
1687 ///
1688 /// [PEP 255]: https://peps.python.org/pep-0255/
1689 /// [PEP 380]: https://peps.python.org/pep-0380/
1690 /// [PEP 492]: https://peps.python.org/pep-0492/
1691 /// [PEP 530]: https://peps.python.org/pep-0530/
1692 YieldOutsideFunction(YieldOutsideFunctionKind),
1693
1694 /// Represents the use of `return` outside of a function scope.
1695 ReturnOutsideFunction,
1696
1697 /// Represents the use of `await`, `async for`, or `async with` outside of an asynchronous
1698 /// function.
1699 ///
1700 /// ## Examples
1701 ///
1702 /// ```python
1703 /// def f():
1704 /// await 1 # error
1705 /// async for x in y: ... # error
1706 /// async with x: ... # error
1707 /// ```
1708 AwaitOutsideAsyncFunction(AwaitOutsideAsyncFunctionKind),
1709
1710 /// Represents a duplicate parameter name in a function or lambda expression.
1711 ///
1712 /// ## Examples
1713 ///
1714 /// ```python
1715 /// def f(x, x): ...
1716 /// lambda x, x: ...
1717 /// ```
1718 DuplicateParameter(String),
1719
1720 /// Represents a nonlocal declaration at module level
1721 NonlocalDeclarationAtModuleLevel,
1722
1723 /// Represents the same variable declared as both nonlocal and global
1724 NonlocalAndGlobal(String),
1725
1726 /// Represents a type annotation on a variable that's been declared global
1727 AnnotatedGlobal(String),
1728
1729 /// Represents a type annotation on a variable that's been declared nonlocal
1730 AnnotatedNonlocal(String),
1731
1732 /// Represents the use of `yield from` inside an asynchronous function.
1733 YieldFromInAsyncFunction,
1734
1735 /// Represents the use of `from <module> import *` outside module scope.
1736 NonModuleImportStar(String),
1737
1738 /// Represents the use of more than one starred expression in an assignment.
1739 ///
1740 /// Python only allows a single starred target when unpacking values on the
1741 /// left-hand side of an assignment. Using multiple starred expressions makes
1742 /// the statement invalid and results in a `SyntaxError`.
1743 MultipleStarredExpressions,
1744
1745 /// Represents the use of a `__future__` feature that is not defined.
1746 FutureFeatureNotDefined(String),
1747
1748 /// Represents the use of a `break` statement outside of a loop.
1749 BreakOutsideLoop,
1750
1751 /// Represents the use of a `continue` statement outside of a loop.
1752 ContinueOutsideLoop,
1753
1754 /// Represents a function parameter that is also declared as `global`.
1755 ///
1756 /// Declaring a parameter as `global` is invalid, since parameters are already
1757 /// bound in the local scope of the function. Using `global` on them introduces
1758 /// ambiguity and will result in a `SyntaxError`.
1759 GlobalParameter(String),
1760
1761 /// Represents the use of alternative patterns in a `match` statement that bind different names.
1762 ///
1763 /// Python requires all alternatives in an OR pattern (`|`) to bind the same set of names.
1764 /// Using different names results in a `SyntaxError`.
1765 ///
1766 /// ## Example:
1767 ///
1768 /// ```python
1769 /// match 5:
1770 /// case [x] | [y]: # error
1771 /// ...
1772 /// ```
1773 DifferentMatchPatternBindings,
1774
1775 /// Represents a nonlocal statement for a name that has no binding in an enclosing scope.
1776 NonlocalWithoutBinding(String),
1777
1778 /// Represents a default type parameter followed by a non-default type parameter.
1779 TypeParameterDefaultOrder(String),
1780
1781 /// Represents a `return` statement with a value in an asynchronous generator.
1782 ReturnInGenerator,
1783}
1784
1785#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
1786pub enum AwaitOutsideAsyncFunctionKind {
1787 Await,
1788 AsyncFor,
1789 AsyncWith,
1790 AsyncComprehension,
1791}
1792
1793impl Display for AwaitOutsideAsyncFunctionKind {
1794 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1795 f.write_str(match self {
1796 AwaitOutsideAsyncFunctionKind::Await => "`await`",
1797 AwaitOutsideAsyncFunctionKind::AsyncFor => "`async for`",
1798 AwaitOutsideAsyncFunctionKind::AsyncWith => "`async with`",
1799 AwaitOutsideAsyncFunctionKind::AsyncComprehension => "asynchronous comprehension",
1800 })
1801 }
1802}
1803
1804#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
1805pub enum YieldOutsideFunctionKind {
1806 Yield,
1807 YieldFrom,
1808 Await,
1809}
1810
1811impl YieldOutsideFunctionKind {
1812 pub fn is_await(&self) -> bool {
1813 matches!(self, Self::Await)
1814 }
1815}
1816
1817impl Display for YieldOutsideFunctionKind {
1818 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1819 f.write_str(match self {
1820 YieldOutsideFunctionKind::Yield => "yield",
1821 YieldOutsideFunctionKind::YieldFrom => "yield from",
1822 YieldOutsideFunctionKind::Await => "await",
1823 })
1824 }
1825}
1826
1827#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, get_size2::GetSize)]
1828pub enum InvalidExpressionPosition {
1829 TypeVarBound,
1830 TypeVarDefault,
1831 TypeVarTupleDefault,
1832 ParamSpecDefault,
1833 TypeAnnotation,
1834 GenericDefinition,
1835 TypeAlias,
1836}
1837
1838impl Display for InvalidExpressionPosition {
1839 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1840 f.write_str(match self {
1841 InvalidExpressionPosition::TypeVarBound => "TypeVar bound",
1842 InvalidExpressionPosition::TypeVarDefault => "TypeVar default",
1843 InvalidExpressionPosition::TypeVarTupleDefault => "TypeVarTuple default",
1844 InvalidExpressionPosition::ParamSpecDefault => "ParamSpec default",
1845 InvalidExpressionPosition::TypeAnnotation => "type annotation",
1846 InvalidExpressionPosition::GenericDefinition => "generic definition",
1847 InvalidExpressionPosition::TypeAlias => "type alias",
1848 })
1849 }
1850}
1851
1852#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
1853pub enum InvalidExpressionKind {
1854 Yield,
1855 NamedExpr,
1856 Await,
1857}
1858
1859impl Display for InvalidExpressionKind {
1860 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1861 f.write_str(match self {
1862 InvalidExpressionKind::Yield => "yield expression",
1863 InvalidExpressionKind::NamedExpr => "named expression",
1864 InvalidExpressionKind::Await => "await expression",
1865 })
1866 }
1867}
1868
1869#[derive(Debug, Clone, PartialEq, Eq, Hash, get_size2::GetSize)]
1870pub enum WriteToDebugKind {
1871 Store,
1872 Delete(PythonVersion),
1873}
1874
1875/// Searches for the first named expression (`x := y`) rebinding one of the `iteration_variables` in
1876/// a comprehension or generator expression.
1877struct ReboundComprehensionVisitor<'a> {
1878 comprehensions: &'a [ast::Comprehension],
1879 rebound_variables: Vec<TextRange>,
1880}
1881
1882impl Visitor<'_> for ReboundComprehensionVisitor<'_> {
1883 fn visit_expr(&mut self, expr: &Expr) {
1884 if let Expr::Named(ast::ExprNamed { target, .. }) = expr {
1885 if let Expr::Name(ast::ExprName { id, range, .. }) = &**target {
1886 if self.comprehensions.iter().any(|comp| {
1887 comp.target
1888 .as_name_expr()
1889 .is_some_and(|name| name.id == *id)
1890 }) {
1891 self.rebound_variables.push(*range);
1892 }
1893 }
1894 }
1895 walk_expr(self, expr);
1896 }
1897}
1898
1899#[derive(Default)]
1900struct ReturnVisitor {
1901 return_range: Option<TextRange>,
1902 has_yield: bool,
1903}
1904
1905impl Visitor<'_> for ReturnVisitor {
1906 fn visit_stmt(&mut self, stmt: &Stmt) {
1907 match stmt {
1908 // Do not recurse into nested functions; they're evaluated separately.
1909 Stmt::FunctionDef(_) | Stmt::ClassDef(_) => {}
1910 Stmt::Return(ast::StmtReturn {
1911 value: Some(_),
1912 range,
1913 ..
1914 }) => {
1915 self.return_range = Some(*range);
1916 walk_stmt(self, stmt);
1917 }
1918 _ => walk_stmt(self, stmt),
1919 }
1920 }
1921
1922 fn visit_expr(&mut self, expr: &Expr) {
1923 match expr {
1924 Expr::Lambda(_) => {}
1925 Expr::Yield(_) | Expr::YieldFrom(_) => {
1926 self.has_yield = true;
1927 }
1928 _ => walk_expr(self, expr),
1929 }
1930 }
1931}
1932
1933struct MatchPatternVisitor<'a, Ctx> {
1934 names: FxHashSet<&'a ast::name::Name>,
1935 ctx: &'a Ctx,
1936}
1937
1938impl<'a, Ctx: SemanticSyntaxContext> MatchPatternVisitor<'a, Ctx> {
1939 fn visit_pattern(&mut self, pattern: &'a Pattern) {
1940 // test_ok class_keyword_in_case_pattern
1941 // match 2:
1942 // case Class(x=x): ...
1943
1944 // test_err multiple_assignment_in_case_pattern
1945 // match 2:
1946 // case [y, z, y]: ... # MatchSequence
1947 // case [y, z, *y]: ... # MatchSequence
1948 // case [y, y, y]: ... # MatchSequence multiple
1949 // case {1: x, 2: x}: ... # MatchMapping duplicate pattern
1950 // case {1: x, **x}: ... # MatchMapping duplicate in **rest
1951 // case Class(x, x): ... # MatchClass positional
1952 // case Class(y=x, z=x): ... # MatchClass keyword
1953 // case [x] | {1: x} | Class(y=x, z=x): ... # MatchOr
1954 // case x as x: ... # MatchAs
1955 match pattern {
1956 Pattern::MatchValue(_) | Pattern::MatchSingleton(_) => {}
1957 Pattern::MatchStar(ast::PatternMatchStar { name, .. }) => {
1958 if let Some(name) = name {
1959 self.insert(name);
1960 }
1961 }
1962 Pattern::MatchSequence(ast::PatternMatchSequence { patterns, .. }) => {
1963 for pattern in patterns {
1964 self.visit_pattern(pattern);
1965 }
1966 }
1967 Pattern::MatchMapping(ast::PatternMatchMapping {
1968 keys,
1969 patterns,
1970 rest,
1971 ..
1972 }) => {
1973 for pattern in patterns {
1974 self.visit_pattern(pattern);
1975 }
1976 if let Some(rest) = rest {
1977 self.insert(rest);
1978 }
1979
1980 let mut seen = FxHashSet::default();
1981 for key in keys
1982 .iter()
1983 // complex numbers (`1 + 2j`) are allowed as keys but are not literals
1984 // because they are represented as a `BinOp::Add` between a real number and
1985 // an imaginary number
1986 .filter(|key| key.is_literal_expr() || key.is_bin_op_expr())
1987 {
1988 if !seen.insert(ComparableExpr::from(key)) {
1989 let key_range = key.range();
1990 let duplicate_key = self.ctx.source()[key_range].to_string();
1991 // test_ok duplicate_match_key_attr
1992 // match x:
1993 // case {x.a: 1, x.a: 2}: ...
1994
1995 // test_err duplicate_match_key
1996 // match x:
1997 // case {"x": 1, "x": 2}: ...
1998 // case {b"x": 1, b"x": 2}: ...
1999 // case {0: 1, 0: 2}: ...
2000 // case {1.0: 1, 1.0: 2}: ...
2001 // case {1.0 + 2j: 1, 1.0 + 2j: 2}: ...
2002 // case {True: 1, True: 2}: ...
2003 // case {None: 1, None: 2}: ...
2004 // case {
2005 // """x
2006 // y
2007 // z
2008 // """: 1,
2009 // """x
2010 // y
2011 // z
2012 // """: 2}: ...
2013 // case {"x": 1, "x": 2, "x": 3}: ...
2014 // case {0: 1, "x": 1, 0: 2, "x": 2}: ...
2015 // case [{"x": 1, "x": 2}]: ...
2016 // case Foo(x=1, y={"x": 1, "x": 2}): ...
2017 // case [Foo(x=1), Foo(x=1, y={"x": 1, "x": 2})]: ...
2018 SemanticSyntaxChecker::add_error(
2019 self.ctx,
2020 SemanticSyntaxErrorKind::DuplicateMatchKey(duplicate_key),
2021 key_range,
2022 );
2023 }
2024 }
2025 }
2026 Pattern::MatchClass(ast::PatternMatchClass { arguments, .. }) => {
2027 for pattern in &arguments.patterns {
2028 self.visit_pattern(pattern);
2029 }
2030 let mut seen = FxHashSet::default();
2031 for keyword in &arguments.keywords {
2032 if !seen.insert(&keyword.attr.id) {
2033 // test_err duplicate_match_class_attr
2034 // match x:
2035 // case Class(x=1, x=2): ...
2036 // case [Class(x=1, x=2)]: ...
2037 // case {"x": x, "y": Foo(x=1, x=2)}: ...
2038 // case [{}, {"x": x, "y": Foo(x=1, x=2)}]: ...
2039 // case Class(x=1, d={"x": 1, "x": 2}, other=Class(x=1, x=2)): ...
2040 SemanticSyntaxChecker::add_error(
2041 self.ctx,
2042 SemanticSyntaxErrorKind::DuplicateMatchClassAttribute(
2043 keyword.attr.id.clone(),
2044 ),
2045 keyword.attr.range,
2046 );
2047 }
2048 self.visit_pattern(&keyword.pattern);
2049 }
2050 }
2051 Pattern::MatchAs(ast::PatternMatchAs { pattern, name, .. }) => {
2052 if let Some(pattern) = pattern {
2053 self.visit_pattern(pattern);
2054 }
2055 if let Some(name) = name {
2056 self.insert(name);
2057 }
2058 }
2059 Pattern::MatchOr(ast::PatternMatchOr {
2060 patterns, range, ..
2061 }) => {
2062 // each of these patterns should be visited separately because patterns can only be
2063 // duplicated within a single arm of the or pattern. For example, the case below is
2064 // a valid pattern.
2065
2066 // test_ok multiple_assignment_in_case_pattern
2067 // match 2:
2068 // case Class(x) | [x] | x: ...
2069
2070 let mut previous_names: Option<FxHashSet<&ast::name::Name>> = None;
2071 for pattern in patterns {
2072 let mut visitor = Self {
2073 names: FxHashSet::default(),
2074 ctx: self.ctx,
2075 };
2076 visitor.visit_pattern(pattern);
2077 let Some(prev) = &previous_names else {
2078 previous_names = Some(visitor.names);
2079 continue;
2080 };
2081 if prev.symmetric_difference(&visitor.names).next().is_some() {
2082 // test_err different_match_pattern_bindings
2083 // match x:
2084 // case [a] | [b]: ...
2085 // case [a] | []: ...
2086 // case (x, y) | (x,): ...
2087 // case [a, _] | [a, b]: ...
2088 // case (x, (y | z)): ...
2089 // case [a] | [b] | [c]: ...
2090 // case [] | [a]: ...
2091 // case [a] | [C(x)]: ...
2092 // case [[a] | [b]]: ...
2093 // case [C(a)] | [C(b)]: ...
2094 // case [C(D(a))] | [C(D(b))]: ...
2095 // case [(a, b)] | [(c, d)]: ...
2096
2097 // test_ok different_match_pattern_bindings
2098 // match x:
2099 // case [a] | [a]: ...
2100 // case (x, y) | (x, y): ...
2101 // case (x, (y | y)): ...
2102 // case [a, _] | [a, _]: ...
2103 // case [a] | [C(a)]: ...
2104
2105 // test_ok nested_alternative_patterns
2106 // match ruff:
2107 // case {"lint": {"select": x} | {"extend-select": x}} | {"select": x}:
2108 // ...
2109 // match 42:
2110 // case [[x] | [x]] | x: ...
2111 // match 42:
2112 // case [[x | x] | [x]] | x: ...
2113 // match 42:
2114 // case ast.Subscript(n, ast.Constant() | ast.Slice()) | ast.Attribute(n): ...
2115 SemanticSyntaxChecker::add_error(
2116 self.ctx,
2117 SemanticSyntaxErrorKind::DifferentMatchPatternBindings,
2118 *range,
2119 );
2120 break;
2121 }
2122 self.names.extend(visitor.names);
2123 }
2124 }
2125 }
2126 }
2127
2128 /// Add an identifier to the set of visited names in `self` and emit a [`SemanticSyntaxError`]
2129 /// if `ident` has already been seen.
2130 fn insert(&mut self, ident: &'a ast::Identifier) {
2131 if !self.names.insert(&ident.id) {
2132 SemanticSyntaxChecker::add_error(
2133 self.ctx,
2134 SemanticSyntaxErrorKind::MultipleCaseAssignment(ident.id.clone()),
2135 ident.range(),
2136 );
2137 }
2138 // test_err debug_shadow_match
2139 // match x:
2140 // case __debug__: ...
2141 SemanticSyntaxChecker::check_identifier(ident, self.ctx);
2142 }
2143}
2144
2145struct InvalidExpressionVisitor<'a, Ctx> {
2146 /// Context used for emitting errors.
2147 ctx: &'a Ctx,
2148
2149 position: InvalidExpressionPosition,
2150}
2151
2152impl<Ctx> Visitor<'_> for InvalidExpressionVisitor<'_, Ctx>
2153where
2154 Ctx: SemanticSyntaxContext,
2155{
2156 fn visit_expr(&mut self, expr: &Expr) {
2157 match expr {
2158 Expr::Named(ast::ExprNamed { range, .. }) => {
2159 SemanticSyntaxChecker::add_error(
2160 self.ctx,
2161 SemanticSyntaxErrorKind::InvalidExpression(
2162 InvalidExpressionKind::NamedExpr,
2163 self.position,
2164 ),
2165 *range,
2166 );
2167 }
2168 Expr::Yield(ast::ExprYield { range, .. })
2169 | Expr::YieldFrom(ast::ExprYieldFrom { range, .. }) => {
2170 SemanticSyntaxChecker::add_error(
2171 self.ctx,
2172 SemanticSyntaxErrorKind::InvalidExpression(
2173 InvalidExpressionKind::Yield,
2174 self.position,
2175 ),
2176 *range,
2177 );
2178 }
2179 Expr::Await(ast::ExprAwait { range, .. }) => {
2180 SemanticSyntaxChecker::add_error(
2181 self.ctx,
2182 SemanticSyntaxErrorKind::InvalidExpression(
2183 InvalidExpressionKind::Await,
2184 self.position,
2185 ),
2186 *range,
2187 );
2188 }
2189 _ => {}
2190 }
2191 ast::visitor::walk_expr(self, expr);
2192 }
2193
2194 fn visit_type_param(&mut self, type_param: &ast::TypeParam) {
2195 match type_param {
2196 ast::TypeParam::TypeVar(ast::TypeParamTypeVar { bound, default, .. }) => {
2197 if let Some(expr) = bound {
2198 self.position = InvalidExpressionPosition::TypeVarBound;
2199 self.visit_expr(expr);
2200 }
2201 if let Some(expr) = default {
2202 self.position = InvalidExpressionPosition::TypeVarDefault;
2203 self.visit_expr(expr);
2204 }
2205 }
2206 ast::TypeParam::TypeVarTuple(ast::TypeParamTypeVarTuple { default, .. }) => {
2207 if let Some(expr) = default {
2208 self.position = InvalidExpressionPosition::TypeVarTupleDefault;
2209 self.visit_expr(expr);
2210 }
2211 }
2212 ast::TypeParam::ParamSpec(ast::TypeParamParamSpec { default, .. }) => {
2213 if let Some(expr) = default {
2214 self.position = InvalidExpressionPosition::ParamSpecDefault;
2215 self.visit_expr(expr);
2216 }
2217 }
2218 }
2219 }
2220}
2221
2222/// Information needed from a parent visitor to emit semantic syntax errors.
2223///
2224/// Note that the `in_*_scope` methods should refer to the immediately-enclosing scope. For example,
2225/// `in_function_scope` should return true for this case:
2226///
2227/// ```python
2228/// def f():
2229/// x # here
2230/// ```
2231///
2232/// but not for this case:
2233///
2234/// ```python
2235/// def f():
2236/// class C:
2237/// x # here
2238/// ```
2239///
2240/// In contrast, the `in_*_context` methods should traverse parent scopes. For example,
2241/// `in_function_context` should return true for this case:
2242///
2243/// ```python
2244/// def f():
2245/// [x # here
2246/// for x in range(3)]
2247/// ```
2248///
2249/// but not here:
2250///
2251/// ```python
2252/// def f():
2253/// class C:
2254/// x # here, classes break function scopes
2255/// ```
2256pub trait SemanticSyntaxContext {
2257 /// Returns `true` if `__future__`-style type annotations are enabled.
2258 fn future_annotations_or_stub(&self) -> bool;
2259
2260 /// Returns the nearest invalid context for a `lazy` import statement, if any.
2261 ///
2262 /// This should return the innermost relevant restriction in order of precedence:
2263 /// function, class, then `try`/`except`.
2264 fn lazy_import_context(&self) -> Option<LazyImportContext>;
2265
2266 /// The target Python version for detecting backwards-incompatible syntax changes.
2267 fn python_version(&self) -> PythonVersion;
2268
2269 /// Returns the source text under analysis.
2270 fn source(&self) -> &str;
2271
2272 /// Return the [`TextRange`] at which a name is declared as `global` in the current scope.
2273 fn global(&self, name: &str) -> Option<TextRange>;
2274
2275 /// Returns `true` if `name` has a binding in an enclosing scope.
2276 fn has_nonlocal_binding(&self, name: &str) -> bool;
2277
2278 /// Returns `true` if the visitor is currently in an async context, i.e. an async function.
2279 fn in_async_context(&self) -> bool;
2280
2281 /// Returns `true` if the visitor is currently in a context where the `await` keyword is
2282 /// allowed.
2283 ///
2284 /// Note that this is method is primarily used to report `YieldOutsideFunction` errors for
2285 /// `await` outside function scopes, irrespective of their async status. As such, this differs
2286 /// from `in_async_context` in two ways:
2287 ///
2288 /// 1. `await` is allowed in a lambda, despite it not being async
2289 /// 2. `await` is allowed in any function, regardless of its async status
2290 ///
2291 /// In short, only nested class definitions should cause this method to return `false`, for
2292 /// example:
2293 ///
2294 /// ```python
2295 /// def f():
2296 /// await 1 # okay, in a function
2297 /// class C:
2298 /// await 1 # error
2299 /// ```
2300 ///
2301 /// See the trait-level documentation for more details.
2302 fn in_await_allowed_context(&self) -> bool;
2303
2304 /// Returns `true` if the visitor is currently in a context where `yield` and `yield from`
2305 /// expressions are allowed.
2306 ///
2307 /// Yield expressions are allowed only in:
2308 /// 1. Function definitions
2309 /// 2. Lambda expressions
2310 ///
2311 /// Unlike `await`, yield is not allowed in:
2312 /// - Comprehensions (list, set, dict)
2313 /// - Generator expressions
2314 /// - Class definitions
2315 ///
2316 /// This method should traverse parent scopes to check if the closest relevant scope
2317 /// is a function or lambda, and that no disallowed context (class, comprehension, generator)
2318 /// intervenes. For example:
2319 ///
2320 /// ```python
2321 /// def f():
2322 /// yield 1 # okay, in a function
2323 /// lambda: (yield 1) # okay, in a lambda
2324 ///
2325 /// [(yield 1) for x in range(3)] # error, in a comprehension
2326 /// ((yield 1) for x in range(3)) # error, in a generator expression
2327 /// class C:
2328 /// yield 1 # error, in a class within a function
2329 /// ```
2330 ///
2331 fn in_yield_allowed_context(&self) -> bool;
2332
2333 /// Returns `true` if the visitor is currently inside of a synchronous comprehension.
2334 ///
2335 /// This method is necessary because `in_async_context` only checks for the nearest, enclosing
2336 /// function to determine the (a)sync context. Instead, this method will search all enclosing
2337 /// scopes until it finds a sync comprehension. As a result, the two methods will typically be
2338 /// used together.
2339 fn in_sync_comprehension(&self) -> bool;
2340
2341 /// Returns `true` if the visitor is at the top-level module scope.
2342 fn in_module_scope(&self) -> bool;
2343
2344 /// Returns `true` if the visitor is in a function scope.
2345 fn in_function_scope(&self) -> bool;
2346
2347 /// Returns `true` if the visitor is within a generator scope.
2348 ///
2349 /// Note that this refers to an `Expr::Generator` precisely, not to comprehensions more
2350 /// generally.
2351 fn in_generator_context(&self) -> bool;
2352
2353 /// Returns `true` if the source file is a Jupyter notebook.
2354 fn in_notebook(&self) -> bool;
2355
2356 fn report_semantic_error(&self, error: SemanticSyntaxError);
2357
2358 /// Returns `true` if the visitor is inside a `for` or `while` loop.
2359 fn in_loop_context(&self) -> bool;
2360
2361 /// Returns `true` if `name` is a bound parameter in the current function or lambda scope.
2362 fn is_bound_parameter(&self, name: &str) -> bool;
2363}
2364
2365/// Modified version of [`std::str::EscapeDefault`] that does not escape single or double quotes.
2366struct EscapeDefault<'a>(&'a str);
2367
2368impl Display for EscapeDefault<'_> {
2369 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2370 use std::fmt::Write;
2371
2372 for c in self.0.chars() {
2373 match c {
2374 '\'' | '\"' => f.write_char(c)?,
2375 _ => write!(f, "{}", c.escape_default())?,
2376 }
2377 }
2378 Ok(())
2379 }
2380}