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