Skip to content

Commit afd9d72

Browse files
committed
gh-153569: read diagnostic lines through the source API
1 parent 841b57e commit afd9d72

4 files changed

Lines changed: 56 additions & 15 deletions

File tree

Modules/_testinternalcapi/tokenizer.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,28 @@ check_system_error(int failed, const char *message)
2323
return 0;
2424
}
2525

26+
static int
27+
check_line_view(const _PyTok_SourceText *source, Py_ssize_t lineno,
28+
const char *expected)
29+
{
30+
Py_ssize_t len;
31+
const char *line = _PyTok_SourceLineView(source, lineno, &len);
32+
return check(len == (Py_ssize_t)strlen(expected) &&
33+
memcmp(line, expected, len) == 0,
34+
"wrong source line view");
35+
}
36+
2637
static PyObject *
2738
test_tokenizer_source(PyObject *Py_UNUSED(module),
2839
PyObject *Py_UNUSED(args))
2940
{
3041
_PyTok_SourceText source;
3142
_PyTok_SourceInit(&source);
3243

44+
if (check_line_view(&source, 1, "") < 0) {
45+
goto error;
46+
}
47+
3348
if (check_system_error(
3449
_PyTok_SourceAppendLine(&source, "", 0, 0) < 0,
3550
"accepted empty source line") < 0 ||
@@ -51,6 +66,14 @@ test_tokenizer_source(PyObject *Py_UNUSED(module),
5166
goto error;
5267
}
5368

69+
if (check_line_view(&source, PY_SSIZE_T_MIN, "alpha") < 0 ||
70+
check_line_view(&source, 1, "alpha") < 0 ||
71+
check_line_view(&source, 2, "\xce\xb2") < 0 ||
72+
check_line_view(&source, 3, "") < 0 ||
73+
check_line_view(&source, PY_SSIZE_T_MAX, "") < 0) {
74+
goto error;
75+
}
76+
5477
if (check(source.len == 9 &&
5578
memcmp(source.bytes, "alpha\n\xce\xb2\n", 10) == 0,
5679
"wrong source contents") < 0) {
@@ -64,6 +87,10 @@ test_tokenizer_source(PyObject *Py_UNUSED(module),
6487
"appended after unterminated source line") < 0) {
6588
goto error;
6689
}
90+
if (check_line_view(&source, 1, "tail") < 0 ||
91+
check_line_view(&source, PY_SSIZE_T_MAX, "tail") < 0) {
92+
goto error;
93+
}
6794

6895
_PyTok_SourceClear(&source);
6996
Py_RETURN_NONE;

Parser/pegen_errors.c

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -226,22 +226,10 @@ _PyPegen_raise_error(Parser *p, PyObject *errtype, int use_mark, const char *err
226226
static PyObject *
227227
get_error_line_from_source(Parser *p, Py_ssize_t lineno)
228228
{
229-
const char *cur_line = _PyTok_SourceData(&p->tok->source);
230-
231229
Py_ssize_t relative_lineno = p->starting_lineno ? lineno - p->starting_lineno + 1 : lineno;
232-
const char *buf_end = cur_line + p->tok->source.len;
233-
234-
for (int i = 0; i < relative_lineno - 1; i++) {
235-
const char *new_line = memchr(cur_line, '\n', buf_end - cur_line);
236-
if (new_line == NULL) {
237-
break;
238-
}
239-
cur_line = new_line + 1;
240-
}
241-
242-
const char *next_newline = memchr(cur_line, '\n', buf_end - cur_line);
243-
next_newline = next_newline != NULL ? next_newline : buf_end;
244-
return PyUnicode_DecodeUTF8(cur_line, next_newline - cur_line, "replace");
230+
Py_ssize_t len;
231+
const char *line = _PyTok_SourceLineView(&p->tok->source, relative_lineno, &len);
232+
return PyUnicode_DecodeUTF8(line, len, "replace");
245233
}
246234

247235
void *

Parser/tokenizer/source.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,26 @@ _PyTok_SourceAppendLine(_PyTok_SourceText *source, const char *bytes,
134134
return start;
135135
}
136136

137+
const char *
138+
_PyTok_SourceLineView(const _PyTok_SourceText *source, Py_ssize_t lineno,
139+
Py_ssize_t *len)
140+
{
141+
assert(len != NULL);
142+
const char *line = _PyTok_SourceData(source);
143+
const char *end = line + source->len;
144+
while (lineno > 1) {
145+
const char *newline = memchr(line, '\n', end - line);
146+
if (newline == NULL) {
147+
break;
148+
}
149+
line = newline + 1;
150+
lineno--;
151+
}
152+
const char *newline = memchr(line, '\n', end - line);
153+
*len = (newline != NULL ? newline : end) - line;
154+
return line;
155+
}
156+
137157
int
138158
_PyTok_SourceLineIsImplicit(const _PyTok_SourceText *source, int lineno)
139159
{

Parser/tokenizer/source.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ PyAPI_FUNC(void) _PyTok_SourceClear(_PyTok_SourceText *);
4343
PyAPI_FUNC(_PyTok_Off) _PyTok_SourceAppendLine(
4444
_PyTok_SourceText *source, const char *bytes, Py_ssize_t len,
4545
int implicit_newline);
46+
/* Return borrowed bytes excluding '\n', writing the byte length to *len.
47+
Line numbers are 1-based and clamp to the first or final line; a trailing
48+
'\n' adds an empty final line. The view need not be NUL-terminated.
49+
This does not set an exception. Append and clear invalidate the view. */
50+
PyAPI_FUNC(const char *) _PyTok_SourceLineView(
51+
const _PyTok_SourceText *source, Py_ssize_t lineno, Py_ssize_t *len);
4652
/* Return false for invalid line numbers and the virtual EOF line. */
4753
PyAPI_FUNC(int) _PyTok_SourceLineIsImplicit(
4854
const _PyTok_SourceText *, int);

0 commit comments

Comments
 (0)