Skip to content

gh-118235: Move RAISE_SYNTAX_ERROR actions to invalid rules and make sure they stay there #119731

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Doc/tools/extensions/peg_highlight.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ class PEGLexer(RegexLexer):
- Rule types
- Rule options
- Rules named `invalid_*` or `incorrect_*`
- Rules with `RAISE_SYNTAX_ERROR`
"""

name = "PEG"
Expand Down Expand Up @@ -60,7 +59,6 @@ class PEGLexer(RegexLexer):
(r"^(\s+\|\s+.*invalid_\w+.*\n)", bygroups(None)),
(r"^(\s+\|\s+.*incorrect_\w+.*\n)", bygroups(None)),
(r"^(#.*invalid syntax.*(?:.|\n)*)", bygroups(None),),
(r"^(\s+\|\s+.*\{[^}]*RAISE_SYNTAX_ERROR[^}]*\})\n", bygroups(None)),
],
"root": [
include("invalids"),
Expand Down
38 changes: 23 additions & 15 deletions Grammar/python.gram
Original file line number Diff line number Diff line change
Expand Up @@ -650,17 +650,8 @@ type_param_seq[asdl_type_param_seq*]: a[asdl_type_param_seq*]=','.type_param+ ['

type_param[type_param_ty] (memo):
| a=NAME b=[type_param_bound] c=[type_param_default] { _PyAST_TypeVar(a->v.Name.id, b, c, EXTRA) }
| '*' a=NAME colon=':' e=expression {
RAISE_SYNTAX_ERROR_STARTING_FROM(colon, e->kind == Tuple_kind
? "cannot use constraints with TypeVarTuple"
: "cannot use bound with TypeVarTuple")
}
| invalid_type_param
| '*' a=NAME b=[type_param_starred_default] { _PyAST_TypeVarTuple(a->v.Name.id, b, EXTRA) }
| '**' a=NAME colon=':' e=expression {
RAISE_SYNTAX_ERROR_STARTING_FROM(colon, e->kind == Tuple_kind
? "cannot use constraints with ParamSpec"
: "cannot use bound with ParamSpec")
}
| '**' a=NAME b=[type_param_default] { _PyAST_ParamSpec(a->v.Name.id, b, EXTRA) }

type_param_bound[expr_ty]: ':' e=expression { e }
Expand Down Expand Up @@ -979,8 +970,7 @@ for_if_clause[comprehension_ty]:
CHECK_VERSION(comprehension_ty, 6, "Async comprehensions are", _PyAST_comprehension(a, b, c, 1, p->arena)) }
| 'for' a=star_targets 'in' ~ b=disjunction c[asdl_expr_seq*]=('if' z=disjunction { z })* {
_PyAST_comprehension(a, b, c, 0, p->arena) }
| 'async'? 'for' (bitwise_or (',' bitwise_or)* [',']) !'in' {
RAISE_SYNTAX_ERROR("'in' expected after for-loop variables") }
| invalid_for_if_clause
| invalid_for_target

listcomp[expr_ty]:
Expand Down Expand Up @@ -1020,9 +1010,9 @@ kwargs[asdl_seq*]:
| ','.kwarg_or_double_starred+

starred_expression[expr_ty]:
| invalid_starred_expression
| invalid_starred_expression_unpacking
| '*' a=expression { _PyAST_Starred(a, Load, EXTRA) }
| '*' { RAISE_SYNTAX_ERROR("Invalid star expression") }
| invalid_starred_expression

kwarg_or_starred[KeywordOrStarred*]:
| invalid_kwarg
Expand Down Expand Up @@ -1176,6 +1166,18 @@ invalid_legacy_expression:
_PyPegen_check_legacy_stmt(p, a) ? RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b,
"Missing parentheses in call to '%U'. Did you mean %U(...)?", a->v.Name.id, a->v.Name.id) : NULL}

invalid_type_param:
| '*' a=NAME colon=':' e=expression {
RAISE_SYNTAX_ERROR_STARTING_FROM(colon, e->kind == Tuple_kind
? "cannot use constraints with TypeVarTuple"
: "cannot use bound with TypeVarTuple")
}
| '**' a=NAME colon=':' e=expression {
RAISE_SYNTAX_ERROR_STARTING_FROM(colon, e->kind == Tuple_kind
? "cannot use constraints with ParamSpec"
: "cannot use bound with ParamSpec")
}

invalid_expression:
# !(NAME STRING) is not matched so we don't show this error with some invalid string prefixes like: kf"dsfsdf"
# Soft keywords need to also be ignored because they can be parsed as NAME NAME
Expand Down Expand Up @@ -1296,6 +1298,10 @@ invalid_with_item:
| expression 'as' a=expression &(',' | ')' | ':') {
RAISE_SYNTAX_ERROR_INVALID_TARGET(STAR_TARGETS, a) }

invalid_for_if_clause:
| 'async'? 'for' (bitwise_or (',' bitwise_or)* [',']) !'in' {
RAISE_SYNTAX_ERROR("'in' expected after for-loop variables") }

invalid_for_target:
| 'async'? 'for' a=star_expressions {
RAISE_SYNTAX_ERROR_INVALID_TARGET(FOR_TARGETS, a) }
Expand Down Expand Up @@ -1409,8 +1415,10 @@ invalid_kvpair:
RAISE_ERROR_KNOWN_LOCATION(p, PyExc_SyntaxError, a->lineno, a->end_col_offset - 1, a->end_lineno, -1, "':' expected after dictionary key") }
| expression ':' a='*' bitwise_or { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "cannot use a starred expression in a dictionary value") }
| expression a=':' &('}'|',') {RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expression expected after dictionary key and ':'") }
invalid_starred_expression:
invalid_starred_expression_unpacking:
| a='*' expression '=' b=expression { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "cannot assign to iterable argument unpacking") }
invalid_starred_expression:
| '*' { RAISE_SYNTAX_ERROR("Invalid star expression") }

invalid_replacement_field:
| '{' a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '='") }
Expand Down
12 changes: 11 additions & 1 deletion Lib/test/test_peg_generator/test_grammar_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
test_tools.skip_if_missing("peg_generator")
with test_tools.imports_under_tool("peg_generator"):
from pegen.grammar_parser import GeneratedParser as GrammarParser
from pegen.validator import SubRuleValidator, ValidationError
from pegen.validator import SubRuleValidator, ValidationError, RaiseRuleValidator
from pegen.testutil import parse_string
from pegen.grammar import Grammar

Expand Down Expand Up @@ -49,3 +49,13 @@ def test_rule_with_collision_after_some_other_rules(self) -> None:
with self.assertRaises(ValidationError):
for rule_name, rule in grammar.rules.items():
validator.validate_rule(rule_name, rule)

def test_raising_valid_rule(self) -> None:
grammar_source = """
start: NAME { RAISE_SYNTAX_ERROR("this is not allowed") }
"""
grammar: Grammar = parse_string(grammar_source, GrammarParser)
validator = RaiseRuleValidator(grammar)
with self.assertRaises(ValidationError):
for rule_name, rule in grammar.rules.items():
validator.validate_rule(rule_name, rule)
Loading
Loading