Skip to content

Commit 177eca5

Browse files
committed
gh-153569: borrow diagnostic lines through the source API
1 parent 2b393f8 commit 177eca5

4 files changed

Lines changed: 64 additions & 20 deletions

File tree

Modules/_testinternalcapi/tokenizer.c

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ 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 int
2738
same_cursor(const _PyTok_Cursor *left, const _PyTok_Cursor *right)
2839
{
@@ -40,6 +51,10 @@ test_tokenizer_source(PyObject *Py_UNUSED(module),
4051
_PyTok_SourceText source;
4152
_PyTok_SourceInit(&source);
4253

54+
if (check_line_view(&source, 1, "") < 0) {
55+
goto error;
56+
}
57+
4358
_PyTok_Loc loc;
4459
_PyTok_Line line;
4560
if (check(_PyTok_SourceLocation(
@@ -67,10 +82,20 @@ test_tokenizer_source(PyObject *Py_UNUSED(module),
6782
"wrong first source offset") < 0 ||
6883
check(_PyTok_SourceAppendLine(
6984
&source, "\xce\xb2\n", 3, 1) == 6,
70-
"wrong second source offset") < 0 ||
71-
check(_PyTok_SourceAppendLine(
72-
&source, "nul\0x\n", 6, 0) == 9,
73-
"wrong third source offset") < 0) {
85+
"wrong second source offset") < 0) {
86+
goto error;
87+
}
88+
89+
if (check_line_view(&source, PY_SSIZE_T_MIN, "alpha") < 0 ||
90+
check_line_view(&source, 1, "alpha") < 0 ||
91+
check_line_view(&source, 2, "\xce\xb2") < 0 ||
92+
check_line_view(&source, 3, "") < 0 ||
93+
check_line_view(&source, PY_SSIZE_T_MAX, "") < 0) {
94+
goto error;
95+
}
96+
97+
if (check(_PyTok_SourceAppendLine(&source, "nul\0x\n", 6, 0) == 9,
98+
"wrong third source offset") < 0) {
7499
goto error;
75100
}
76101

@@ -195,6 +220,11 @@ test_tokenizer_source(PyObject *Py_UNUSED(module),
195220
goto error;
196221
}
197222

223+
if (check_line_view(&source, 1, "tail") < 0 ||
224+
check_line_view(&source, PY_SSIZE_T_MAX, "tail") < 0) {
225+
goto error;
226+
}
227+
198228
_PyTok_SourceDiscard(&source);
199229
if (check(_PyTok_SourceAppendLine(&source, "a\n", 2, 0) == 4,
200230
"wrong retained source offset") < 0 ||

Parser/pegen_errors.c

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -227,25 +227,13 @@ _PyPegen_raise_error(Parser *p, PyObject *errtype, int use_mark, const char *err
227227
static PyObject *
228228
get_error_line_from_source(Parser *p, Py_ssize_t lineno)
229229
{
230-
const char *cur_line = _PyTokenizer_RetainedSource(p->tok);
231-
if (cur_line == NULL) {
230+
if (_PyTokenizer_RetainedSource(p->tok) == NULL) {
232231
return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
233232
}
234-
235233
Py_ssize_t relative_lineno = p->starting_lineno ? lineno - p->starting_lineno + 1 : lineno;
236-
const char *buf_end = cur_line + p->tok->source.len;
237-
238-
for (int i = 0; i < relative_lineno - 1; i++) {
239-
const char *new_line = memchr(cur_line, '\n', buf_end - cur_line);
240-
if (new_line == NULL) {
241-
break;
242-
}
243-
cur_line = new_line + 1;
244-
}
245-
246-
const char *next_newline = memchr(cur_line, '\n', buf_end - cur_line);
247-
next_newline = next_newline != NULL ? next_newline : buf_end;
248-
return PyUnicode_DecodeUTF8(cur_line, next_newline - cur_line, "replace");
234+
Py_ssize_t len;
235+
const char *line = _PyTok_SourceLineView(&p->tok->source, relative_lineno, &len);
236+
return PyUnicode_DecodeUTF8(line, len, "replace");
249237
}
250238

251239
void *

Parser/tokenizer/source.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,26 @@ _PyTok_SourceAppendLine(_PyTok_SourceText *source, const char *bytes,
190190
return source->base_offset + start;
191191
}
192192

193+
const char *
194+
_PyTok_SourceLineView(const _PyTok_SourceText *source, Py_ssize_t lineno,
195+
Py_ssize_t *len)
196+
{
197+
assert(len != NULL);
198+
const char *line = _PyTok_SourceData(source);
199+
const char *end = line + source->len;
200+
while (lineno > 1) {
201+
const char *newline = memchr(line, '\n', end - line);
202+
if (newline == NULL) {
203+
break;
204+
}
205+
line = newline + 1;
206+
lineno--;
207+
}
208+
const char *newline = memchr(line, '\n', end - line);
209+
*len = (newline != NULL ? newline : end) - line;
210+
return line;
211+
}
212+
193213
const char *
194214
_PyTok_SourceSpanView(const _PyTok_SourceText *source, _PyTok_Span span,
195215
Py_ssize_t *len)

Parser/tokenizer/source.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ PyAPI_FUNC(void) _PyTok_SourceDiscard(_PyTok_SourceText *);
6262
PyAPI_FUNC(_PyTok_Off) _PyTok_SourceAppendLine(
6363
_PyTok_SourceText *source, const char *bytes, Py_ssize_t len,
6464
int implicit_newline);
65+
/* Return borrowed bytes excluding '\n', writing the byte length to *len.
66+
Line numbers are 1-based and clamp to the first or final line; a trailing
67+
'\n' adds an empty final line. The view need not be NUL-terminated.
68+
This does not set an exception. Append, discard, and clear invalidate the view. */
69+
PyAPI_FUNC(const char *) _PyTok_SourceLineView(
70+
const _PyTok_SourceText *source, Py_ssize_t lineno, Py_ssize_t *len);
6571
/* The returned view is invalidated by SourceAppendLine and SourceClear. */
6672
PyAPI_FUNC(const char *) _PyTok_SourceSpanView(
6773
const _PyTok_SourceText *, _PyTok_Span, Py_ssize_t *);

0 commit comments

Comments
 (0)