summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsobolevn <mail@sobolevn.me>2024-09-02 11:11:44 (GMT)
committerGitHub <noreply@github.com>2024-09-02 11:11:44 (GMT)
commit23f159ae711d84177e8ce34cd9a6c8a762de64ac (patch)
treece5c8a0a5fb9bf9073f10a10825ea8f48139fe58
parentc3ed775899eedd47d37f8f1840345b108920e400 (diff)
downloadcpython-23f159ae711d84177e8ce34cd9a6c8a762de64ac.zip
cpython-23f159ae711d84177e8ce34cd9a6c8a762de64ac.tar.gz
cpython-23f159ae711d84177e8ce34cd9a6c8a762de64ac.tar.bz2
gh-123562: Improve `SyntaxError` message for `case ... as a.b` (#123563)
-rw-r--r--Grammar/python.gram4
-rw-r--r--Lib/test/test_patma.py7
-rw-r--r--Lib/test/test_syntax.py42
-rw-r--r--Misc/NEWS.d/next/Core_and_Builtins/2024-09-01-12-08-39.gh-issue-123562.aJPKVu.rst2
-rw-r--r--Parser/parser.c14
5 files changed, 59 insertions, 10 deletions
diff --git a/Grammar/python.gram b/Grammar/python.gram
index 431bb90..e9a8c69 100644
--- a/Grammar/python.gram
+++ b/Grammar/python.gram
@@ -1375,7 +1375,9 @@ invalid_case_block:
RAISE_INDENTATION_ERROR("expected an indented block after 'case' statement on line %d", a->lineno) }
invalid_as_pattern:
| or_pattern 'as' a="_" { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "cannot use '_' as a target") }
- | or_pattern 'as' !NAME a=expression { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "invalid pattern target") }
+ | or_pattern 'as' a=expression {
+ RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
+ a, "cannot use %s as pattern target", _PyPegen_get_expr_name(a)) }
invalid_class_pattern:
| name_or_attr '(' a=invalid_class_argument_pattern { RAISE_SYNTAX_ERROR_KNOWN_RANGE(
PyPegen_first_item(a, pattern_ty),
diff --git a/Lib/test/test_patma.py b/Lib/test/test_patma.py
index 8325b83..dae6d89 100644
--- a/Lib/test/test_patma.py
+++ b/Lib/test/test_patma.py
@@ -3015,6 +3015,13 @@ class TestSyntaxErrors(unittest.TestCase):
pass
""")
+ def test_multiple_assignments_to_name_in_pattern_6(self):
+ self.assert_syntax_error("""
+ match ...:
+ case a as a + 1: # NAME and expression with no ()
+ pass
+ """)
+
def test_multiple_starred_names_in_sequence_pattern_0(self):
self.assert_syntax_error("""
match ...:
diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py
index 406ea21..132e2b8 100644
--- a/Lib/test/test_syntax.py
+++ b/Lib/test/test_syntax.py
@@ -1932,7 +1932,31 @@ Corner-cases that used to crash:
... case 42 as 1+2+4:
... ...
Traceback (most recent call last):
- SyntaxError: invalid pattern target
+ SyntaxError: cannot use expression as pattern target
+
+ >>> match ...:
+ ... case 42 as a.b:
+ ... ...
+ Traceback (most recent call last):
+ SyntaxError: cannot use attribute as pattern target
+
+ >>> match ...:
+ ... case 42 as (a, b):
+ ... ...
+ Traceback (most recent call last):
+ SyntaxError: cannot use tuple as pattern target
+
+ >>> match ...:
+ ... case 42 as (a + 1):
+ ... ...
+ Traceback (most recent call last):
+ SyntaxError: cannot use expression as pattern target
+
+ >>> match ...:
+ ... case (32 as x) | (42 as a()):
+ ... ...
+ Traceback (most recent call last):
+ SyntaxError: cannot use function call as pattern target
>>> match ...:
... case Foo(z=1, y=2, x):
@@ -2817,6 +2841,22 @@ while 1:
end_offset=22 + len("obj.attr"),
)
+ def test_match_stmt_invalid_as_expr(self):
+ self._check_error(
+ textwrap.dedent(
+ """
+ match 1:
+ case x as obj.attr:
+ ...
+ """
+ ),
+ errtext="cannot use attribute as pattern target",
+ lineno=3,
+ end_lineno=3,
+ offset=15,
+ end_offset=15 + len("obj.attr"),
+ )
+
def load_tests(loader, tests, pattern):
tests.addTest(doctest.DocTestSuite())
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-09-01-12-08-39.gh-issue-123562.aJPKVu.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-09-01-12-08-39.gh-issue-123562.aJPKVu.rst
new file mode 100644
index 0000000..10ef82c
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-09-01-12-08-39.gh-issue-123562.aJPKVu.rst
@@ -0,0 +1,2 @@
+Improve :exc:`SyntaxError` message for using ``case ... as ...`` with not a
+name.
diff --git a/Parser/parser.c b/Parser/parser.c
index 3c40395..e5567d0 100644
--- a/Parser/parser.c
+++ b/Parser/parser.c
@@ -24005,7 +24005,7 @@ invalid_case_block_rule(Parser *p)
return _res;
}
-// invalid_as_pattern: or_pattern 'as' "_" | or_pattern 'as' !NAME expression
+// invalid_as_pattern: or_pattern 'as' "_" | or_pattern 'as' expression
static void *
invalid_as_pattern_rule(Parser *p)
{
@@ -24048,12 +24048,12 @@ invalid_as_pattern_rule(Parser *p)
D(fprintf(stderr, "%*c%s invalid_as_pattern[%d-%d]: %s failed!\n", p->level, ' ',
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "or_pattern 'as' \"_\""));
}
- { // or_pattern 'as' !NAME expression
+ { // or_pattern 'as' expression
if (p->error_indicator) {
p->level--;
return NULL;
}
- D(fprintf(stderr, "%*c> invalid_as_pattern[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "or_pattern 'as' !NAME expression"));
+ D(fprintf(stderr, "%*c> invalid_as_pattern[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "or_pattern 'as' expression"));
Token * _keyword;
expr_ty a;
pattern_ty or_pattern_var;
@@ -24062,13 +24062,11 @@ invalid_as_pattern_rule(Parser *p)
&&
(_keyword = _PyPegen_expect_token(p, 666)) // token='as'
&&
- _PyPegen_lookahead_with_name(0, _PyPegen_name_token, p)
- &&
(a = expression_rule(p)) // expression
)
{
- D(fprintf(stderr, "%*c+ invalid_as_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "or_pattern 'as' !NAME expression"));
- _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "invalid pattern target" );
+ D(fprintf(stderr, "%*c+ invalid_as_pattern[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "or_pattern 'as' expression"));
+ _res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "cannot use %s as pattern target" , _PyPegen_get_expr_name ( a ) );
if (_res == NULL && PyErr_Occurred()) {
p->error_indicator = 1;
p->level--;
@@ -24078,7 +24076,7 @@ invalid_as_pattern_rule(Parser *p)
}
p->mark = _mark;
D(fprintf(stderr, "%*c%s invalid_as_pattern[%d-%d]: %s failed!\n", p->level, ' ',
- p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "or_pattern 'as' !NAME expression"));
+ p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "or_pattern 'as' expression"));
}
_res = NULL;
done: