@@ -1112,9 +1112,6 @@ add_keyword_typo(KeywordTypo *candidates, int *count, int mark,
11121112 if (word == NULL ) {
11131113 return -1 ;
11141114 }
1115- if (PyUnicode_GET_LENGTH (name ) <= 2 ) {
1116- max_cost = Py_MIN (max_cost , 2 );
1117- }
11181115 Py_ssize_t cost = _Py_UTF8_Edit_Cost (name , word , max_cost );
11191116 // Adjacent transpositions are one spelling error. The shared Levenshtein
11201117 // distance counts them as two substitutions.
@@ -1185,17 +1182,20 @@ keyword_trial_accepts(Parser *p, const KeywordTypo *candidate)
11851182static int
11861183try_keyword_typo (Parser * p , const KeywordTypo * candidate , KeywordTrial * state )
11871184{
1185+ PyArena * arena = _PyArena_New ();
1186+ if (arena == NULL ) {
1187+ return -1 ;
1188+ }
11881189 // Token text belongs to the original arena. Token headers and memo entries
11891190 // must be private: even a failed trial can memoize nodes containing its edit.
1190- Token * tokens = PyMem_Malloc ( p -> fill * sizeof (* tokens ));
1191- Token * * index = PyMem_Malloc ( p -> fill * sizeof (* index ));
1191+ Token * tokens = _PyArena_Malloc ( arena , p -> fill * sizeof (* tokens ));
1192+ Token * * index = _PyArena_Malloc ( arena , p -> fill * sizeof (* index ));
11921193 Parser trial = * p ;
11931194 trial .identifier_cache = p -> identifier_cache == NULL ? NULL :
11941195 PyMem_Malloc (IDENTIFIER_CACHE_SIZE * sizeof (* trial .identifier_cache ));
1195- PyArena * arena = _PyArena_New ();
11961196 PyObject * replacement = PyBytes_FromString (candidate -> keyword );
11971197 int accepted = -1 ;
1198- if (tokens == NULL || index == NULL || arena == NULL || replacement == NULL ||
1198+ if (tokens == NULL || index == NULL || replacement == NULL ||
11991199 (p -> identifier_cache != NULL && trial .identifier_cache == NULL ))
12001200 {
12011201 goto done ;
@@ -1239,14 +1239,10 @@ try_keyword_typo(Parser *p, const KeywordTypo *candidate, KeywordTrial *state)
12391239 accepted = keyword_trial_accepts (& trial , candidate );
12401240done :
12411241 // No Parser_Free: the trial borrows the tokenizer and type-ignore comments,
1242- // and its token headers are a single allocation .
1242+ // and its token arrays belong to the arena .
12431243 PyMem_Free (trial .identifier_cache );
1244- if (arena != NULL ) {
1245- _PyArena_Free (arena );
1246- }
1244+ _PyArena_Free (arena );
12471245 Py_XDECREF (replacement );
1248- PyMem_Free (index );
1249- PyMem_Free (tokens );
12501246 return PyErr_Occurred () ? -1 : accepted ;
12511247}
12521248
@@ -1357,7 +1353,8 @@ collect_keyword_typos(Parser *p, PySyntaxErrorObject *error, KeywordTypo *candid
13571353 // Preserve missing-comma diagnostics and correctly spelled keywords
13581354 // unless one edit repairs the input. For keywords, only substitute a
13591355 // character: deleting one from "for" must not suggest "or".
1360- Py_ssize_t max_cost = p -> missing_comma || is_keyword ? 2 : 4 ;
1356+ Py_ssize_t max_cost =
1357+ p -> missing_comma || is_keyword || PyUnicode_GET_LENGTH (name ) <= 2 ? 2 : 4 ;
13611358 int result = 0 ;
13621359 for (int length = 0 ; length < p -> n_keyword_lists && result == 0 ; length ++ ) {
13631360 if (is_keyword && length != text_length ) {
0 commit comments