diff --git a/Grammar/python.gram b/Grammar/python.gram index 9bf3a67939fcf3..4bc3e030e52057 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -1287,6 +1287,12 @@ invalid_named_expression(memo): | a=expression ':=' expression { RAISE_SYNTAX_ERROR_KNOWN_LOCATION( a, "cannot use assignment expressions with %s", _PyPegen_get_expr_name(a)) } + | a=expression '&''&' b=expression { + RAISE_SYNTAX_ERROR_KNOWN_RANGE( + a, b, "invalid syntax '&&'. Use 'and' instead.") } + | a=expression '|''|' b=expression { + RAISE_SYNTAX_ERROR_KNOWN_RANGE( + a, b, "invalid syntax '||'. Use 'or' instead.") } | a=NAME '=' b=bitwise_or !('='|':=') { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "invalid syntax. Maybe you meant '==' or ':=' instead of '='?") } | !(list|tuple|genexp|'True'|'None'|'False') a=bitwise_or b='=' bitwise_or !('='|':=') { diff --git a/Lib/traceback.py b/Lib/traceback.py index 614a12f69b32e4..8de6bafefec948 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -1485,8 +1485,12 @@ def _find_keyword_typos(self): # Limit the number of possible matches to try max_matches = 3 matches = [] + + hint = _get_cross_language_keyword_hint(wrong_name) + if hint: + matches.append(hint) if _suggestions is not None: - suggestion = _suggestions._generate_suggestions(keyword.kwlist, wrong_name) + suggestion = _suggestions._generate_suggestions(keyword.kwlist + keyword.softkwlist, wrong_name) if suggestion: matches.append(suggestion) matches.extend(difflib.get_close_matches(wrong_name, keyword.kwlist, n=max_matches, cutoff=0.5)) @@ -1787,6 +1791,20 @@ def print(self, *, file=None, chain=True, **kwargs): }) +# Cross-language keyword suggestions. +_CROSS_LANGUAGE_KEYWORD_HINTS = frozendict({ + # C/C++ equivalents + 'switch': 'match', + 'delete': 'del', + # function define equivalents + 'function': 'def', + 'func': 'def', + # null equivalents + 'NULL': 'None', + 'null': 'None', + 'nil': 'None', +}) + def _substitution_cost(ch_a, ch_b): if ch_a == ch_b: return 0 @@ -1866,6 +1884,13 @@ def _get_cross_language_hint(obj, wrong_name): return None +def _get_cross_language_keyword_hint(wrong_name): + """Check if wrong_name is a common keyword from another language + """ + hint = _CROSS_LANGUAGE_KEYWORD_HINTS.get(wrong_name) + return hint + + def _get_safe___dir__(obj): # Use obj.__dir__() to avoid a TypeError when calling dir(obj). # See gh-131001 and gh-139933. diff --git a/Parser/parser.c b/Parser/parser.c index c55c081dfc3d8e..42c9070944309c 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -22029,6 +22029,8 @@ invalid_if_expression_rule(Parser *p) // invalid_named_expression: // | expression ':=' expression +// | expression '&' '&' expression +// | expression '|' '|' expression // | NAME '=' bitwise_or !('=' | ':=') // | !(list | tuple | genexp | 'True' | 'None' | 'False') bitwise_or '=' bitwise_or !('=' | ':=') static void * @@ -22077,6 +22079,72 @@ invalid_named_expression_rule(Parser *p) D(fprintf(stderr, "%*c%s invalid_named_expression[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression ':=' expression")); } + { // expression '&' '&' expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_named_expression[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression '&' '&' expression")); + Token * _literal; + Token * _literal_1; + expr_ty a; + expr_ty b; + if ( + (a = expression_rule(p)) // expression + && + (_literal = _PyPegen_expect_token(p, 19)) // token='&' + && + (_literal_1 = _PyPegen_expect_token(p, 19)) // token='&' + && + (b = expression_rule(p)) // expression + ) + { + D(fprintf(stderr, "%*c+ invalid_named_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression '&' '&' expression")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "invalid syntax '&&'. Use 'and' instead." ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_named_expression[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression '&' '&' expression")); + } + { // expression '|' '|' expression + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_named_expression[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "expression '|' '|' expression")); + Token * _literal; + Token * _literal_1; + expr_ty a; + expr_ty b; + if ( + (a = expression_rule(p)) // expression + && + (_literal = _PyPegen_expect_token(p, 18)) // token='|' + && + (_literal_1 = _PyPegen_expect_token(p, 18)) // token='|' + && + (b = expression_rule(p)) // expression + ) + { + D(fprintf(stderr, "%*c+ invalid_named_expression[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression '|' '|' expression")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "invalid syntax '||'. Use 'or' instead." ); + if ((_res == NULL || p->error_indicator) && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_named_expression[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "expression '|' '|' expression")); + } { // NAME '=' bitwise_or !('=' | ':=') if (p->error_indicator) { p->level--;