/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.19k | { |
15 | 7.19k | 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.01k | return 0; |
19 | 3.01k | } |
20 | 4.17k | unsigned char c = (unsigned char)*first_invalid_escape; |
21 | 4.17k | if ((t->type == FSTRING_MIDDLE || t->type == FSTRING_END || t->type == TSTRING_MIDDLE || t->type == TSTRING_END) |
22 | 2.91k | && (c == '{' || c == '}')) { |
23 | | // in this case the tokenizer has already emitted a warning, |
24 | | // see Parser/tokenizer/helpers.c:warn_invalid_escape_sequence |
25 | 841 | return 0; |
26 | 841 | } |
27 | | |
28 | 3.33k | int octal = ('4' <= c && c <= '7'); |
29 | 3.33k | PyObject *msg = |
30 | 3.33k | octal |
31 | 3.33k | ? PyUnicode_FromFormat( |
32 | 579 | "\"\\%.3s\" is an invalid octal escape sequence. " |
33 | 579 | "Such sequences will not work in the future. " |
34 | 579 | "Did you mean \"\\\\%.3s\"? A raw string is also an option.", |
35 | 579 | first_invalid_escape, first_invalid_escape) |
36 | 3.33k | : PyUnicode_FromFormat( |
37 | 2.75k | "\"\\%c\" is an invalid escape sequence. " |
38 | 2.75k | "Such sequences will not work in the future. " |
39 | 2.75k | "Did you mean \"\\\\%c\"? A raw string is also an option.", |
40 | 2.75k | c, c); |
41 | 3.33k | if (msg == NULL) { |
42 | 0 | return -1; |
43 | 0 | } |
44 | 3.33k | PyObject *category; |
45 | 3.33k | if (p->feature_version >= 12) { |
46 | 3.33k | category = PyExc_SyntaxWarning; |
47 | 3.33k | } |
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.33k | const char *start = buffer; |
54 | 3.33k | const char *end = first_invalid_escape; |
55 | 3.33k | int lineno = t->lineno; |
56 | 3.33k | int col_offset = t->col_offset; |
57 | 30.1k | while (start < end) { |
58 | 26.8k | if (*start == '\n') { |
59 | 976 | lineno++; |
60 | 976 | col_offset = 0; |
61 | 976 | } |
62 | 25.8k | else { |
63 | 25.8k | col_offset++; |
64 | 25.8k | } |
65 | 26.8k | start++; |
66 | 26.8k | } |
67 | | |
68 | | // Count the number of quotes in the token |
69 | 3.33k | char first_quote = 0; |
70 | 3.33k | if (lineno == t->lineno) { |
71 | 3.12k | int quote_count = 0; |
72 | 3.12k | const char* tok = PyBytes_AsString(t->bytes); |
73 | 5.55k | for (int i = 0; i < PyBytes_Size(t->bytes); i++) { |
74 | 4.41k | if (tok[i] == '\'' || tok[i] == '\"') { |
75 | 2.43k | if (quote_count == 0) { |
76 | 1.87k | first_quote = tok[i]; |
77 | 1.87k | } |
78 | 2.43k | if (tok[i] == first_quote) { |
79 | 2.12k | quote_count++; |
80 | 2.12k | } |
81 | 2.43k | } else { |
82 | 1.97k | break; |
83 | 1.97k | } |
84 | 4.41k | } |
85 | | |
86 | 3.12k | col_offset += quote_count; |
87 | 3.12k | } |
88 | | |
89 | 3.33k | _PyTokenizer_Info info = _PyTokenizer_GetInfo(p->tok); |
90 | 3.33k | if (PyErr_WarnExplicitObject(category, msg, info.filename, |
91 | 3.33k | 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.33k | Py_DECREF(msg); |
118 | 3.33k | return 0; |
119 | 3.33k | } |
120 | | |
121 | | static PyObject * |
122 | | decode_utf8(const char **sPtr, const char *end) |
123 | 4.20k | { |
124 | 4.20k | const char *s; |
125 | 4.20k | const char *t; |
126 | 4.20k | t = s = *sPtr; |
127 | 47.4k | while (s < end && (*s & 0x80)) { |
128 | 43.2k | s++; |
129 | 43.2k | } |
130 | 4.20k | *sPtr = s; |
131 | 4.20k | return PyUnicode_DecodeUTF8(t, s - t, NULL); |
132 | 4.20k | } |
133 | | |
134 | | static PyObject * |
135 | | decode_unicode_with_escapes(Parser *parser, const char *s, size_t len, Token *t) |
136 | 30.7k | { |
137 | 30.7k | PyObject *v; |
138 | 30.7k | char *p; |
139 | 30.7k | const char *end; |
140 | | |
141 | | /* check for integer overflow */ |
142 | 30.7k | 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 | 30.7k | Py_ssize_t alloc = (Py_ssize_t)len * 6; |
148 | 30.7k | char *buf = PyMem_Malloc(alloc); |
149 | 30.7k | if (buf == NULL) { |
150 | 0 | return PyErr_NoMemory(); |
151 | 0 | } |
152 | 30.7k | p = buf; |
153 | 30.7k | end = s + len; |
154 | 213k | while (s < end) { |
155 | 183k | if (*s == '\\') { |
156 | 40.1k | *p++ = *s++; |
157 | 40.1k | if (s >= end || *s & 0x80) { |
158 | 1.08k | memcpy(p, "u005c", 5); |
159 | 1.08k | p += 5; |
160 | 1.08k | if (s >= end) { |
161 | 555 | break; |
162 | 555 | } |
163 | 1.08k | } |
164 | 40.1k | } |
165 | 183k | if (*s & 0x80) { |
166 | 4.20k | PyObject *w; |
167 | 4.20k | int kind; |
168 | 4.20k | const void *data; |
169 | 4.20k | Py_ssize_t w_len; |
170 | 4.20k | Py_ssize_t i; |
171 | 4.20k | w = decode_utf8(&s, end); |
172 | 4.20k | if (w == NULL) { |
173 | 0 | PyMem_Free(buf); |
174 | 0 | return NULL; |
175 | 0 | } |
176 | 4.20k | kind = PyUnicode_KIND(w); |
177 | 4.20k | data = PyUnicode_DATA(w); |
178 | 4.20k | w_len = PyUnicode_GET_LENGTH(w); |
179 | 19.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 | 14.9k | assert((p + 11 - buf) <= alloc); |
183 | 14.9k | Py_UCS4 chr = PyUnicode_READ(kind, data, i); |
184 | 14.9k | sprintf(p, "\\U%08x", chr); |
185 | 14.9k | p += 10; |
186 | 14.9k | } |
187 | 4.20k | Py_DECREF(w); |
188 | 4.20k | } |
189 | 178k | else { |
190 | 178k | *p++ = *s++; |
191 | 178k | } |
192 | 183k | } |
193 | 30.7k | len = (size_t)(p - buf); |
194 | | |
195 | 30.7k | int first_invalid_escape_char; |
196 | 30.7k | const char *first_invalid_escape_ptr; |
197 | 30.7k | v = _PyUnicode_DecodeUnicodeEscapeInternal2(buf, (Py_ssize_t)len, NULL, NULL, |
198 | 30.7k | &first_invalid_escape_char, |
199 | 30.7k | &first_invalid_escape_ptr); |
200 | | |
201 | | // HACK: later we can simply pass the line no, since we don't preserve the tokens |
202 | | // when we are decoding the string but we preserve the line numbers. |
203 | 30.7k | if (v != NULL && first_invalid_escape_ptr != NULL && t != NULL) { |
204 | 6.24k | if (warn_invalid_escape_sequence(parser, buf, first_invalid_escape_ptr, t) < 0) { |
205 | | /* We have not deallocated the buffer before because |
206 | | * first_invalid_escape_ptr points inside buf. */ |
207 | 0 | PyMem_Free(buf); |
208 | 0 | Py_DECREF(v); |
209 | 0 | return NULL; |
210 | 0 | } |
211 | 6.24k | } |
212 | 30.7k | PyMem_Free(buf); |
213 | 30.7k | return v; |
214 | 30.7k | } |
215 | | |
216 | | static PyObject * |
217 | | decode_bytes_with_escapes(Parser *p, const char *s, Py_ssize_t len, Token *t) |
218 | 2.48k | { |
219 | 2.48k | int first_invalid_escape_char; |
220 | 2.48k | const char *first_invalid_escape_ptr; |
221 | 2.48k | PyObject *result = _PyBytes_DecodeEscape2(s, len, NULL, |
222 | 2.48k | &first_invalid_escape_char, |
223 | 2.48k | &first_invalid_escape_ptr); |
224 | 2.48k | if (result == NULL) { |
225 | 5 | return NULL; |
226 | 5 | } |
227 | | |
228 | 2.47k | if (first_invalid_escape_ptr != NULL) { |
229 | 950 | if (warn_invalid_escape_sequence(p, s, first_invalid_escape_ptr, t) < 0) { |
230 | 0 | Py_DECREF(result); |
231 | 0 | return NULL; |
232 | 0 | } |
233 | 950 | } |
234 | 2.47k | return result; |
235 | 2.47k | } |
236 | | |
237 | | PyObject * |
238 | | _PyPegen_decode_string(Parser *p, int raw, const char *s, size_t len, Token *t) |
239 | 82.1k | { |
240 | 82.1k | if (raw) { |
241 | 51.4k | return PyUnicode_DecodeUTF8Stateful(s, (Py_ssize_t)len, NULL, NULL); |
242 | 51.4k | } |
243 | 30.7k | return decode_unicode_with_escapes(p, s, len, t); |
244 | 82.1k | } |
245 | | |
246 | | /* s must include the bracketing quote characters, and r, b &/or f prefixes |
247 | | (if any), and embedded escape sequences (if any). (f-strings are handled by the parser) |
248 | | _PyPegen_parse_string parses it, and returns the decoded Python string object. */ |
249 | | PyObject * |
250 | | _PyPegen_parse_string(Parser *p, Token *t) |
251 | 52.8k | { |
252 | 52.8k | const char *s = PyBytes_AsString(t->bytes); |
253 | 52.8k | if (s == NULL) { |
254 | 0 | return NULL; |
255 | 0 | } |
256 | | |
257 | 52.8k | size_t len; |
258 | 52.8k | int quote = Py_CHARMASK(*s); |
259 | 52.8k | int bytesmode = 0; |
260 | 52.8k | int rawmode = 0; |
261 | | |
262 | 52.8k | if (Py_ISALPHA(quote)) { |
263 | 20.8k | while (!bytesmode || !rawmode) { |
264 | 20.2k | if (quote == 'b' || quote == 'B') { |
265 | 6.50k | quote =(unsigned char)*++s; |
266 | 6.50k | bytesmode = 1; |
267 | 6.50k | } |
268 | 13.7k | else if (quote == 'u' || quote == 'U') { |
269 | 3.26k | quote = (unsigned char)*++s; |
270 | 3.26k | } |
271 | 10.5k | else if (quote == 'r' || quote == 'R') { |
272 | 909 | quote = (unsigned char)*++s; |
273 | 909 | rawmode = 1; |
274 | 909 | } |
275 | 9.61k | else { |
276 | 9.61k | break; |
277 | 9.61k | } |
278 | 20.2k | } |
279 | 10.1k | } |
280 | | |
281 | 52.8k | if (quote != '\'' && quote != '\"') { |
282 | 0 | PyErr_BadInternalCall(); |
283 | 0 | return NULL; |
284 | 0 | } |
285 | | |
286 | | /* Skip the leading quote char. */ |
287 | 52.8k | s++; |
288 | 52.8k | len = strlen(s); |
289 | | // gh-120155: 's' contains at least the trailing quote, |
290 | | // so the code '--len' below is safe. |
291 | 52.8k | assert(len >= 1); |
292 | | |
293 | 52.8k | if (len > INT_MAX) { |
294 | 0 | PyErr_SetString(PyExc_OverflowError, "string to parse is too long"); |
295 | 0 | return NULL; |
296 | 0 | } |
297 | 52.8k | if (s[--len] != quote) { |
298 | | /* Last quote char must match the first. */ |
299 | 0 | PyErr_BadInternalCall(); |
300 | 0 | return NULL; |
301 | 0 | } |
302 | 52.8k | if (len >= 4 && s[0] == quote && s[1] == quote) { |
303 | | /* A triple quoted string. We've already skipped one quote at |
304 | | the start and one at the end of the string. Now skip the |
305 | | two at the start. */ |
306 | 866 | s += 2; |
307 | 866 | len -= 2; |
308 | | /* And check that the last two match. */ |
309 | 866 | if (s[--len] != quote || s[--len] != quote) { |
310 | 0 | PyErr_BadInternalCall(); |
311 | 0 | return NULL; |
312 | 0 | } |
313 | 866 | } |
314 | | |
315 | | /* Avoid invoking escape decoding routines if possible. */ |
316 | 52.8k | rawmode = rawmode || strchr(s, '\\') == NULL; |
317 | 52.8k | if (bytesmode) { |
318 | | /* Disallow non-ASCII characters. */ |
319 | 6.50k | const char *ch; |
320 | 123k | for (ch = s; *ch; ch++) { |
321 | 116k | if (Py_CHARMASK(*ch) >= 0x80) { |
322 | 8 | RAISE_SYNTAX_ERROR_KNOWN_LOCATION( |
323 | 8 | t, |
324 | 8 | "bytes can only contain ASCII " |
325 | 8 | "literal characters"); |
326 | 8 | return NULL; |
327 | 8 | } |
328 | 116k | } |
329 | 6.49k | if (rawmode) { |
330 | 4.01k | return PyBytes_FromStringAndSize(s, (Py_ssize_t)len); |
331 | 4.01k | } |
332 | 2.48k | return decode_bytes_with_escapes(p, s, (Py_ssize_t)len, t); |
333 | 6.49k | } |
334 | 46.3k | return _PyPegen_decode_string(p, rawmode, s, len, t); |
335 | 52.8k | } |