gh-138857: Improve error message for case outside of match (#138858)

* gh-138857: Improve error message for `case` outside of `match`

---------

Co-authored-by: Bartosz Sławecki <bartosz@ilikepython.com>
This commit is contained in:
sobolevn
2025-10-24 12:20:54 +03:00
committed by GitHub
parent 161b3064ef
commit 92c0c45563
4 changed files with 80 additions and 0 deletions

View File

@@ -1477,6 +1477,10 @@ invalid_match_stmt:
| "match" subject_expr NEWLINE { CHECK_VERSION(void*, 10, "Pattern matching is", RAISE_SYNTAX_ERROR("expected ':'") ) }
| a="match" subject=subject_expr ':' NEWLINE !INDENT {
RAISE_INDENTATION_ERROR("expected an indented block after 'match' statement on line %d", a->lineno) }
| a="case" patterns guard? b=':' block {
RAISE_SYNTAX_ERROR_KNOWN_RANGE(
a, b,
"case statement must be inside match statement") }
invalid_case_block:
| "case" patterns guard? NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") }
| a="case" patterns guard? ':' NEWLINE !INDENT {

View File

@@ -376,6 +376,42 @@ SyntaxError: invalid syntax
Traceback (most recent call last):
SyntaxError: invalid syntax
# Check incorrect "case" placement with specialized error messages
>>> case "pattern": ...
Traceback (most recent call last):
SyntaxError: case statement must be inside match statement
>>> case 1 | 2: ...
Traceback (most recent call last):
SyntaxError: case statement must be inside match statement
>>> case klass(attr=1) | {}: ...
Traceback (most recent call last):
SyntaxError: case statement must be inside match statement
>>> case [] if x > 1: ...
Traceback (most recent call last):
SyntaxError: case statement must be inside match statement
>>> case match: ...
Traceback (most recent call last):
SyntaxError: case statement must be inside match statement
>>> case case: ...
Traceback (most recent call last):
SyntaxError: case statement must be inside match statement
>>> if some:
... case 1: ...
Traceback (most recent call last):
SyntaxError: case statement must be inside match statement
>>> case some:
... case 1: ...
Traceback (most recent call last):
SyntaxError: case statement must be inside match statement
# But prefixes of soft keywords should
# still raise specialized errors

View File

@@ -0,0 +1,2 @@
Improve :exc:`SyntaxError` message for ``case`` keyword placed outside
:keyword:`match` body.

38
Parser/parser.c generated
View File

@@ -25154,6 +25154,7 @@ invalid_except_star_stmt_indent_rule(Parser *p)
// invalid_match_stmt:
// | "match" subject_expr NEWLINE
// | "match" subject_expr ':' NEWLINE !INDENT
// | "case" patterns guard? ':' block
static void *
invalid_match_stmt_rule(Parser *p)
{
@@ -25231,6 +25232,43 @@ invalid_match_stmt_rule(Parser *p)
D(fprintf(stderr, "%*c%s invalid_match_stmt[%d-%d]: %s failed!\n", p->level, ' ',
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "\"match\" subject_expr ':' NEWLINE !INDENT"));
}
{ // "case" patterns guard? ':' block
if (p->error_indicator) {
p->level--;
return NULL;
}
D(fprintf(stderr, "%*c> invalid_match_stmt[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "\"case\" patterns guard? ':' block"));
void *_opt_var;
UNUSED(_opt_var); // Silence compiler warnings
expr_ty a;
Token * b;
asdl_stmt_seq* block_var;
pattern_ty patterns_var;
if (
(a = _PyPegen_expect_soft_keyword(p, "case")) // soft_keyword='"case"'
&&
(patterns_var = patterns_rule(p)) // patterns
&&
(_opt_var = guard_rule(p), !p->error_indicator) // guard?
&&
(b = _PyPegen_expect_token(p, 11)) // token=':'
&&
(block_var = block_rule(p)) // block
)
{
D(fprintf(stderr, "%*c+ invalid_match_stmt[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "\"case\" patterns guard? ':' block"));
_res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "case statement must be inside match statement" );
if (_res == NULL && PyErr_Occurred()) {
p->error_indicator = 1;
p->level--;
return NULL;
}
goto done;
}
p->mark = _mark;
D(fprintf(stderr, "%*c%s invalid_match_stmt[%d-%d]: %s failed!\n", p->level, ' ',
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "\"case\" patterns guard? ':' block"));
}
_res = NULL;
done:
p->level--;