Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(38)

Delta Between Two Patch Sets: Objects/exceptions.c

Issue 21669: Custom error messages when print & exec are used as statements
Left Patch Set: Created 5 years, 8 months ago
Right Patch Set: Created 5 years, 8 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Right: Side by side diff | Download
« no previous file with change/comment | « Lib/test/test_grammar.py ('k') | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
(no file at all)
1 /* 1 /*
2 * New exceptions.c written in Iceland by Richard Jones and Georg Brandl. 2 * New exceptions.c written in Iceland by Richard Jones and Georg Brandl.
3 * 3 *
4 * Thanks go to Tim Peters and Michael Hudson for debugging. 4 * Thanks go to Tim Peters and Michael Hudson for debugging.
5 */ 5 */
6 6
7 #define PY_SSIZE_T_CLEAN 7 #define PY_SSIZE_T_CLEAN
8 #include <Python.h> 8 #include <Python.h>
9 #include "structmember.h" 9 #include "structmember.h"
10 #include "osdefs.h" 10 #include "osdefs.h"
(...skipping 1236 matching lines...) Expand 10 before | Expand all | Expand 10 after
1247 * AttributeError extends Exception 1247 * AttributeError extends Exception
1248 */ 1248 */
1249 SimpleExtendsException(PyExc_Exception, AttributeError, 1249 SimpleExtendsException(PyExc_Exception, AttributeError,
1250 "Attribute not found."); 1250 "Attribute not found.");
1251 1251
1252 1252
1253 /* 1253 /*
1254 * SyntaxError extends Exception 1254 * SyntaxError extends Exception
1255 */ 1255 */
1256 1256
1257 /* Helper function to customise error message for some syntax errors */
1258 static int _report_missing_parentheses(PySyntaxErrorObject *self);
1259
1257 static int 1260 static int
1258 SyntaxError_init(PySyntaxErrorObject *self, PyObject *args, PyObject *kwds) 1261 SyntaxError_init(PySyntaxErrorObject *self, PyObject *args, PyObject *kwds)
1259 { 1262 {
1260 PyObject *info = NULL; 1263 PyObject *info = NULL;
1261 Py_ssize_t lenargs = PyTuple_GET_SIZE(args); 1264 Py_ssize_t lenargs = PyTuple_GET_SIZE(args);
1262 1265
1263 if (BaseException_init((PyBaseExceptionObject *)self, args, kwds) == -1) 1266 if (BaseException_init((PyBaseExceptionObject *)self, args, kwds) == -1)
1264 return -1; 1267 return -1;
1265 1268
1266 if (lenargs >= 1) { 1269 if (lenargs >= 1) {
(...skipping 24 matching lines...) Expand all
1291 1294
1292 Py_CLEAR(self->offset); 1295 Py_CLEAR(self->offset);
1293 self->offset = PyTuple_GET_ITEM(info, 2); 1296 self->offset = PyTuple_GET_ITEM(info, 2);
1294 Py_INCREF(self->offset); 1297 Py_INCREF(self->offset);
1295 1298
1296 Py_CLEAR(self->text); 1299 Py_CLEAR(self->text);
1297 self->text = PyTuple_GET_ITEM(info, 3); 1300 self->text = PyTuple_GET_ITEM(info, 3);
1298 Py_INCREF(self->text); 1301 Py_INCREF(self->text);
1299 1302
1300 Py_DECREF(info); 1303 Py_DECREF(info);
1304
1305 /* Issue #21669: Custom error for 'print' & 'exec' as statements */
1306 if (self->text && PyUnicode_Check(self->text)) {
1307 if (_report_missing_parentheses(self) < 0) {
1308 return -1;
1309 }
1310 }
1301 } 1311 }
1302 return 0; 1312 return 0;
1303 } 1313 }
1304 1314
1305 static int 1315 static int
1306 SyntaxError_clear(PySyntaxErrorObject *self) 1316 SyntaxError_clear(PySyntaxErrorObject *self)
1307 { 1317 {
1308 Py_CLEAR(self->msg); 1318 Py_CLEAR(self->msg);
1309 Py_CLEAR(self->filename); 1319 Py_CLEAR(self->filename);
1310 Py_CLEAR(self->lineno); 1320 Py_CLEAR(self->lineno);
(...skipping 1465 matching lines...) Expand 10 before | Expand all | Expand 10 after
2776 PyErr_Format(exc, "%U (%s: %S)", 2786 PyErr_Format(exc, "%U (%s: %S)",
2777 msg_prefix, Py_TYPE(val)->tp_name, val); 2787 msg_prefix, Py_TYPE(val)->tp_name, val);
2778 Py_DECREF(exc); 2788 Py_DECREF(exc);
2779 Py_DECREF(msg_prefix); 2789 Py_DECREF(msg_prefix);
2780 PyErr_Fetch(&new_exc, &new_val, &new_tb); 2790 PyErr_Fetch(&new_exc, &new_val, &new_tb);
2781 PyErr_NormalizeException(&new_exc, &new_val, &new_tb); 2791 PyErr_NormalizeException(&new_exc, &new_val, &new_tb);
2782 PyException_SetCause(new_val, val); 2792 PyException_SetCause(new_val, val);
2783 PyErr_Restore(new_exc, new_val, new_tb); 2793 PyErr_Restore(new_exc, new_val, new_tb);
2784 return new_val; 2794 return new_val;
2785 } 2795 }
2796
2797
2798 /* To help with migration from Python 2, SyntaxError.__init__ applies some
2799 * heuristics to try to report a more meaningful exception when print and
2800 * exec are used like statements.
2801 *
2802 * The heuristics are currently expected to detect the following cases:
2803 * - top level statement
2804 * - statement in a nested suite
2805 * - trailing section of a one line complex statement
2806 *
2807 * They're currently known not to trigger:
2808 * - after a semi-colon
2809 *
2810 * The error message can be a bit odd in cases where the "arguments" are
2811 * completely illegal syntactically, but that isn't worth the hassle of
2812 * fixing.
2813 *
2814 * We also can't do anything about cases that are legal Python 3 syntax
2815 * but mean something entirely different from what they did in Python 2
2816 * (omitting the arguments entirely, printing items preceded by a unary plus
2817 * or minus, using the stream redirection syntax).
2818 */
2819
2820 static int
2821 _check_for_legacy_statements(PySyntaxErrorObject *self, Py_ssize_t start)
2822 {
2823 /* Return values:
2824 * -1: an error occurred
2825 * 0: nothing happened
2826 * 1: the check triggered & the error message was changed
2827 */
2828 static PyObject *print_prefix = NULL;
2829 static PyObject *exec_prefix = NULL;
2830 Py_ssize_t text_len = PyUnicode_GET_LENGTH(self->text);
2831 int kind = PyUnicode_KIND(self->text);
2832 void *data = PyUnicode_DATA(self->text);
2833
2834 /* Ignore leading whitespace */
2835 while (start < text_len) {
2836 Py_UCS4 ch = PyUnicode_READ(kind, data, start);
2837 if (!Py_UNICODE_ISSPACE(ch))
2838 break;
2839 start++;
2840 }
2841 /* Checking against an empty or whitespace-only part of the string */
2842 if (start == text_len) {
2843 return 0;
2844 }
2845
2846 /* Check for legacy print statements */
2847 if (print_prefix == NULL) {
2848 print_prefix = PyUnicode_InternFromString("print ");
2849 if (print_prefix == NULL) {
2850 return -1;
2851 }
2852 }
2853 if (PyUnicode_Tailmatch(self->text, print_prefix,
2854 start, text_len, -1)) {
2855 Py_CLEAR(self->msg);
2856 self->msg = PyUnicode_FromString(
2857 "Missing parentheses in call to 'print'");
2858 return 1;
2859 }
2860
2861 /* Check for legacy exec statements */
2862 if (exec_prefix == NULL) {
2863 exec_prefix = PyUnicode_InternFromString("exec ");
2864 if (exec_prefix == NULL) {
2865 return -1;
2866 }
2867 }
2868 if (PyUnicode_Tailmatch(self->text, exec_prefix,
2869 start, text_len, -1)) {
2870 Py_CLEAR(self->msg);
2871 self->msg = PyUnicode_FromString(
2872 "Missing parentheses in call to 'exec'");
2873 return 1;
2874 }
2875 /* Fall back to the default error message */
2876 return 0;
2877 }
2878
2879 static int
2880 _report_missing_parentheses(PySyntaxErrorObject *self)
2881 {
2882 Py_UCS4 left_paren = 40;
2883 Py_ssize_t left_paren_index;
2884 Py_ssize_t text_len = PyUnicode_GET_LENGTH(self->text);
2885 int legacy_check_result = 0;
2886
2887 /* Skip entirely if there is an opening parenthesis */
2888 left_paren_index = PyUnicode_FindChar(self->text, left_paren,
2889 0, text_len, 1);
2890 if (left_paren_index < -1) {
2891 return -1;
2892 }
2893 if (left_paren_index != -1) {
2894 /* Use default error message for any line with an opening paren */
2895 return 0;
2896 }
2897 /* Handle the simple statement case */
2898 legacy_check_result = _check_for_legacy_statements(self, 0);
2899 if (legacy_check_result < 0) {
2900 return -1;
2901
2902 }
2903 if (legacy_check_result == 0) {
2904 /* Handle the one-line complex statement case */
2905 Py_UCS4 colon = 58;
2906 Py_ssize_t colon_index;
2907 colon_index = PyUnicode_FindChar(self->text, colon,
2908 0, text_len, 1);
2909 if (colon_index < -1) {
2910 return -1;
2911 }
2912 if (colon_index >= 0 && colon_index < text_len) {
2913 /* Check again, starting from just after the colon */
2914 if (_check_for_legacy_statements(self, colon_index+1) < 0) {
2915 return -1;
2916 }
2917 }
2918 }
2919 return 0;
2920 }
LEFTRIGHT

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+