/src/cpython/Parser/string_parser.c
Line | Count | Source |
1 | | #include <Python.h> |
2 | | #include "pycore_bytesobject.h" // _PyBytes_DecodeEscape() |
3 | | #include "pycore_unicodeobject.h" // _PyUnicode_DecodeUnicodeEscapeInternal() |
4 | | |
5 | | #include "pegen.h" |
6 | | #include "string_parser.h" |
7 | | |
8 | | #include <stdbool.h> |
9 | | |
10 | | //// STRING HANDLING FUNCTIONS //// |
11 | | |
12 | | static int |
13 | | warn_invalid_escape_sequence(Parser *p, const char* buffer, const char *first_invalid_escape, Token *t) |
14 | 7.51k | { |
15 | 7.51k | if (p->call_invalid_rules) { |
16 | | // Do not report warnings if we are in the second pass of the parser |
17 | | // to avoid showing the warning twice. |
18 | 3.21k | return 0; |
19 | 3.21k | } |
20 | 4.30k | unsigned char c = (unsigned char)*first_invalid_escape; |
21 | 4.30k | if ((t->type == FSTRING_MIDDLE || t->type == FSTRING_END || t->type == TSTRING_MIDDLE || t->type == TSTRING_END) |
22 | 3.03k | && (c == '{' || c == '}')) { |
23 | | // in this case the tokenizer has already emitted a warning, |
24 | | // see Parser/tokenizer/helpers.c:warn_invalid_escape_sequence |
25 | 827 | return 0; |
26 | 827 | } |
27 | | |
28 | 3.47k | int octal = ('4' <= c && c <= '7'); |
29 | 3.47k | PyObject *msg = |
30 | 3.47k | octal |
31 | 3.47k | ? PyUnicode_FromFormat( |
32 | 531 | "\"\\%.3s\" is an invalid octal escape sequence. " |
33 | 531 | "Such sequences will not work in the future. " |
34 | 531 | "Did you mean \"\\\\%.3s\"? A raw string is also an option.", |
35 | 531 | first_invalid_escape, first_invalid_escape) |
36 | 3.47k | : PyUnicode_FromFormat( |
37 | 2.94k | "\"\\%c\" is an invalid escape sequence. " |
38 | 2.94k | "Such sequences will not work in the future. " |
39 | 2.94k | "Did you mean \"\\\\%c\"? A raw string is also an option.", |
40 | 2.94k | c, c); |
41 | 3.47k | if (msg == NULL) { |
42 | 0 | return -1; |
43 | 0 | } |
44 | 3.47k | PyObject *category; |
45 | 3.47k | if (p->feature_version >= 12) { |
46 | 3.47k | category = PyExc_SyntaxWarning; |
47 | 3.47k | } |
48 | 0 | else { |
49 | 0 | category = PyExc_DeprecationWarning; |
50 | 0 | } |
51 | | |
52 | | // Calculate the lineno and the col_offset of the invalid escape sequence |
53 | 3.47k | const char *start = buffer; |
54 | 3.47k | const char *end = first_invalid_escape; |
55 | 3.47k | int lineno = t->lineno; |
56 | 3.47k | int col_offset = t->col_offset; |
57 | 33.9k | while (start < end) { |
58 | 30.5k | if (*start == '\n') { |
59 | 1.80k | lineno++; |
60 | 1.80k | col_offset = 0; |
61 | 1.80k | } |
62 | 28.7k | else { |
63 | 28.7k | col_offset++; |
64 | 28.7k | } |
65 | 30.5k | start++; |
66 | 30.5k | } |
67 | | |
68 | | // Count the number of quotes in the token |
69 | 3.47k | char first_quote = 0; |
70 | 3.47k | if (lineno == t->lineno) { |
71 | 3.26k | int quote_count = 0; |
72 | 3.26k | const char* tok = PyBytes_AsString(t->bytes); |
73 | 5.72k | for (int i = 0; i < PyBytes_Size(t->bytes); i++) { |
74 | 4.45k | if (tok[i] == '\'' || tok[i] == '\"') { |
75 | 2.45k | if (quote_count == 0) { |
76 | 1.94k | first_quote = tok[i]; |
77 | 1.94k | } |
78 | 2.45k | if (tok[i] == first_quote) { |
79 | 2.14k | quote_count++; |
80 | 2.14k | } |
81 | 2.45k | } else { |
82 | 2.00k | break; |
83 | 2.00k | } |
84 | 4.45k | } |
85 | | |
86 | 3.26k | col_offset += quote_count; |
87 | 3.26k | } |
88 | | |
89 | 3.47k | _PyTokenizer_Info info = _PyTokenizer_GetInfo(p->tok); |
90 | 3.47k | if (PyErr_WarnExplicitObject(category, msg, info.filename, |
91 | 3.47k | lineno, info.module, NULL) < 0) { |
92 | 0 | if (PyErr_ExceptionMatches(category)) { |
93 | | /* Replace the Syntax/DeprecationWarning exception with a SyntaxError |
94 | | to get a more accurate error report */ |
95 | 0 | PyErr_Clear(); |
96 | | |
97 | | /* This is needed, in order for the SyntaxError to point to the token t, |
98 | | since _PyPegen_raise_error uses p->tokens[p->fill - 1] for the |
99 | | error location, if p->known_err_token is not set. */ |
100 | 0 | p->known_err_token = t; |
101 | 0 | if (octal) { |
102 | 0 | RAISE_ERROR_KNOWN_LOCATION(p, PyExc_SyntaxError, lineno, col_offset-1, lineno, col_offset+1, |
103 | 0 | "\"\\%.3s\" is an invalid octal escape sequence. " |
104 | 0 | "Did you mean \"\\\\%.3s\"? A raw string is also an option.", |
105 | 0 | first_invalid_escape, first_invalid_escape); |
106 | 0 | } |
107 | 0 | else { |
108 | 0 | RAISE_ERROR_KNOWN_LOCATION(p, PyExc_SyntaxError, lineno, col_offset-1, lineno, col_offset+1, |
109 | 0 | "\"\\%c\" is an invalid escape sequence. " |
110 | 0 | "Did you mean \"\\\\%c\"? A raw string is also an option.", |
111 | 0 | c, c); |
112 | 0 | } |
113 | 0 | } |
114 | 0 | Py_DECREF(msg); |
115 | 0 | return -1; |
116 | 0 | } |
117 | 3.47k | Py_DECREF(msg); |
118 | 3.47k | return 0; |
119 | 3.47k | } |
120 | | |
121 | | static PyObject * |
122 | | decode_utf8(const char **sPtr, const char *end) |
123 | 4.10k | { |
124 | 4.10k | const char *s; |
125 | 4.10k | const char *t; |
126 | 4.10k | t = s = *sPtr; |
127 | 50.6k | while (s < end && (*s & 0x80)) { |
128 | 46.5k | s++; |
129 | 46.5k | } |
130 | 4.10k | *sPtr = s; |
131 | 4.10k | return PyUnicode_DecodeUTF8(t, s - t, NULL); |
132 | 4.10k | } |
133 | | |
134 | | static PyObject * |
135 | | decode_unicode_with_escapes(Parser *parser, const char *s, size_t len, Token *t) |
136 | 31.0k | { |
137 | 31.0k | PyObject *v; |
138 | 31.0k | char *p; |
139 | 31.0k | const char *end; |
140 | | |
141 | | /* check for integer overflow */ |
142 | 31.0k | if (len > (size_t)PY_SSIZE_T_MAX / 6) { |
143 | 0 | return NULL; |
144 | 0 | } |
145 | | /* "ä" (2 bytes) may become "\U000000E4" (10 bytes), or 1:5. |
146 | | * "\ä" (3 bytes) may become "\u005c\U000000E4" (16 bytes), or ~1:6. */ |
147 | 31.0k | Py_ssize_t alloc = (Py_ssize_t)len * 6; |
148 | 31.0k | char *buf = PyMem_Malloc(alloc); |
149 | 31.0k | if (buf == NULL) { |
150 | 0 | return PyErr_NoMemory(); |
151 | 0 | } |
152 | 31.0k | p = buf; |
153 | 31.0k | end = s + len; |
154 | 216k | while (s < end) { |
155 | 186k | if (*s == '\\') { |
156 | 40.5k | *p++ = *s++; |
157 | 40.5k | if (s >= end || *s & 0x80) { |
158 | 1.12k | memcpy(p, "u005c", 5); |
159 | 1.12k | p += 5; |
160 | 1.12k | if (s >= end) { |
161 | 613 | break; |
162 | 613 | } |
163 | 1.12k | } |
164 | 40.5k | } |
165 | 185k | if (*s & 0x80) { |
166 | 4.10k | PyObject *w; |
167 | 4.10k | int kind; |
168 | 4.10k | const void *data; |
169 | 4.10k | Py_ssize_t w_len; |
170 | 4.10k | Py_ssize_t i; |
171 | 4.10k | w = decode_utf8(&s, end); |
172 | 4.10k | if (w == NULL) { |
173 | 0 | PyMem_Free(buf); |
174 | 0 | return NULL; |
175 | 0 | } |
176 | 4.10k | kind = PyUnicode_KIND(w); |
177 | 4.10k | data = PyUnicode_DATA(w); |
178 | 4.10k | w_len = PyUnicode_GET_LENGTH(w); |
179 | 20.1k | for (i = 0; i < w_len; i++) { |
180 | | // sprintf() writes a null byte: the buffer is large enough |
181 | | // for that thanks to the overallocation. |
182 | 16.0k | assert((p + 11 - buf) <= alloc); |
183 | 16.0k | Py_UCS4 chr = PyUnicode_READ(kind, data, i); |
184 | 16.0k | sprintf(p, "\\U%08x", chr); |
185 | 16.0k | p += 10; |
186 | 16.0k | } |
187 | 4.10k | Py_DECREF(w); |
188 | 4.10k | } |
189 | 181k | else { |
190 | 181k | *p++ = *s++; |
191 | 181k | } |
192 | 185k | } |
193 | 31.0k | len = (size_t)(p - buf); |
194 | | |
195 | 31.0k | int first_invalid_escape_char; |
196 | 31.0k | const char *first_invalid_escape_ptr; |
197 | | // Allow a trailing unfinished escape in a partial token. |
198 | 31.0k | int incomplete = t != NULL && t->is_incomplete; |
199 | 31.0k | Py_ssize_t consumed; |
200 | 31.0k | v = _PyUnicode_DecodeUnicodeEscapeInternal2(buf, (Py_ssize_t)len, NULL, |
201 | 31.0k | incomplete ? &consumed : NULL, |
202 | 31.0k | &first_invalid_escape_char, |
203 | 31.0k | &first_invalid_escape_ptr); |
204 | | |
205 | | // HACK: later we can simply pass the line no, since we don't preserve the tokens |
206 | | // when we are decoding the string but we preserve the line numbers. |
207 | 31.0k | if (v != NULL && first_invalid_escape_ptr != NULL && t != NULL && !incomplete) { |
208 | 6.36k | if (warn_invalid_escape_sequence(parser, buf, first_invalid_escape_ptr, t) < 0) { |
209 | | /* We have not deallocated the buffer before because |
210 | | * first_invalid_escape_ptr points inside buf. */ |
211 | 0 | PyMem_Free(buf); |
212 | 0 | Py_DECREF(v); |
213 | 0 | return NULL; |
214 | 0 | } |
215 | 6.36k | } |
216 | 31.0k | PyMem_Free(buf); |
217 | 31.0k | return v; |
218 | 31.0k | } |
219 | | |
220 | | static PyObject * |
221 | | decode_bytes_with_escapes(Parser *p, const char *s, Py_ssize_t len, Token *t) |
222 | 2.82k | { |
223 | 2.82k | int first_invalid_escape_char; |
224 | 2.82k | const char *first_invalid_escape_ptr; |
225 | 2.82k | PyObject *result = _PyBytes_DecodeEscape2(s, len, NULL, |
226 | 2.82k | &first_invalid_escape_char, |
227 | 2.82k | &first_invalid_escape_ptr); |
228 | 2.82k | if (result == NULL) { |
229 | 4 | return NULL; |
230 | 4 | } |
231 | | |
232 | 2.82k | if (first_invalid_escape_ptr != NULL) { |
233 | 1.14k | if (warn_invalid_escape_sequence(p, s, first_invalid_escape_ptr, t) < 0) { |
234 | 0 | Py_DECREF(result); |
235 | 0 | return NULL; |
236 | 0 | } |
237 | 1.14k | } |
238 | 2.82k | return result; |
239 | 2.82k | } |
240 | | |
241 | | PyObject * |
242 | | _PyPegen_decode_string(Parser *p, int raw, const char *s, size_t len, Token *t) |
243 | 87.1k | { |
244 | 87.1k | if (raw) { |
245 | 56.1k | return PyUnicode_DecodeUTF8Stateful(s, (Py_ssize_t)len, NULL, NULL); |
246 | 56.1k | } |
247 | 31.0k | return decode_unicode_with_escapes(p, s, len, t); |
248 | 87.1k | } |
249 | | |
250 | | /* s must include the bracketing quote characters, and r, b &/or f prefixes |
251 | | (if any), and embedded escape sequences (if any). (f-strings are handled by the parser) |
252 | | _PyPegen_parse_string parses it, and returns the decoded Python string object. */ |
253 | | PyObject * |
254 | | _PyPegen_parse_string(Parser *p, Token *t) |
255 | 54.9k | { |
256 | 54.9k | const char *s = PyBytes_AsString(t->bytes); |
257 | 54.9k | if (s == NULL) { |
258 | 0 | return NULL; |
259 | 0 | } |
260 | | |
261 | 54.9k | size_t len; |
262 | 54.9k | int quote = Py_CHARMASK(*s); |
263 | 54.9k | int bytesmode = 0; |
264 | 54.9k | int rawmode = 0; |
265 | | |
266 | 54.9k | if (Py_ISALPHA(quote)) { |
267 | 23.8k | while (!bytesmode || !rawmode) { |
268 | 23.3k | if (quote == 'b' || quote == 'B') { |
269 | 6.67k | quote =(unsigned char)*++s; |
270 | 6.67k | bytesmode = 1; |
271 | 6.67k | } |
272 | 16.6k | else if (quote == 'u' || quote == 'U') { |
273 | 4.55k | quote = (unsigned char)*++s; |
274 | 4.55k | } |
275 | 12.0k | else if (quote == 'r' || quote == 'R') { |
276 | 1.00k | quote = (unsigned char)*++s; |
277 | 1.00k | rawmode = 1; |
278 | 1.00k | } |
279 | 11.0k | else { |
280 | 11.0k | break; |
281 | 11.0k | } |
282 | 23.3k | } |
283 | 11.6k | } |
284 | | |
285 | 54.9k | if (quote != '\'' && quote != '\"') { |
286 | 0 | PyErr_BadInternalCall(); |
287 | 0 | return NULL; |
288 | 0 | } |
289 | | |
290 | | /* Skip the leading quote char. */ |
291 | 54.9k | s++; |
292 | 54.9k | len = strlen(s); |
293 | | // gh-120155: 's' contains at least the trailing quote, |
294 | | // so the code '--len' below is safe. |
295 | 54.9k | assert(len >= 1); |
296 | | |
297 | 54.9k | if (len > INT_MAX) { |
298 | 0 | PyErr_SetString(PyExc_OverflowError, "string to parse is too long"); |
299 | 0 | return NULL; |
300 | 0 | } |
301 | 54.9k | if (s[--len] != quote) { |
302 | | /* Last quote char must match the first. */ |
303 | 0 | PyErr_BadInternalCall(); |
304 | 0 | return NULL; |
305 | 0 | } |
306 | 54.9k | if (len >= 4 && s[0] == quote && s[1] == quote) { |
307 | | /* A triple quoted string. We've already skipped one quote at |
308 | | the start and one at the end of the string. Now skip the |
309 | | two at the start. */ |
310 | 879 | s += 2; |
311 | 879 | len -= 2; |
312 | | /* And check that the last two match. */ |
313 | 879 | if (s[--len] != quote || s[--len] != quote) { |
314 | 0 | PyErr_BadInternalCall(); |
315 | 0 | return NULL; |
316 | 0 | } |
317 | 879 | } |
318 | | |
319 | | /* Avoid invoking escape decoding routines if possible. */ |
320 | 54.9k | rawmode = rawmode || strchr(s, '\\') == NULL; |
321 | 54.9k | if (bytesmode) { |
322 | | /* Disallow non-ASCII characters. */ |
323 | 6.67k | const char *ch; |
324 | 117k | for (ch = s; *ch; ch++) { |
325 | 110k | if (Py_CHARMASK(*ch) >= 0x80) { |
326 | 4 | RAISE_SYNTAX_ERROR_KNOWN_LOCATION( |
327 | 4 | t, |
328 | 4 | "bytes can only contain ASCII " |
329 | 4 | "literal characters"); |
330 | 4 | return NULL; |
331 | 4 | } |
332 | 110k | } |
333 | 6.66k | if (rawmode) { |
334 | 3.84k | return PyBytes_FromStringAndSize(s, (Py_ssize_t)len); |
335 | 3.84k | } |
336 | 2.82k | return decode_bytes_with_escapes(p, s, (Py_ssize_t)len, t); |
337 | 6.66k | } |
338 | 48.2k | return _PyPegen_decode_string(p, rawmode, s, len, t); |
339 | 54.9k | } |