Skip to content

Commit a39cdf2

Browse files
committed
gh-159672: Simplify keyword trial cleanup
1 parent 4074a61 commit a39cdf2

2 files changed

Lines changed: 17 additions & 34 deletions

File tree

‎Lib/test/test_traceback.py‎

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1859,12 +1859,6 @@ def test_keyword_suggestions_from_single_input(self):
18591859
with self.assertRaises(SyntaxError) as caught:
18601860
compile(source + ending, "<test>", "single")
18611861
self.assertEqual(caught.exception._metadata[0], keyword)
1862-
with (unittest.mock.patch("builtins.compile") as compile_mock,
1863-
unittest.mock.patch("builtins.open") as open_mock):
1864-
text = ''.join(traceback.format_exception_only(caught.exception))
1865-
self.assertIn(f"Did you mean '{keyword}'", text)
1866-
compile_mock.assert_not_called()
1867-
open_mock.assert_not_called()
18681862

18691863
def test_no_keyword_suggestion_for_comma_errors(self):
18701864
# When the parser identifies a missing comma, don't suggest
@@ -1887,19 +1881,12 @@ def test_no_keyword_suggestion_for_comma_errors(self):
18871881
@cpython_only
18881882
def test_parser_suggestions_need_no_formatting_work(self):
18891883
cases = [
1890-
("def f():\n returm value", "return", 2, 5),
18911884
("a=b=c=d=e=f=g=h=i=0\ndef fn():\n retrun True", "return", 3, 5),
18921885
("def f(a,b):\n if b:\n yield\n retrun a+b", "return", 4, 5),
18931886
("def f():\n for i in range(10):\n yield i\n\ndfe x():", "def", 5, 1),
18941887
("é = 1; impor math", "import", 1, 8),
18951888
("for café is items:\n pass", "in", 1, 10),
1896-
("a = 1\nif True:\n pass\nelso:\n pass", "else", 4, 1),
18971889
("[123 fur x\nin range(3)\nif x]", "for", 1, 6),
1898-
("mach x:\n case 1: pass", "match", 1, 1),
1899-
("match x:\n cse 1: pass", "case", 2, 5),
1900-
("typ Alias = int", "type", 1, 1),
1901-
("lazi import math", "lazy", 1, 1),
1902-
("long_name = 1\n" * 85 + "whille True:\n pass", "while", 86, 1),
19031890
]
19041891
for source, keyword, lineno, offset in cases:
19051892
with self.subTest(source=source):

‎Parser/pegen.c‎

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,31 +1145,26 @@ try_keyword_typo(Parser *p, KeywordTypo *candidate, KeywordTrial *state)
11451145
// must be private: even a failed trial can memoize nodes containing its edit.
11461146
Token *tokens = PyMem_Malloc(p->fill * sizeof(*tokens));
11471147
Token **index = PyMem_Malloc(p->fill * sizeof(*index));
1148-
IdentifierCacheEntry *identifiers = p->identifier_cache == NULL ? NULL :
1149-
PyMem_Malloc(IDENTIFIER_CACHE_SIZE * sizeof(*identifiers));
1148+
Parser trial = *p;
1149+
trial.identifier_cache = p->identifier_cache == NULL ? NULL :
1150+
PyMem_Malloc(IDENTIFIER_CACHE_SIZE * sizeof(*trial.identifier_cache));
11501151
PyArena *arena = _PyArena_New();
11511152
PyObject *replacement = PyBytes_FromString(candidate->keyword);
1153+
int accepted = -1;
11521154
if (tokens == NULL || index == NULL || arena == NULL || replacement == NULL ||
1153-
(p->identifier_cache != NULL && identifiers == NULL))
1155+
(p->identifier_cache != NULL && trial.identifier_cache == NULL))
11541156
{
1155-
PyMem_Free(tokens);
1156-
PyMem_Free(index);
1157-
PyMem_Free(identifiers);
1158-
if (arena != NULL) {
1159-
_PyArena_Free(arena);
1160-
}
1161-
Py_XDECREF(replacement);
1162-
return -1;
1157+
goto done;
11631158
}
11641159
for (int i = 0; i < p->fill; i++) {
11651160
tokens[i] = *p->tokens[i];
11661161
tokens[i].memo = NULL;
11671162
tokens[i].memo_mask = 0;
11681163
index[i] = &tokens[i];
11691164
}
1170-
if (identifiers != NULL) {
1171-
memcpy(identifiers, p->identifier_cache,
1172-
IDENTIFIER_CACHE_SIZE * sizeof(*identifiers));
1165+
if (trial.identifier_cache != NULL) {
1166+
memcpy(trial.identifier_cache, p->identifier_cache,
1167+
IDENTIFIER_CACHE_SIZE * sizeof(*trial.identifier_cache));
11731168
}
11741169
Token *edited = &tokens[candidate->mark];
11751170
edited->type = _get_keyword_or_name_type(
@@ -1178,7 +1173,6 @@ try_keyword_typo(Parser *p, KeywordTypo *candidate, KeywordTrial *state)
11781173

11791174
state->furthest = 0;
11801175
state->incomplete_suite = 0;
1181-
Parser trial = *p;
11821176
trial.tokens = index;
11831177
trial.arena = arena;
11841178
trial.size = trial.fill;
@@ -1197,10 +1191,9 @@ try_keyword_typo(Parser *p, KeywordTypo *candidate, KeywordTrial *state)
11971191
#endif
11981192
trial.known_err_token = NULL;
11991193
trial.errcode = NULL;
1200-
trial.identifier_cache = identifiers;
12011194
trial.normalize = NULL;
1202-
int accepted = _PyPegen_parse(&trial) != NULL &&
1203-
!trial.error_indicator && !PyErr_Occurred();
1195+
accepted = _PyPegen_parse(&trial) != NULL &&
1196+
!trial.error_indicator && !PyErr_Occurred();
12041197
for (int i = trial.mark; accepted && i < trial.fill; i++) {
12051198
accepted = tokens[i].type == NEWLINE || tokens[i].type == ENDMARKER;
12061199
}
@@ -1224,11 +1217,14 @@ try_keyword_typo(Parser *p, KeywordTypo *candidate, KeywordTrial *state)
12241217
// older grammars, require a complete repair so those checks execute.
12251218
accepted = state->incomplete_suite && p->feature_version == PY_MINOR_VERSION;
12261219
}
1220+
done:
12271221
// No Parser_Free: the trial borrows the tokenizer and type-ignore comments,
12281222
// and its token headers are a single allocation.
12291223
PyMem_Free(trial.identifier_cache);
1230-
_PyArena_Free(arena);
1231-
Py_DECREF(replacement);
1224+
if (arena != NULL) {
1225+
_PyArena_Free(arena);
1226+
}
1227+
Py_XDECREF(replacement);
12321228
PyMem_Free(index);
12331229
PyMem_Free(tokens);
12341230
return PyErr_Occurred() ? -1 : accepted;
@@ -1365,7 +1361,7 @@ keyword_typo_metadata(Parser *p, PySyntaxErrorObject *error)
13651361
qsort(candidates, count, sizeof(*candidates), compare_keyword_typos);
13661362
// All candidates and both parser passes share this work budget.
13671363
KeywordTrial state = {.budget = MAX_KEYWORD_TYPO_STEPS};
1368-
for (int i = 0; i < count; i++) {
1364+
for (int i = 0; i < count && state.budget > 0; i++) {
13691365
int accepted = try_keyword_typo(p, &candidates[i], &state);
13701366
if (accepted < 0) {
13711367
return NULL;

0 commit comments

Comments
 (0)