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