Kaydet (Commit) ad3d3f2f authored tarafından Jeremy Hylton's avatar Jeremy Hylton

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().
üst 5687ffe0
...@@ -99,6 +99,10 @@ extern DL_IMPORT(int) PyErr_Warn(PyObject *, char *); ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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) ...@@ -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 ...@@ -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) ...@@ -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) ...@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment