@@ -5,7 +5,8 @@ use rustc_hash::{FxBuildHasher, FxHashSet};
5
5
6
6
use ruff_python_ast:: name:: Name ;
7
7
use ruff_python_ast:: {
8
- self as ast, ExceptHandler , Expr , ExprContext , IpyEscapeKind , Operator , Stmt , WithItem ,
8
+ self as ast, ExceptHandler , Expr , ExprContext , IpyEscapeKind , Operator , PythonVersion , Stmt ,
9
+ WithItem ,
9
10
} ;
10
11
use ruff_text_size:: { Ranged , TextRange , TextSize } ;
11
12
@@ -2599,14 +2600,63 @@ impl<'src> Parser<'src> {
2599
2600
let decorator_start = self . node_start ( ) ;
2600
2601
self . bump ( TokenKind :: At ) ;
2601
2602
2603
+ let parsed_expr = self . parse_named_expression_or_higher ( ExpressionContext :: default ( ) ) ;
2604
+
2605
+ if self . options . target_version < PythonVersion :: PY39 {
2606
+ // test_ok decorator_expression_dotted_ident_py38
2607
+ // # parse_options: { "target-version": "3.8" }
2608
+ // @buttons.clicked.connect
2609
+ // def spam(): ...
2610
+
2611
+ // test_ok decorator_expression_identity_hack_py38
2612
+ // # parse_options: { "target-version": "3.8" }
2613
+ // def _(x): return x
2614
+ // @_(buttons[0].clicked.connect)
2615
+ // def spam(): ...
2616
+
2617
+ // test_ok decorator_expression_eval_hack_py38
2618
+ // # parse_options: { "target-version": "3.8" }
2619
+ // @eval("buttons[0].clicked.connect")
2620
+ // def spam(): ...
2621
+
2622
+ // test_ok decorator_expression_py39
2623
+ // # parse_options: { "target-version": "3.9" }
2624
+ // @buttons[0].clicked.connect
2625
+ // def spam(): ...
2626
+ // @(x := lambda x: x)(foo)
2627
+ // def bar(): ...
2628
+
2629
+ // test_err decorator_expression_py38
2630
+ // # parse_options: { "target-version": "3.8" }
2631
+ // @buttons[0].clicked.connect
2632
+ // def spam(): ...
2633
+
2634
+ // test_err decorator_named_expression_py37
2635
+ // # parse_options: { "target-version": "3.7" }
2636
+ // @(x := lambda x: x)(foo)
2637
+ // def bar(): ...
2638
+ let allowed_decorator = match & parsed_expr. expr {
2639
+ Expr :: Call ( expr_call) => {
2640
+ helpers:: is_name_or_attribute_expression ( & expr_call. func )
2641
+ }
2642
+ expr => helpers:: is_name_or_attribute_expression ( expr) ,
2643
+ } ;
2644
+
2645
+ if !allowed_decorator {
2646
+ self . add_unsupported_syntax_error (
2647
+ UnsupportedSyntaxErrorKind :: RelaxedDecorator ,
2648
+ parsed_expr. range ( ) ,
2649
+ ) ;
2650
+ }
2651
+ }
2652
+
2602
2653
// test_err decorator_invalid_expression
2603
2654
// @*x
2604
2655
// @(*x)
2605
2656
// @((*x))
2606
2657
// @yield x
2607
2658
// @yield from x
2608
2659
// def foo(): ...
2609
- let parsed_expr = self . parse_named_expression_or_higher ( ExpressionContext :: default ( ) ) ;
2610
2660
2611
2661
decorators. push ( ast:: Decorator {
2612
2662
expression : parsed_expr. expr ,
0 commit comments