Skip to content

Commit da3629e

Browse files
committed
gh-153569: use tokenizer source for parser diagnostics
Prepared and interactive input already lives in the tokenizer source object. Read syntax-error text from that bounded storage instead of keeping separate source pointers in tok_state. A shared accessor supplies an empty string for empty prepared input. Error-line lookup uses the recorded source length, so it never scans beyond stored text.
1 parent 490f6be commit da3629e

6 files changed

Lines changed: 22 additions & 41 deletions

File tree

Parser/lexer/state.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ struct tok_state {
102102
/* Stuff for PEP 0263 */
103103
char *encoding; /* Source encoding. */
104104
const char* line_start; /* pointer to start of current line */
105-
char* str; /* Source string being tokenized (if tokenizing from a string)*/
106105

107106
_PyTok_SourceText source;
108107
struct _PyTok_Reader *reader;

Parser/pegen.c

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -961,12 +961,9 @@ _PyPegen_set_syntax_error_metadata(Parser *p) {
961961
PyErr_SetRaisedException(exc);
962962
return;
963963
}
964-
const char *source = NULL;
965-
if (p->tok->str != NULL) {
966-
source = p->tok->str;
967-
}
968-
if (!source && p->tok->fp_interactive && p->tok->interactive_src_start) {
969-
source = p->tok->interactive_src_start;
964+
const char *source = p->tok->source.bytes;
965+
if (source == NULL && p->tok->fp == NULL) {
966+
source = _PyTok_SourceData(&p->tok->source);
970967
}
971968
PyObject* the_source = NULL;
972969
if (source) {

Parser/pegen_errors.c

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -225,45 +225,23 @@ _PyPegen_raise_error(Parser *p, PyObject *errtype, int use_mark, const char *err
225225
}
226226

227227
static PyObject *
228-
get_error_line_from_tokenizer_buffers(Parser *p, Py_ssize_t lineno)
228+
get_error_line_from_source(Parser *p, Py_ssize_t lineno)
229229
{
230-
/* If the file descriptor is interactive, the source lines of the current
231-
* (multi-line) statement are stored in p->tok->interactive_src_start.
232-
* If not, we're parsing from a string, which means that the whole source
233-
* is stored in p->tok->str. */
234-
assert((p->tok->fp == NULL && p->tok->str != NULL) || p->tok->fp != NULL);
235-
236-
char *cur_line = p->tok->fp_interactive ? p->tok->interactive_src_start : p->tok->str;
237-
if (cur_line == NULL) {
238-
assert(p->tok->fp_interactive);
239-
// We can reach this point if the tokenizer buffers for interactive source have not been
240-
// initialized because we failed to decode the original source with the given locale.
241-
return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
242-
}
230+
const char *cur_line = _PyTok_SourceData(&p->tok->source);
243231

244232
Py_ssize_t relative_lineno = p->starting_lineno ? lineno - p->starting_lineno + 1 : lineno;
245-
const char* buf_end = p->tok->fp_interactive ? p->tok->interactive_src_end : p->tok->inp;
246-
247-
if (buf_end < cur_line) {
248-
buf_end = cur_line + strlen(cur_line);
249-
}
233+
const char *buf_end = cur_line + p->tok->source.len;
250234

251235
for (int i = 0; i < relative_lineno - 1; i++) {
252-
char *new_line = strchr(cur_line, '\n');
253-
// The assert is here for debug builds but the conditional that
254-
// follows is there so in release builds we do not crash at the cost
255-
// to report a potentially wrong line.
256-
assert(new_line != NULL && new_line + 1 < buf_end);
257-
if (new_line == NULL || new_line + 1 > buf_end) {
236+
const char *new_line = memchr(cur_line, '\n', buf_end - cur_line);
237+
if (new_line == NULL) {
258238
break;
259239
}
260240
cur_line = new_line + 1;
261241
}
262242

263-
char *next_newline;
264-
if ((next_newline = strchr(cur_line, '\n')) == NULL) { // This is the last line
265-
next_newline = cur_line + strlen(cur_line);
266-
}
243+
const char *next_newline = memchr(cur_line, '\n', buf_end - cur_line);
244+
next_newline = next_newline != NULL ? next_newline : buf_end;
267245
return PyUnicode_DecodeUTF8(cur_line, next_newline - cur_line, "replace");
268246
}
269247

@@ -296,7 +274,7 @@ _PyPegen_raise_error_known_location(Parser *p, PyObject *errtype,
296274
}
297275

298276
if (p->tok->fp_interactive && p->tok->interactive_src_start != NULL) {
299-
error_line = get_error_line_from_tokenizer_buffers(p, lineno);
277+
error_line = get_error_line_from_source(p, lineno);
300278
}
301279
else if (p->start_rule == Py_file_input) {
302280
error_line = _PyErr_ProgramDecodedTextObject(p->tok->filename,
@@ -318,7 +296,7 @@ _PyPegen_raise_error_known_location(Parser *p, PyObject *errtype,
318296
error_line = PyUnicode_DecodeUTF8(p->tok->line_start, size, "replace");
319297
}
320298
else if (p->tok->fp == NULL || p->tok->fp == stdin) {
321-
error_line = get_error_line_from_tokenizer_buffers(p, lineno);
299+
error_line = get_error_line_from_source(p, lineno);
322300
}
323301
else {
324302
error_line = Py_GetConstant(Py_CONSTANT_EMPTY_STR);

Parser/tokenizer/decoder.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,10 +399,9 @@ _PyTok_PrepareString(struct tok_state *tok, const char *input, int utf8_only,
399399
if (stored < 0) {
400400
return -1;
401401
}
402-
tok->str = tok->source.bytes != NULL ? tok->source.bytes : (char *)"";
403402
if (!utf8_only &&
404403
(tok->encoding == NULL || strcmp(tok->encoding, "utf-8") == 0) &&
405-
!_PyTokenizer_ensure_utf8(tok->str, tok, 1)) {
404+
!_PyTokenizer_ensure_utf8(_PyTok_SourceData(&tok->source), tok, 1)) {
406405
return -1;
407406
}
408407
return 0;

Parser/tokenizer/reader.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,9 @@ tokenizer_from_string(const char *input, int utf8_only, int exec_input,
753753
_PyTokenizer_Free(tok);
754754
return NULL;
755755
}
756-
tok->buf = tok->cur = tok->inp = tok->str;
756+
char *source = (char *)_PyTok_SourceData(&tok->source);
757+
tok->buf = tok->cur = tok->inp = source;
758+
tok->line_start = source;
757759
return tok;
758760
}
759761

Parser/tokenizer/source.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ typedef struct {
2727
Py_ssize_t implicit_cap;
2828
} _PyTok_SourceText;
2929

30+
static inline const char *
31+
_PyTok_SourceData(const _PyTok_SourceText *source)
32+
{
33+
return source->bytes != NULL ? source->bytes : "";
34+
}
35+
3036
PyAPI_FUNC(void) _PyTok_SourceInit(_PyTok_SourceText *);
3137
/* Clear invalidates all spans and views for the source. */
3238
PyAPI_FUNC(void) _PyTok_SourceClear(_PyTok_SourceText *);

0 commit comments

Comments
 (0)