Improve SyntaxErrors for bad future statements. Set file and location
for errors raised in future.c. Move some helper functions from compile.c to errors.c and make them API functions: PyErr_SyntaxLocation() and PyErr_ProgramText().
This commit is contained in:
parent
5687ffe0c5
commit
ad3d3f2f3f
|
@ -99,6 +99,10 @@ extern DL_IMPORT(int) PyErr_Warn(PyObject *, char *);
|
||||||
extern DL_IMPORT(int) PyErr_CheckSignals(void);
|
extern DL_IMPORT(int) PyErr_CheckSignals(void);
|
||||||
extern DL_IMPORT(void) PyErr_SetInterrupt(void);
|
extern DL_IMPORT(void) PyErr_SetInterrupt(void);
|
||||||
|
|
||||||
|
/* Support for adding program text to SyntaxErrors */
|
||||||
|
extern DL_IMPORT(void) PyErr_SyntaxLocation(char *, int);
|
||||||
|
extern DL_IMPORT(PyObject *) PyErr_ProgramText(char *, int);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -381,49 +381,6 @@ int is_free(int v)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* com_fetch_program_text will attempt to load the line of text that
|
|
||||||
the exception refers to. If it fails, it will return NULL but will
|
|
||||||
not set an exception.
|
|
||||||
|
|
||||||
XXX The functionality of this function is quite similar to the
|
|
||||||
functionality in tb_displayline() in traceback.c.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
fetch_program_text(char *filename, int lineno)
|
|
||||||
{
|
|
||||||
FILE *fp;
|
|
||||||
int i;
|
|
||||||
char linebuf[1000];
|
|
||||||
|
|
||||||
if (filename == NULL || lineno <= 0)
|
|
||||||
return NULL;
|
|
||||||
fp = fopen(filename, "r");
|
|
||||||
if (fp == NULL)
|
|
||||||
return NULL;
|
|
||||||
for (i = 0; i < lineno; i++) {
|
|
||||||
char *pLastChar = &linebuf[sizeof(linebuf) - 2];
|
|
||||||
do {
|
|
||||||
*pLastChar = '\0';
|
|
||||||
if (fgets(linebuf, sizeof linebuf, fp) == NULL)
|
|
||||||
break;
|
|
||||||
/* fgets read *something*; if it didn't get as
|
|
||||||
far as pLastChar, it must have found a newline
|
|
||||||
or hit the end of the file; if pLastChar is \n,
|
|
||||||
it obviously found a newline; else we haven't
|
|
||||||
yet seen a newline, so must continue */
|
|
||||||
} while (*pLastChar != '\0' && *pLastChar != '\n');
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
if (i == lineno) {
|
|
||||||
char *p = linebuf;
|
|
||||||
while (*p == ' ' || *p == '\t' || *p == '\014')
|
|
||||||
p++;
|
|
||||||
return PyString_FromString(p);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
com_error(struct compiling *c, PyObject *exc, char *msg)
|
com_error(struct compiling *c, PyObject *exc, char *msg)
|
||||||
{
|
{
|
||||||
|
@ -445,7 +402,7 @@ com_error(struct compiling *c, PyObject *exc, char *msg)
|
||||||
if (v == NULL)
|
if (v == NULL)
|
||||||
return; /* MemoryError, too bad */
|
return; /* MemoryError, too bad */
|
||||||
|
|
||||||
line = fetch_program_text(c->c_filename, c->c_lineno);
|
line = PyErr_ProgramText(c->c_filename, c->c_lineno);
|
||||||
if (line == NULL) {
|
if (line == NULL) {
|
||||||
Py_INCREF(Py_None);
|
Py_INCREF(Py_None);
|
||||||
line = Py_None;
|
line = Py_None;
|
||||||
|
@ -4028,41 +3985,6 @@ get_ref_type(struct compiling *c, char *name)
|
||||||
|
|
||||||
/* Helper function for setting lineno and filename */
|
/* Helper function for setting lineno and filename */
|
||||||
|
|
||||||
static void
|
|
||||||
set_error_location(char *filename, int lineno)
|
|
||||||
{
|
|
||||||
PyObject *exc, *v, *tb, *tmp;
|
|
||||||
|
|
||||||
/* add attributes for the line number and filename for the error */
|
|
||||||
PyErr_Fetch(&exc, &v, &tb);
|
|
||||||
PyErr_NormalizeException(&exc, &v, &tb);
|
|
||||||
tmp = PyInt_FromLong(lineno);
|
|
||||||
if (tmp == NULL)
|
|
||||||
PyErr_Clear();
|
|
||||||
else {
|
|
||||||
if (PyObject_SetAttrString(v, "lineno", tmp))
|
|
||||||
PyErr_Clear();
|
|
||||||
Py_DECREF(tmp);
|
|
||||||
}
|
|
||||||
if (filename != NULL) {
|
|
||||||
tmp = PyString_FromString(filename);
|
|
||||||
if (tmp == NULL)
|
|
||||||
PyErr_Clear();
|
|
||||||
else {
|
|
||||||
if (PyObject_SetAttrString(v, "filename", tmp))
|
|
||||||
PyErr_Clear();
|
|
||||||
Py_DECREF(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp = fetch_program_text(filename, lineno);
|
|
||||||
if (tmp) {
|
|
||||||
PyObject_SetAttrString(v, "text", tmp);
|
|
||||||
Py_DECREF(tmp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PyErr_Restore(exc, v, tb);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
symtable_build(struct compiling *c, node *n)
|
symtable_build(struct compiling *c, node *n)
|
||||||
{
|
{
|
||||||
|
@ -4198,7 +4120,7 @@ symtable_update_flags(struct compiling *c, PySymtableEntryObject *ste,
|
||||||
PyErr_Format(PyExc_SyntaxError,
|
PyErr_Format(PyExc_SyntaxError,
|
||||||
ILLEGAL_DYNAMIC_SCOPE,
|
ILLEGAL_DYNAMIC_SCOPE,
|
||||||
PyString_AS_STRING(ste->ste_name));
|
PyString_AS_STRING(ste->ste_name));
|
||||||
set_error_location(c->c_symtable->st_filename,
|
PyErr_SyntaxLocation(c->c_symtable->st_filename,
|
||||||
ste->ste_lineno);
|
ste->ste_lineno);
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
|
@ -4273,7 +4195,7 @@ symtable_load_symbols(struct compiling *c)
|
||||||
if (flags & DEF_PARAM) {
|
if (flags & DEF_PARAM) {
|
||||||
PyErr_Format(PyExc_SyntaxError, LOCAL_GLOBAL,
|
PyErr_Format(PyExc_SyntaxError, LOCAL_GLOBAL,
|
||||||
PyString_AS_STRING(name));
|
PyString_AS_STRING(name));
|
||||||
set_error_location(st->st_filename,
|
PyErr_SyntaxLocation(st->st_filename,
|
||||||
ste->ste_lineno);
|
ste->ste_lineno);
|
||||||
st->st_errors++;
|
st->st_errors++;
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -4581,7 +4503,7 @@ symtable_add_def_o(struct symtable *st, PyObject *dict,
|
||||||
if ((flag & DEF_PARAM) && (val & DEF_PARAM)) {
|
if ((flag & DEF_PARAM) && (val & DEF_PARAM)) {
|
||||||
PyErr_Format(PyExc_SyntaxError, DUPLICATE_ARGUMENT,
|
PyErr_Format(PyExc_SyntaxError, DUPLICATE_ARGUMENT,
|
||||||
PyString_AsString(name));
|
PyString_AsString(name));
|
||||||
set_error_location(st->st_filename,
|
PyErr_SyntaxLocation(st->st_filename,
|
||||||
st->st_cur->ste_lineno);
|
st->st_cur->ste_lineno);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -4904,7 +4826,7 @@ symtable_global(struct symtable *st, node *n)
|
||||||
PyErr_Format(PyExc_SyntaxError,
|
PyErr_Format(PyExc_SyntaxError,
|
||||||
"name '%.400s' is local and global",
|
"name '%.400s' is local and global",
|
||||||
name);
|
name);
|
||||||
set_error_location(st->st_filename,
|
PyErr_SyntaxLocation(st->st_filename,
|
||||||
st->st_cur->ste_lineno);
|
st->st_cur->ste_lineno);
|
||||||
st->st_errors++;
|
st->st_errors++;
|
||||||
return;
|
return;
|
||||||
|
@ -4958,7 +4880,7 @@ symtable_import(struct symtable *st, node *n)
|
||||||
if (n->n_lineno >= st->st_future->ff_last_lineno) {
|
if (n->n_lineno >= st->st_future->ff_last_lineno) {
|
||||||
PyErr_SetString(PyExc_SyntaxError,
|
PyErr_SetString(PyExc_SyntaxError,
|
||||||
LATE_FUTURE);
|
LATE_FUTURE);
|
||||||
set_error_location(st->st_filename,
|
PyErr_SyntaxLocation(st->st_filename,
|
||||||
n->n_lineno);
|
n->n_lineno);
|
||||||
st->st_errors++;
|
st->st_errors++;
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -622,3 +622,82 @@ PyErr_Warn(PyObject *category, char *message)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PyErr_SyntaxLocation(char *filename, int lineno)
|
||||||
|
{
|
||||||
|
PyObject *exc, *v, *tb, *tmp;
|
||||||
|
|
||||||
|
/* add attributes for the line number and filename for the error */
|
||||||
|
PyErr_Fetch(&exc, &v, &tb);
|
||||||
|
PyErr_NormalizeException(&exc, &v, &tb);
|
||||||
|
/* XXX check that it is, indeed, a syntax error */
|
||||||
|
tmp = PyInt_FromLong(lineno);
|
||||||
|
if (tmp == NULL)
|
||||||
|
PyErr_Clear();
|
||||||
|
else {
|
||||||
|
if (PyObject_SetAttrString(v, "lineno", tmp))
|
||||||
|
PyErr_Clear();
|
||||||
|
Py_DECREF(tmp);
|
||||||
|
}
|
||||||
|
if (filename != NULL) {
|
||||||
|
tmp = PyString_FromString(filename);
|
||||||
|
if (tmp == NULL)
|
||||||
|
PyErr_Clear();
|
||||||
|
else {
|
||||||
|
if (PyObject_SetAttrString(v, "filename", tmp))
|
||||||
|
PyErr_Clear();
|
||||||
|
Py_DECREF(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = PyErr_ProgramText(filename, lineno);
|
||||||
|
if (tmp) {
|
||||||
|
PyObject_SetAttrString(v, "text", tmp);
|
||||||
|
Py_DECREF(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PyErr_Restore(exc, v, tb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* com_fetch_program_text will attempt to load the line of text that
|
||||||
|
the exception refers to. If it fails, it will return NULL but will
|
||||||
|
not set an exception.
|
||||||
|
|
||||||
|
XXX The functionality of this function is quite similar to the
|
||||||
|
functionality in tb_displayline() in traceback.c.
|
||||||
|
*/
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
PyErr_ProgramText(char *filename, int lineno)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
int i;
|
||||||
|
char linebuf[1000];
|
||||||
|
|
||||||
|
if (filename == NULL || lineno <= 0)
|
||||||
|
return NULL;
|
||||||
|
fp = fopen(filename, "r");
|
||||||
|
if (fp == NULL)
|
||||||
|
return NULL;
|
||||||
|
for (i = 0; i < lineno; i++) {
|
||||||
|
char *pLastChar = &linebuf[sizeof(linebuf) - 2];
|
||||||
|
do {
|
||||||
|
*pLastChar = '\0';
|
||||||
|
if (fgets(linebuf, sizeof linebuf, fp) == NULL)
|
||||||
|
break;
|
||||||
|
/* fgets read *something*; if it didn't get as
|
||||||
|
far as pLastChar, it must have found a newline
|
||||||
|
or hit the end of the file; if pLastChar is \n,
|
||||||
|
it obviously found a newline; else we haven't
|
||||||
|
yet seen a newline, so must continue */
|
||||||
|
} while (*pLastChar != '\0' && *pLastChar != '\n');
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
if (i == lineno) {
|
||||||
|
char *p = linebuf;
|
||||||
|
while (*p == ' ' || *p == '\t' || *p == '\014')
|
||||||
|
p++;
|
||||||
|
return PyString_FromString(p);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
|
@ -6,24 +6,40 @@
|
||||||
#include "symtable.h"
|
#include "symtable.h"
|
||||||
|
|
||||||
#define UNDEFINED_FUTURE_FEATURE "future feature %.100s is not defined"
|
#define UNDEFINED_FUTURE_FEATURE "future feature %.100s is not defined"
|
||||||
|
#define FUTURE_IMPORT_STAR "future statement does not support import *"
|
||||||
|
|
||||||
#define FUTURE_POSSIBLE(FF) ((FF)->ff_last_lineno == -1)
|
#define FUTURE_POSSIBLE(FF) ((FF)->ff_last_lineno == -1)
|
||||||
|
|
||||||
static int
|
static int
|
||||||
future_check_features(PyFutureFeatures *ff, node *n)
|
future_check_features(PyFutureFeatures *ff, node *n, char *filename)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
char *feature;
|
char *feature;
|
||||||
|
node *ch;
|
||||||
|
|
||||||
REQ(n, import_stmt); /* must by from __future__ import ... */
|
REQ(n, import_stmt); /* must by from __future__ import ... */
|
||||||
|
|
||||||
for (i = 3; i < NCH(n); ++i) {
|
for (i = 3; i < NCH(n); ++i) {
|
||||||
feature = STR(CHILD(CHILD(n, i), 0));
|
ch = CHILD(n, i);
|
||||||
|
if (TYPE(ch) == STAR) {
|
||||||
|
PyErr_SetString(PyExc_SyntaxError,
|
||||||
|
FUTURE_IMPORT_STAR);
|
||||||
|
PyErr_SyntaxLocation(filename, ch->n_lineno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
REQ(ch, import_as_name);
|
||||||
|
feature = STR(CHILD(ch, 0));
|
||||||
if (strcmp(feature, FUTURE_NESTED_SCOPES) == 0) {
|
if (strcmp(feature, FUTURE_NESTED_SCOPES) == 0) {
|
||||||
ff->ff_nested_scopes = 1;
|
ff->ff_nested_scopes = 1;
|
||||||
|
} else if (strcmp(feature, "braces") == 0) {
|
||||||
|
PyErr_SetString(PyExc_SyntaxError,
|
||||||
|
"not a chance");
|
||||||
|
PyErr_SyntaxLocation(filename, CHILD(ch, 0)->n_lineno);
|
||||||
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
PyErr_Format(PyExc_SyntaxError,
|
PyErr_Format(PyExc_SyntaxError,
|
||||||
UNDEFINED_FUTURE_FEATURE, feature);
|
UNDEFINED_FUTURE_FEATURE, feature);
|
||||||
|
PyErr_SyntaxLocation(filename, CHILD(ch, 0)->n_lineno);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +52,7 @@ future_error(node *n, char *filename)
|
||||||
PyErr_SetString(PyExc_SyntaxError,
|
PyErr_SetString(PyExc_SyntaxError,
|
||||||
"from __future__ imports must occur at the "
|
"from __future__ imports must occur at the "
|
||||||
"beginning of the file");
|
"beginning of the file");
|
||||||
|
PyErr_SyntaxLocation(filename, n->n_lineno);
|
||||||
/* XXX set filename and lineno */
|
/* XXX set filename and lineno */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,8 +62,10 @@ single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
|
||||||
file_input: (NEWLINE | stmt)* ENDMARKER
|
file_input: (NEWLINE | stmt)* ENDMARKER
|
||||||
stmt: simple_stmt | compound_stmt
|
stmt: simple_stmt | compound_stmt
|
||||||
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
|
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
|
||||||
small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | exec_stmt | assert_stmt
|
small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt
|
||||||
import_stmt: 'import' dotted_as_name (',' dotted_as_name)* | 'from' dotted_name 'import' ('*' | import_as_name (',' import_as_name)*)
|
| import_stmt | global_stmt | exec_stmt | assert_stmt
|
||||||
|
import_stmt: 'import' dotted_as_name (',' dotted_as_name)*
|
||||||
|
| 'from' dotted_name 'import' ('*' | import_as_name (',' import_as_name)*)
|
||||||
import_as_name: NAME [NAME NAME]
|
import_as_name: NAME [NAME NAME]
|
||||||
dotted_as_name: dotted_name [NAME NAME]
|
dotted_as_name: dotted_name [NAME NAME]
|
||||||
dotted_name: NAME ('.' NAME)*
|
dotted_name: NAME ('.' NAME)*
|
||||||
|
@ -64,11 +83,6 @@ future_parse(PyFutureFeatures *ff, node *n, char *filename)
|
||||||
int i, r;
|
int i, r;
|
||||||
loop:
|
loop:
|
||||||
|
|
||||||
/* fprintf(stderr, "future_parse(%d, %d, %s, %d)\n",
|
|
||||||
TYPE(n), NCH(n), (n == NULL) ? "NULL" : STR(n),
|
|
||||||
n->n_lineno);
|
|
||||||
*/
|
|
||||||
|
|
||||||
switch (TYPE(n)) {
|
switch (TYPE(n)) {
|
||||||
|
|
||||||
case single_input:
|
case single_input:
|
||||||
|
@ -162,7 +176,7 @@ future_parse(PyFutureFeatures *ff, node *n, char *filename)
|
||||||
name = CHILD(n, 1);
|
name = CHILD(n, 1);
|
||||||
if (strcmp(STR(CHILD(name, 0)), "__future__") != 0)
|
if (strcmp(STR(CHILD(name, 0)), "__future__") != 0)
|
||||||
return 0;
|
return 0;
|
||||||
if (future_check_features(ff, n) < 0)
|
if (future_check_features(ff, n, filename) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
ff->ff_last_lineno = n->n_lineno + 1;
|
ff->ff_last_lineno = n->n_lineno + 1;
|
||||||
return 1;
|
return 1;
|
||||||
|
|
Loading…
Reference in New Issue