diff options
author | Irit Katriel <1055913+iritkatriel@users.noreply.github.com> | 2022-10-31 13:08:03 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-31 13:08:03 (GMT) |
commit | 39448adc9db0c0444443a42f13a9d7e1441b88e8 (patch) | |
tree | 6b311cc323760f1231550babdb525e1c6feafef6 /Python | |
parent | 29f98b46b77ee528477b9a7b335974b9682f7f14 (diff) | |
download | cpython-39448adc9db0c0444443a42f13a9d7e1441b88e8.zip cpython-39448adc9db0c0444443a42f13a9d7e1441b88e8.tar.gz cpython-39448adc9db0c0444443a42f13a9d7e1441b88e8.tar.bz2 |
gh-98811: use full source location to simplify __future__ imports error checking. This also fixes an incorrect error offset. (GH-98812)
Diffstat (limited to 'Python')
-rw-r--r-- | Python/compile.c | 62 | ||||
-rw-r--r-- | Python/future.c | 53 |
2 files changed, 49 insertions, 66 deletions
diff --git a/Python/compile.c b/Python/compile.c index 7797291..f892478 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -125,18 +125,23 @@ (c->c_flags->cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT) \ && (c->u->u_ste->ste_type == ModuleBlock)) -typedef struct location_ { - int lineno; - int end_lineno; - int col_offset; - int end_col_offset; -} location; +typedef _PyCompilerSrcLocation location; #define LOCATION(LNO, END_LNO, COL, END_COL) \ ((const location){(LNO), (END_LNO), (COL), (END_COL)}) static location NO_LOCATION = {-1, -1, -1, -1}; +/* Return true if loc1 starts after loc2 ends. */ +static inline bool +location_is_after(location loc1, location loc2) { + return (loc1.lineno > loc2.end_lineno) || + ((loc1.lineno == loc2.end_lineno) && + (loc1.col_offset > loc2.end_col_offset)); +} + +#define LOC(x) SRC_LOCATION_FROM_AST(x) + typedef struct jump_target_label_ { int id; } jump_target_label; @@ -1012,11 +1017,6 @@ basicblock_next_instr(basicblock *b) // Artificial instructions #define UNSET_LOC(c) -#define LOC(x) LOCATION((x)->lineno, \ - (x)->end_lineno, \ - (x)->col_offset, \ - (x)->end_col_offset) - /* Return the stack effect of opcode with argument oparg. @@ -3911,59 +3911,61 @@ compiler_import(struct compiler *c, stmt_ty s) static int compiler_from_import(struct compiler *c, stmt_ty s) { - location loc = LOC(s); - Py_ssize_t i, n = asdl_seq_LEN(s->v.ImportFrom.names); - PyObject *names; + Py_ssize_t n = asdl_seq_LEN(s->v.ImportFrom.names); - ADDOP_LOAD_CONST_NEW(c, loc, PyLong_FromLong(s->v.ImportFrom.level)); + ADDOP_LOAD_CONST_NEW(c, LOC(s), PyLong_FromLong(s->v.ImportFrom.level)); - names = PyTuple_New(n); - if (!names) + PyObject *names = PyTuple_New(n); + if (!names) { return 0; + } /* build up the names */ - for (i = 0; i < n; i++) { + for (Py_ssize_t i = 0; i < n; i++) { alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, i); Py_INCREF(alias->name); PyTuple_SET_ITEM(names, i, alias->name); } - if (s->lineno > c->c_future->ff_lineno && s->v.ImportFrom.module && - _PyUnicode_EqualToASCIIString(s->v.ImportFrom.module, "__future__")) { + if (location_is_after(LOC(s), c->c_future->ff_location) && + s->v.ImportFrom.module && + _PyUnicode_EqualToASCIIString(s->v.ImportFrom.module, "__future__")) + { Py_DECREF(names); - return compiler_error(c, loc, "from __future__ imports must occur " + return compiler_error(c, LOC(s), "from __future__ imports must occur " "at the beginning of the file"); } - ADDOP_LOAD_CONST_NEW(c, loc, names); + ADDOP_LOAD_CONST_NEW(c, LOC(s), names); if (s->v.ImportFrom.module) { - ADDOP_NAME(c, loc, IMPORT_NAME, s->v.ImportFrom.module, names); + ADDOP_NAME(c, LOC(s), IMPORT_NAME, s->v.ImportFrom.module, names); } else { _Py_DECLARE_STR(empty, ""); - ADDOP_NAME(c, loc, IMPORT_NAME, &_Py_STR(empty), names); + ADDOP_NAME(c, LOC(s), IMPORT_NAME, &_Py_STR(empty), names); } - for (i = 0; i < n; i++) { + for (Py_ssize_t i = 0; i < n; i++) { alias_ty alias = (alias_ty)asdl_seq_GET(s->v.ImportFrom.names, i); identifier store_name; if (i == 0 && PyUnicode_READ_CHAR(alias->name, 0) == '*') { assert(n == 1); - ADDOP(c, loc, IMPORT_STAR); + ADDOP(c, LOC(s), IMPORT_STAR); return 1; } - ADDOP_NAME(c, loc, IMPORT_FROM, alias->name, names); + ADDOP_NAME(c, LOC(s), IMPORT_FROM, alias->name, names); store_name = alias->name; - if (alias->asname) + if (alias->asname) { store_name = alias->asname; + } - if (!compiler_nameop(c, loc, store_name, Store)) { + if (!compiler_nameop(c, LOC(s), store_name, Store)) { return 0; } } /* remove imported module */ - ADDOP(c, loc, POP_TOP); + ADDOP(c, LOC(s), POP_TOP); return 1; } diff --git a/Python/future.c b/Python/future.c index d465608..2a45d2e 100644 --- a/Python/future.c +++ b/Python/future.c @@ -2,8 +2,6 @@ #include "pycore_ast.h" // _PyAST_GetDocString() #define UNDEFINED_FUTURE_FEATURE "future feature %.100s is not defined" -#define ERR_LATE_FUTURE \ -"from __future__ imports must occur at the beginning of the file" static int future_check_features(PyFutureFeatures *ff, stmt_ty s, PyObject *filename) @@ -56,59 +54,42 @@ future_check_features(PyFutureFeatures *ff, stmt_ty s, PyObject *filename) static int future_parse(PyFutureFeatures *ff, mod_ty mod, PyObject *filename) { - int i, done = 0, prev_line = 0; - - if (!(mod->kind == Module_kind || mod->kind == Interactive_kind)) + if (!(mod->kind == Module_kind || mod->kind == Interactive_kind)) { return 1; + } - if (asdl_seq_LEN(mod->v.Module.body) == 0) + Py_ssize_t n = asdl_seq_LEN(mod->v.Module.body); + if (n == 0) { return 1; + } - /* A subsequent pass will detect future imports that don't - appear at the beginning of the file. There's one case, - however, that is easier to handle here: A series of imports - joined by semi-colons, where the first import is a future - statement but some subsequent import has the future form - but is preceded by a regular import. - */ - - i = 0; - if (_PyAST_GetDocString(mod->v.Module.body) != NULL) + Py_ssize_t i = 0; + if (_PyAST_GetDocString(mod->v.Module.body) != NULL) { i++; + } - for (; i < asdl_seq_LEN(mod->v.Module.body); i++) { + for (; i < n; i++) { stmt_ty s = (stmt_ty)asdl_seq_GET(mod->v.Module.body, i); - if (done && s->lineno > prev_line) - return 1; - prev_line = s->lineno; - - /* The tests below will return from this function unless it is - still possible to find a future statement. The only things - that can precede a future statement are another future - statement and a doc string. - */ + /* The only things that can precede a future statement + * are another future statement and a doc string. + */ if (s->kind == ImportFrom_kind) { identifier modname = s->v.ImportFrom.module; if (modname && _PyUnicode_EqualToASCIIString(modname, "__future__")) { - if (done) { - PyErr_SetString(PyExc_SyntaxError, - ERR_LATE_FUTURE); - PyErr_SyntaxLocationObject(filename, s->lineno, s->col_offset); + if (!future_check_features(ff, s, filename)) { return 0; } - if (!future_check_features(ff, s, filename)) - return 0; - ff->ff_lineno = s->lineno; + ff->ff_location = SRC_LOCATION_FROM_AST(s); } else { - done = 1; + return 1; } } else { - done = 1; + return 1; } } return 1; @@ -126,7 +107,7 @@ _PyFuture_FromAST(mod_ty mod, PyObject *filename) return NULL; } ff->ff_features = 0; - ff->ff_lineno = -1; + ff->ff_location = (_PyCompilerSrcLocation){-1, -1, -1, -1}; if (!future_parse(ff, mod, filename)) { PyObject_Free(ff); |