/src/cpython/Parser/pegen_errors.c
Line | Count | Source |
1 | | #include <Python.h> |
2 | | #include <errcode.h> |
3 | | |
4 | | #include "pycore_pyerrors.h" // _PyErr_ProgramDecodedTextObject() |
5 | | #include "pycore_runtime.h" // _Py_ID() |
6 | | #include "pycore_tuple.h" // _PyTuple_FromPair |
7 | | #include "pegen.h" |
8 | | #include "tokenizer/tokenizer.h" |
9 | | |
10 | | // TOKENIZER ERRORS |
11 | | |
12 | | void * |
13 | | _PyPegen_raise_incomplete_input(Parser *p) |
14 | 0 | { |
15 | 0 | if ((p->flags & PyPARSE_ALLOW_INCOMPLETE_INPUT) && !PyErr_Occurred()) { |
16 | 0 | return _PyPegen_raise_error(p, PyExc_IncompleteInputError, 0, |
17 | 0 | "incomplete input"); |
18 | 0 | } |
19 | 0 | return NULL; |
20 | 0 | } |
21 | | |
22 | | static inline void |
23 | 1.98k | raise_unclosed_parentheses_error(Parser *p) { |
24 | 1.98k | _PyTokenizer_Info info = _PyTokenizer_GetInfo(p->tok); |
25 | 1.98k | int error_lineno = info.delimiter_loc.lineno; |
26 | 1.98k | int error_col = info.delimiter_loc.byte_col; |
27 | 1.98k | RAISE_ERROR_KNOWN_LOCATION(p, PyExc_SyntaxError, |
28 | 1.98k | error_lineno, error_col, error_lineno, -1, |
29 | 1.98k | "'%c' was never closed", |
30 | 1.98k | info.delimiter); |
31 | 1.98k | } |
32 | | |
33 | | int |
34 | | _Pypegen_tokenizer_error(Parser *p) |
35 | 3.86k | { |
36 | 3.86k | if (PyErr_Occurred()) { |
37 | 1.88k | return -1; |
38 | 1.88k | } |
39 | | |
40 | 1.97k | const char *msg = NULL; |
41 | 1.97k | PyObject* errtype = PyExc_SyntaxError; |
42 | 1.97k | Py_ssize_t col_offset = -1; |
43 | 1.97k | p->error_indicator = 1; |
44 | 1.97k | _PyTokenizer_Info info = _PyTokenizer_GetInfo(p->tok); |
45 | 1.97k | if ((p->flags & PyPARSE_ALLOW_INCOMPLETE_INPUT) && |
46 | 0 | (info.status == E_EOF || info.status == E_INCOMPLETE)) |
47 | 0 | { |
48 | 0 | _PyPegen_raise_incomplete_input(p); |
49 | 0 | return -1; |
50 | 0 | } |
51 | 1.97k | switch (info.status) { |
52 | 0 | case E_INCOMPLETE: |
53 | | // The diagnostic pass rejected this unfinished token. Let the |
54 | | // parser report the syntax error at its original failure point. |
55 | 0 | return -1; |
56 | 0 | case E_TOKEN: |
57 | 0 | msg = "invalid token"; |
58 | 0 | break; |
59 | 1.93k | case E_EOF: |
60 | 1.93k | if (info.level) { |
61 | 1.88k | raise_unclosed_parentheses_error(p); |
62 | 1.88k | } else { |
63 | 41 | RAISE_SYNTAX_ERROR("unexpected EOF while parsing"); |
64 | 41 | } |
65 | 1.93k | return -1; |
66 | 7 | case E_DEDENT: |
67 | 7 | RAISE_INDENTATION_ERROR("unindent does not match any outer indentation level"); |
68 | 7 | return -1; |
69 | 0 | case E_INTR: |
70 | 0 | if (!PyErr_Occurred()) { |
71 | 0 | PyErr_SetNone(PyExc_KeyboardInterrupt); |
72 | 0 | } |
73 | 0 | return -1; |
74 | 0 | case E_NOMEM: |
75 | 0 | PyErr_NoMemory(); |
76 | 0 | return -1; |
77 | 1 | case E_TABSPACE: |
78 | 1 | errtype = PyExc_TabError; |
79 | 1 | msg = "inconsistent use of tabs and spaces in indentation"; |
80 | 1 | break; |
81 | 0 | case E_TOODEEP: |
82 | 0 | errtype = PyExc_IndentationError; |
83 | 0 | msg = "too many levels of indentation"; |
84 | 0 | break; |
85 | 40 | case E_LINECONT: { |
86 | 40 | col_offset = info.cursor - info.line_span.start - 1; |
87 | 40 | msg = "unexpected character after line continuation character"; |
88 | 40 | break; |
89 | 0 | } |
90 | 0 | case E_COLUMNOVERFLOW: |
91 | 0 | PyErr_SetString(PyExc_OverflowError, |
92 | 0 | "Parser column offset overflow - source line is too big"); |
93 | 0 | return -1; |
94 | 0 | default: |
95 | 0 | msg = "unknown parsing error"; |
96 | 1.97k | } |
97 | | |
98 | 41 | RAISE_ERROR_KNOWN_LOCATION(p, errtype, info.location.lineno, |
99 | 41 | col_offset >= 0 ? col_offset : 0, |
100 | 41 | info.location.lineno, -1, msg); |
101 | 41 | return -1; |
102 | 1.97k | } |
103 | | |
104 | | int |
105 | | _Pypegen_raise_decode_error(Parser *p) |
106 | 120 | { |
107 | 120 | return _Pypegen_raise_decode_error_known_location(p, NULL); |
108 | 120 | } |
109 | | |
110 | | int |
111 | | _Pypegen_raise_decode_error_known_location(Parser *p, expr_ty a) |
112 | 129 | { |
113 | 129 | assert(PyErr_Occurred()); |
114 | 129 | const char *errtype = NULL; |
115 | 129 | if (PyErr_ExceptionMatches(PyExc_UnicodeError)) { |
116 | 121 | errtype = "unicode error"; |
117 | 121 | } |
118 | 8 | else if (PyErr_ExceptionMatches(PyExc_ValueError)) { |
119 | 4 | errtype = "value error"; |
120 | 4 | } |
121 | 129 | if (errtype) { |
122 | 125 | PyObject *type; |
123 | 125 | PyObject *value; |
124 | 125 | PyObject *tback; |
125 | 125 | PyObject *errstr; |
126 | 125 | PyErr_Fetch(&type, &value, &tback); |
127 | 125 | errstr = PyObject_Str(value); |
128 | 125 | if (errstr) { |
129 | 125 | if (a != NULL) { |
130 | 9 | RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "(%s) %U", errtype, errstr); |
131 | 9 | } |
132 | 116 | else { |
133 | 116 | RAISE_SYNTAX_ERROR("(%s) %U", errtype, errstr); |
134 | 116 | } |
135 | 125 | Py_DECREF(errstr); |
136 | 125 | } |
137 | 0 | else { |
138 | 0 | PyErr_Clear(); |
139 | 0 | if (a != NULL) { |
140 | 0 | RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "(%s) unknown error", errtype); |
141 | 0 | } |
142 | 0 | else { |
143 | 0 | RAISE_SYNTAX_ERROR("(%s) unknown error", errtype); |
144 | 0 | } |
145 | 0 | } |
146 | 125 | Py_XDECREF(type); |
147 | 125 | Py_XDECREF(value); |
148 | 125 | Py_XDECREF(tback); |
149 | 125 | } |
150 | | |
151 | 129 | return -1; |
152 | 129 | } |
153 | | |
154 | | static int |
155 | 61.0k | _PyPegen_tokenize_full_source_to_check_for_errors(Parser *p) { |
156 | | // Tokenize the whole input to see if there are any tokenization |
157 | | // errors such as mismatching parentheses. These will get priority |
158 | | // over generic syntax errors only if the line number of the error is |
159 | | // before the one that we had for the generic error. |
160 | | |
161 | | // We don't want to tokenize to the end for interactive input |
162 | 61.0k | if (_PyTokenizer_IsInteractive(p->tok)) { |
163 | 0 | return 0; |
164 | 0 | } |
165 | | |
166 | 61.0k | PyObject *type, *value, *traceback; |
167 | 61.0k | PyErr_Fetch(&type, &value, &traceback); |
168 | | |
169 | 61.0k | Token *current_token = p->known_err_token != NULL ? p->known_err_token : p->tokens[p->fill - 1]; |
170 | 61.0k | Py_ssize_t current_err_line = current_token->lineno; |
171 | | |
172 | 61.0k | int ret = 0; |
173 | 61.0k | struct token new_token; |
174 | 61.0k | _PyToken_Init(&new_token); |
175 | | |
176 | 267k | for (;;) { |
177 | 267k | _PyTokenizer_Get(p->tok, &new_token); |
178 | 267k | switch (new_token.type) { |
179 | 3.04k | case ERRORTOKEN: { |
180 | 3.04k | if (PyErr_Occurred()) { |
181 | 682 | ret = -1; |
182 | 682 | goto exit; |
183 | 682 | } |
184 | 2.36k | _PyTokenizer_Info info = _PyTokenizer_GetInfo(p->tok); |
185 | 2.36k | if (info.level != 0) { |
186 | 2.33k | int error_lineno = info.delimiter_loc.lineno; |
187 | 2.33k | if (current_err_line > error_lineno) { |
188 | 96 | raise_unclosed_parentheses_error(p); |
189 | 96 | ret = -1; |
190 | 96 | goto exit; |
191 | 96 | } |
192 | 2.33k | } |
193 | 2.26k | break; |
194 | 2.36k | } |
195 | 57.9k | case ENDMARKER: |
196 | 57.9k | break; |
197 | 206k | default: |
198 | 206k | continue; |
199 | 267k | } |
200 | 60.2k | break; |
201 | 267k | } |
202 | | |
203 | | |
204 | 61.0k | exit: |
205 | 61.0k | _PyToken_Free(&new_token); |
206 | 61.0k | _PyTokenizer_Info info = _PyTokenizer_GetInfo(p->tok); |
207 | | // Preserve expression errors over later formatted-string errors. |
208 | 61.0k | if (PyErr_Occurred() && !info.in_formatted_string) { |
209 | 560 | Py_XDECREF(value); |
210 | 560 | Py_XDECREF(type); |
211 | 560 | Py_XDECREF(traceback); |
212 | 60.4k | } else { |
213 | 60.4k | PyErr_Restore(type, value, traceback); |
214 | 60.4k | } |
215 | 61.0k | return ret; |
216 | 61.0k | } |
217 | | |
218 | | // PARSER ERRORS |
219 | | |
220 | | void * |
221 | | _PyPegen_raise_error(Parser *p, PyObject *errtype, int use_mark, const char *errmsg, ...) |
222 | 1.17k | { |
223 | | // Bail out if we already have an error set. |
224 | 1.17k | if (p->error_indicator && PyErr_Occurred()) { |
225 | 245 | return NULL; |
226 | 245 | } |
227 | 930 | if (p->fill == 0) { |
228 | 0 | va_list va; |
229 | 0 | va_start(va, errmsg); |
230 | 0 | _PyPegen_raise_error_known_location(p, errtype, 0, 0, 0, -1, errmsg, va); |
231 | 0 | va_end(va); |
232 | 0 | return NULL; |
233 | 0 | } |
234 | 930 | if (use_mark && p->mark == p->fill && _PyPegen_fill_token(p) < 0) { |
235 | 0 | p->error_indicator = 1; |
236 | 0 | return NULL; |
237 | 0 | } |
238 | 930 | Token *t = p->known_err_token != NULL |
239 | 930 | ? p->known_err_token |
240 | 930 | : p->tokens[use_mark ? p->mark : p->fill - 1]; |
241 | 930 | Py_ssize_t col_offset; |
242 | 930 | Py_ssize_t end_col_offset = -1; |
243 | 930 | if (t->col_offset == -1) { |
244 | 218 | _PyTokenizer_Info info = _PyTokenizer_GetInfo(p->tok); |
245 | 218 | if (info.diagnostic.location.lineno != 0) { |
246 | 0 | col_offset = info.diagnostic.location.byte_col; |
247 | 218 | } else if (info.cursor == info.input_span.start) { |
248 | 4 | col_offset = 0; |
249 | 214 | } else { |
250 | 214 | col_offset = Py_SAFE_DOWNCAST( |
251 | 214 | info.cursor - info.line_span.start, intptr_t, int); |
252 | 214 | } |
253 | 712 | } else { |
254 | 712 | col_offset = t->col_offset + 1; |
255 | 712 | } |
256 | | |
257 | 930 | if (t->end_col_offset != -1) { |
258 | 712 | end_col_offset = t->end_col_offset + 1; |
259 | 712 | } |
260 | | |
261 | 930 | va_list va; |
262 | 930 | va_start(va, errmsg); |
263 | 930 | _PyPegen_raise_error_known_location(p, errtype, t->lineno, col_offset, t->end_lineno, end_col_offset, errmsg, va); |
264 | 930 | va_end(va); |
265 | | |
266 | 930 | return NULL; |
267 | 930 | } |
268 | | |
269 | | static PyObject * |
270 | | get_error_line_from_source(Parser *p, Py_ssize_t lineno) |
271 | 310 | { |
272 | 310 | if (_PyTokenizer_RetainedSource(p->tok) == NULL) { |
273 | 0 | return Py_GetConstant(Py_CONSTANT_EMPTY_STR); |
274 | 0 | } |
275 | 310 | Py_ssize_t relative_lineno = p->starting_lineno ? lineno - p->starting_lineno + 1 : lineno; |
276 | 310 | Py_ssize_t len; |
277 | 310 | const char *line = _PyTokenizer_LineView(p->tok, relative_lineno, &len); |
278 | 310 | return PyUnicode_DecodeUTF8(line, len, "replace"); |
279 | 310 | } |
280 | | |
281 | | void * |
282 | | _PyPegen_raise_error_known_location(Parser *p, PyObject *errtype, |
283 | | Py_ssize_t lineno, Py_ssize_t col_offset, |
284 | | Py_ssize_t end_lineno, Py_ssize_t end_col_offset, |
285 | | const char *errmsg, va_list va) |
286 | 63.8k | { |
287 | | // Bail out if we already have an error set. |
288 | 63.8k | if (p->error_indicator && PyErr_Occurred()) { |
289 | 627 | return NULL; |
290 | 627 | } |
291 | 63.2k | PyObject *value = NULL; |
292 | 63.2k | PyObject *errstr = NULL; |
293 | 63.2k | PyObject *error_line = NULL; |
294 | 63.2k | PyObject *tmp = NULL; |
295 | 63.2k | p->error_indicator = 1; |
296 | 63.2k | _PyTokenizer_Info info = _PyTokenizer_GetInfo(p->tok); |
297 | 63.2k | _PyTok_Loc location = info.location; |
298 | 63.2k | _PyTok_Span text_span = info.line_span; |
299 | | |
300 | 63.2k | if (errtype == PyExc_IncompleteInputError && |
301 | 0 | info.diagnostic.location.lineno != 0) { |
302 | 0 | location = info.diagnostic.location; |
303 | 0 | text_span = info.diagnostic.text_span; |
304 | 0 | lineno = end_lineno = location.lineno; |
305 | 0 | col_offset = location.byte_col; |
306 | 0 | end_col_offset = -1; |
307 | 0 | } |
308 | | |
309 | 63.2k | if (end_lineno == CURRENT_POS) { |
310 | 31 | end_lineno = location.lineno; |
311 | 31 | } |
312 | 63.2k | if (end_col_offset == CURRENT_POS) { |
313 | 31 | end_col_offset = location.byte_col; |
314 | 31 | } |
315 | | |
316 | 63.2k | errstr = PyUnicode_FromFormatV(errmsg, va); |
317 | 63.2k | if (!errstr) { |
318 | 0 | goto error; |
319 | 0 | } |
320 | | |
321 | 63.2k | if (info.is_interactive && _PyTokenizer_RetainedSource(p->tok) != NULL) { |
322 | 0 | error_line = get_error_line_from_source(p, lineno); |
323 | 0 | } |
324 | 63.2k | else if (p->start_rule == Py_file_input) { |
325 | 63.2k | error_line = _PyErr_ProgramDecodedTextObject(info.filename, |
326 | 63.2k | (int) lineno, info.encoding); |
327 | 63.2k | } |
328 | | |
329 | 63.2k | if (!error_line) { |
330 | | /* PyErr_ProgramTextObject was not called or returned NULL. If it was not called, |
331 | | then we need to find the error line from some other source, because |
332 | | p->start_rule != Py_file_input. If it returned NULL, then it either unexpectedly |
333 | | failed or we're parsing from a string or the REPL. There's a third edge case where |
334 | | we're actually parsing from a file, which has an E_EOF SyntaxError and in that case |
335 | | `PyErr_ProgramTextObject` fails because lineno points to last_file_line + 1, which |
336 | | does not physically exist */ |
337 | 63.2k | assert(!info.is_file || info.status == E_EOF); |
338 | | |
339 | 63.2k | if (location.lineno <= lineno && |
340 | 62.9k | info.input_span.end > info.input_span.start) { |
341 | 62.9k | Py_ssize_t size; |
342 | 62.9k | const char *line = _PyTokenizer_SpanView( |
343 | 62.9k | p->tok, text_span, &size); |
344 | 62.9k | if (errtype == PyExc_IncompleteInputError && size > 0 && |
345 | 0 | (info.implicit_newline == _PyTokenizer_APPEND_NEWLINE || |
346 | 0 | (info.implicit_newline == _PyTokenizer_IMPLY_NEWLINE && |
347 | 0 | p->start_rule != Py_file_input))) { |
348 | 0 | size--; |
349 | 0 | } |
350 | 62.9k | error_line = PyUnicode_DecodeUTF8(line, size, "replace"); |
351 | 62.9k | } |
352 | 270 | else if (!info.is_file) { |
353 | 270 | error_line = get_error_line_from_source(p, lineno); |
354 | 270 | } |
355 | 0 | else { |
356 | 0 | error_line = Py_GetConstant(Py_CONSTANT_EMPTY_STR); |
357 | 0 | } |
358 | 63.2k | if (!error_line) { |
359 | 0 | goto error; |
360 | 0 | } |
361 | 63.2k | } |
362 | | |
363 | 63.2k | Py_ssize_t col_number = col_offset; |
364 | 63.2k | Py_ssize_t end_col_number = end_col_offset; |
365 | | |
366 | 63.2k | col_number = _PyPegen_byte_offset_to_character_offset(error_line, col_offset); |
367 | 63.2k | if (col_number < 0) { |
368 | 0 | goto error; |
369 | 0 | } |
370 | | |
371 | 63.2k | if (end_col_offset > 0) { |
372 | 60.9k | PyObject *end_line = end_lineno != lineno && _PyTokenizer_RetainedSource(p->tok) != NULL |
373 | 60.9k | ? get_error_line_from_source(p, end_lineno) : Py_NewRef(error_line); |
374 | 60.9k | if (end_line == NULL) { |
375 | 0 | goto error; |
376 | 0 | } |
377 | 60.9k | end_col_number = _PyPegen_byte_offset_to_character_offset(end_line, end_col_offset); |
378 | 60.9k | Py_DECREF(end_line); |
379 | 60.9k | if (end_col_number < 0) { |
380 | 0 | goto error; |
381 | 0 | } |
382 | 60.9k | } |
383 | | |
384 | 63.2k | tmp = Py_BuildValue("(OnnNnn)", info.filename, lineno, col_number, error_line, end_lineno, end_col_number); |
385 | 63.2k | error_line = NULL; |
386 | 63.2k | if (!tmp) { |
387 | 0 | goto error; |
388 | 0 | } |
389 | 63.2k | value = _PyTuple_FromPair(errstr, tmp); |
390 | 63.2k | Py_DECREF(tmp); |
391 | 63.2k | if (!value) { |
392 | 0 | goto error; |
393 | 0 | } |
394 | 63.2k | PyErr_SetObject(errtype, value); |
395 | | |
396 | 63.2k | Py_DECREF(errstr); |
397 | 63.2k | Py_DECREF(value); |
398 | 63.2k | return NULL; |
399 | | |
400 | 0 | error: |
401 | 0 | Py_XDECREF(errstr); |
402 | 0 | Py_XDECREF(error_line); |
403 | 0 | return NULL; |
404 | 63.2k | } |
405 | | |
406 | | void |
407 | 65.0k | _Pypegen_set_syntax_error(Parser* p, Token* last_token) { |
408 | 65.0k | _PyTokenizer_Info info = _PyTokenizer_GetInfo(p->tok); |
409 | | // Existing syntax error |
410 | 65.0k | if (PyErr_Occurred()) { |
411 | | // Prioritize tokenizer errors to custom syntax errors raised |
412 | | // on the second phase only if the errors come from the parser. |
413 | 5.94k | int is_tok_ok = (info.status == E_DONE || info.status == E_OK); |
414 | 5.94k | if (is_tok_ok && PyErr_ExceptionMatches(PyExc_SyntaxError)) { |
415 | 1.91k | _PyPegen_tokenize_full_source_to_check_for_errors(p); |
416 | 1.91k | } |
417 | | // Propagate the existing syntax error. |
418 | 5.94k | return; |
419 | 5.94k | } |
420 | | // Initialization error |
421 | 59.1k | if (p->fill == 0) { |
422 | 0 | RAISE_SYNTAX_ERROR("error at start before reading any input"); |
423 | 0 | } |
424 | | // Parser encountered EOF (End of File) unexpectedtly |
425 | 59.1k | if (last_token->type == ERRORTOKEN && info.status == E_EOF) { |
426 | 0 | if (info.level) { |
427 | 0 | raise_unclosed_parentheses_error(p); |
428 | 0 | } else { |
429 | 0 | RAISE_SYNTAX_ERROR("unexpected EOF while parsing"); |
430 | 0 | } |
431 | 0 | return; |
432 | 0 | } |
433 | | // Indentation error in the tokenizer |
434 | 59.1k | if (last_token->type == INDENT || last_token->type == DEDENT) { |
435 | 59 | RAISE_INDENTATION_ERROR(last_token->type == INDENT ? "unexpected indent" : "unexpected unindent"); |
436 | 59 | return; |
437 | 59 | } |
438 | | // Unknown error (generic case) |
439 | | |
440 | | // Use the last token we found on the first pass to avoid reporting |
441 | | // incorrect locations for generic syntax errors just because we reached |
442 | | // further away when trying to find specific syntax errors in the second |
443 | | // pass. |
444 | 59.0k | RAISE_SYNTAX_ERROR_KNOWN_LOCATION(last_token, "invalid syntax"); |
445 | | // _PyPegen_tokenize_full_source_to_check_for_errors will override the existing |
446 | | // generic SyntaxError we just raised if errors are found. |
447 | 59.0k | _PyPegen_tokenize_full_source_to_check_for_errors(p); |
448 | 59.0k | } |
449 | | |
450 | | void |
451 | | _Pypegen_stack_overflow(Parser *p) |
452 | 56 | { |
453 | 56 | p->error_indicator = 1; |
454 | 56 | PyErr_SetString(PyExc_MemoryError, |
455 | 56 | "Parser stack overflowed - Python source too complex to parse"); |
456 | 56 | } |