diff options
author | Guido van Rossum <guido@python.org> | 2020-05-15 02:22:48 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-05-15 02:22:48 (GMT) |
commit | 15bc9ab301d73f20bff47a12ef05326feb40f797 (patch) | |
tree | e29afe19a6ce7aaa5b7f1ea3639810080ea6d2d5 /Python/pythonrun.c | |
parent | 1aa8767baf498a920f0461d1088772a12dcb4d20 (diff) | |
download | cpython-15bc9ab301d73f20bff47a12ef05326feb40f797.zip cpython-15bc9ab301d73f20bff47a12ef05326feb40f797.tar.gz cpython-15bc9ab301d73f20bff47a12ef05326feb40f797.tar.bz2 |
bpo-40612: Fix SyntaxError edge cases in traceback formatting (GH-20072)
This fixes both the traceback.py module and the C code for formatting syntax errors (in Python/pythonrun.c). They now both consistently do the following:
- Suppress caret if it points left of text
- Allow caret pointing just past end of line
- If caret points past end of line, clip to *just* past end of line
The syntax error formatting code in traceback.py was mostly rewritten; small, subtle changes were applied to the C code in pythonrun.c.
There's still a difference when the text contains embedded newlines. Neither handles these very well, and I don't think the case occurs in practice.
Automerge-Triggered-By: @gvanrossum
Diffstat (limited to 'Python/pythonrun.c')
-rw-r--r-- | Python/pythonrun.c | 66 |
1 files changed, 47 insertions, 19 deletions
diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 45f08b7..160f44d 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -554,37 +554,65 @@ finally: static void print_error_text(PyObject *f, int offset, PyObject *text_obj) { - const char *text; - const char *nl; - - text = PyUnicode_AsUTF8(text_obj); + /* Convert text to a char pointer; return if error */ + const char *text = PyUnicode_AsUTF8(text_obj); if (text == NULL) return; - if (offset >= 0) { - if (offset > 0 && (size_t)offset == strlen(text) && text[offset - 1] == '\n') - offset--; - for (;;) { - nl = strchr(text, '\n'); - if (nl == NULL || nl-text >= offset) - break; - offset -= (int)(nl+1-text); - text = nl+1; + /* Convert offset from 1-based to 0-based */ + offset--; + + /* Strip leading whitespace from text, adjusting offset as we go */ + while (*text == ' ' || *text == '\t' || *text == '\f') { + text++; + offset--; + } + + /* Calculate text length excluding trailing newline */ + Py_ssize_t len = strlen(text); + if (len > 0 && text[len-1] == '\n') { + len--; + } + + /* Clip offset to at most len */ + if (offset > len) { + offset = len; + } + + /* Skip past newlines embedded in text */ + for (;;) { + const char *nl = strchr(text, '\n'); + if (nl == NULL) { + break; } - while (*text == ' ' || *text == '\t' || *text == '\f') { - text++; - offset--; + Py_ssize_t inl = nl - text; + if (inl >= (Py_ssize_t)offset) { + break; } + inl += 1; + text += inl; + len -= inl; + offset -= (int)inl; } + + /* Print text */ PyFile_WriteString(" ", f); PyFile_WriteString(text, f); - if (*text == '\0' || text[strlen(text)-1] != '\n') + + /* Make sure there's a newline at the end */ + if (text[len] != '\n') { PyFile_WriteString("\n", f); - if (offset == -1) + } + + /* Don't print caret if it points to the left of the text */ + if (offset < 0) return; + + /* Write caret line */ PyFile_WriteString(" ", f); - while (--offset > 0) + while (--offset >= 0) { PyFile_WriteString(" ", f); + } PyFile_WriteString("^\n", f); } |