Skip to content

Commit d4efd9e

Browse files
committed
add column offset to all syntax errors
1 parent 2bc5c0b commit d4efd9e

File tree

6 files changed

+60
-40
lines changed

6 files changed

+60
-40
lines changed

Include/symtable.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ typedef struct _symtable_entry {
4646
unsigned ste_returns_value : 1; /* true if namespace uses return with
4747
an argument */
4848
int ste_lineno; /* first line of block */
49+
int ste_col_offset; /* offset of first line of block */
4950
int ste_opt_lineno; /* lineno of last exec or import * */
51+
int ste_opt_col_offset; /* offset of last exec or import * */
5052
int ste_tmpname; /* counter for listcomp temp vars */
5153
struct symtable *ste_table;
5254
} PySTEntryObject;

Misc/NEWS

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ What's New in Python 3.2 Alpha 3?
1010
Core and Builtins
1111
-----------------
1212

13-
- Issue #9901: Destroying the GIL in Py_Finalize() can fail if some other
14-
threads are still running. Instead, reinitialize the GIL on a second
15-
call to Py_Initialize().
13+
- All SyntaxErrors now have a column offset and therefore a caret when the error
14+
is printed.
1615

1716
- Issue #9252: PyImport_Import no longer uses a fromlist hack to return the
1817
module that was imported, but instead gets the module from sys.modules.
@@ -59,10 +58,6 @@ Core and Builtins
5958
Library
6059
-------
6160

62-
- Issue #9877: Expose sysconfig.get_makefile_filename()
63-
64-
- logging: Added hasHandlers() method to Logger and LoggerAdapter.
65-
6661
- Issue #1686: Fix string.Template when overriding the pattern attribute.
6762

6863
- Issue #9854: SocketIO objects now observe the RawIOBase interface in

Python/ast.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ new_identifier(const char* n, PyArena *arena)
9090
static int
9191
ast_error(const node *n, const char *errstr)
9292
{
93-
PyObject *u = Py_BuildValue("zi", errstr, LINENO(n));
93+
PyObject *u = Py_BuildValue("zii", errstr, LINENO(n), n->n_col_offset);
9494
if (!u)
9595
return 0;
9696
PyErr_SetObject(PyExc_SyntaxError, u);
@@ -101,7 +101,7 @@ ast_error(const node *n, const char *errstr)
101101
static void
102102
ast_error_finish(const char *filename)
103103
{
104-
PyObject *type, *value, *tback, *errstr, *loc, *tmp;
104+
PyObject *type, *value, *tback, *errstr, *offset, *loc, *tmp;
105105
long lineno;
106106

107107
assert(PyErr_Occurred());
@@ -118,14 +118,19 @@ ast_error_finish(const char *filename)
118118
Py_DECREF(errstr);
119119
return;
120120
}
121+
offset = PyTuple_GetItem(value, 2);
122+
if (!offset) {
123+
Py_DECREF(errstr);
124+
return;
125+
}
121126
Py_DECREF(value);
122127

123128
loc = PyErr_ProgramText(filename, lineno);
124129
if (!loc) {
125130
Py_INCREF(Py_None);
126131
loc = Py_None;
127132
}
128-
tmp = Py_BuildValue("(zlOO)", filename, lineno, Py_None, loc);
133+
tmp = Py_BuildValue("(zlOO)", filename, lineno, offset, loc);
129134
Py_DECREF(loc);
130135
if (!tmp) {
131136
Py_DECREF(errstr);

Python/compile.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ struct compiler_unit {
123123

124124
int u_firstlineno; /* the first lineno of the block */
125125
int u_lineno; /* the lineno for the current stmt */
126+
int u_col_offset; /* the offset of the current stmt */
126127
int u_lineno_set; /* boolean to indicate whether instr
127128
has been generated with current lineno */
128129
};
@@ -486,6 +487,7 @@ compiler_enter_scope(struct compiler *c, identifier name, void *key,
486487
u->u_nfblocks = 0;
487488
u->u_firstlineno = lineno;
488489
u->u_lineno = 0;
490+
u->u_col_offset = 0;
489491
u->u_lineno_set = 0;
490492
u->u_consts = PyDict_New();
491493
if (!u->u_consts) {
@@ -1965,6 +1967,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
19651967
return compiler_error(c, "default 'except:' must be last");
19661968
c->u->u_lineno_set = 0;
19671969
c->u->u_lineno = handler->lineno;
1970+
c->u->u_col_offset = handler->col_offset;
19681971
except = compiler_new_block(c);
19691972
if (except == NULL)
19701973
return 0;
@@ -2247,6 +2250,7 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
22472250

22482251
/* Always assign a lineno to the next instruction for a stmt. */
22492252
c->u->u_lineno = s->lineno;
2253+
c->u->u_col_offset = s->col_offset;
22502254
c->u->u_lineno_set = 0;
22512255

22522256
switch (s->kind) {
@@ -3122,6 +3126,8 @@ compiler_visit_expr(struct compiler *c, expr_ty e)
31223126
c->u->u_lineno = e->lineno;
31233127
c->u->u_lineno_set = 0;
31243128
}
3129+
/* Updating the column offset is always harmless. */
3130+
c->u->u_col_offset = e->col_offset;
31253131
switch (e->kind) {
31263132
case BoolOp_kind:
31273133
return compiler_boolop(c, e);
@@ -3363,8 +3369,8 @@ compiler_error(struct compiler *c, const char *errstr)
33633369
Py_INCREF(Py_None);
33643370
loc = Py_None;
33653371
}
3366-
u = Py_BuildValue("(ziOO)", c->c_filename, c->u->u_lineno,
3367-
Py_None, loc);
3372+
u = Py_BuildValue("(ziiO)", c->c_filename, c->u->u_lineno,
3373+
c->u->u_col_offset, loc);
33683374
if (!u)
33693375
goto exit;
33703376
v = Py_BuildValue("(zO)", errstr, u);

Python/future.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@ future_check_features(PyFutureFeatures *ff, stmt_ty s, const char *filename)
4444
} else if (strcmp(feature, "braces") == 0) {
4545
PyErr_SetString(PyExc_SyntaxError,
4646
"not a chance");
47-
PyErr_SyntaxLocation(filename, s->lineno);
47+
PyErr_SyntaxLocationEx(filename, s->lineno, s->col_offset);
4848
return 0;
4949
} else {
5050
PyErr_Format(PyExc_SyntaxError,
5151
UNDEFINED_FUTURE_FEATURE, feature);
52-
PyErr_SyntaxLocation(filename, s->lineno);
52+
PyErr_SyntaxLocationEx(filename, s->lineno, s->col_offset);
5353
return 0;
5454
}
5555
}
@@ -98,8 +98,7 @@ future_parse(PyFutureFeatures *ff, mod_ty mod, const char *filename)
9898
if (done) {
9999
PyErr_SetString(PyExc_SyntaxError,
100100
ERR_LATE_FUTURE);
101-
PyErr_SyntaxLocation(filename,
102-
s->lineno);
101+
PyErr_SyntaxLocationEx(filename, s->lineno, s->col_offset);
103102
return 0;
104103
}
105104
if (!future_check_features(ff, s, filename))

Python/symtable.c

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
static PySTEntryObject *
2727
ste_new(struct symtable *st, identifier name, _Py_block_ty block,
28-
void *key, int lineno)
28+
void *key, int lineno, int col_offset)
2929
{
3030
PySTEntryObject *ste = NULL;
3131
PyObject *k;
@@ -65,7 +65,9 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block,
6565
ste->ste_varargs = 0;
6666
ste->ste_varkeywords = 0;
6767
ste->ste_opt_lineno = 0;
68+
ste->ste_opt_col_offset = 0;
6869
ste->ste_lineno = lineno;
70+
ste->ste_col_offset = col_offset;
6971

7072
if (st->st_cur != NULL &&
7173
(st->st_cur->ste_nested ||
@@ -163,7 +165,8 @@ PyTypeObject PySTEntry_Type = {
163165
static int symtable_analyze(struct symtable *st);
164166
static int symtable_warn(struct symtable *st, char *msg, int lineno);
165167
static int symtable_enter_block(struct symtable *st, identifier name,
166-
_Py_block_ty block, void *ast, int lineno);
168+
_Py_block_ty block, void *ast, int lineno,
169+
int col_offset);
167170
static int symtable_exit_block(struct symtable *st, void *ast);
168171
static int symtable_visit_stmt(struct symtable *st, stmt_ty s);
169172
static int symtable_visit_expr(struct symtable *st, expr_ty s);
@@ -230,7 +233,7 @@ PySymtable_Build(mod_ty mod, const char *filename, PyFutureFeatures *future)
230233
st->st_future = future;
231234
/* Make the initial symbol information gathering pass */
232235
if (!GET_IDENTIFIER(top) ||
233-
!symtable_enter_block(st, top, ModuleBlock, (void *)mod, 0)) {
236+
!symtable_enter_block(st, top, ModuleBlock, (void *)mod, 0, 0)) {
234237
PySymtable_Free(st);
235238
return NULL;
236239
}
@@ -390,8 +393,8 @@ analyze_name(PySTEntryObject *ste, PyObject *scopes, PyObject *name, long flags,
390393
PyErr_Format(PyExc_SyntaxError,
391394
"name '%U' is parameter and global",
392395
name);
393-
PyErr_SyntaxLocation(ste->ste_table->st_filename,
394-
ste->ste_lineno);
396+
PyErr_SyntaxLocationEx(ste->ste_table->st_filename,
397+
ste->ste_lineno, ste->ste_col_offset);
395398

396399
return 0;
397400
}
@@ -534,8 +537,8 @@ check_unoptimized(const PySTEntryObject* ste) {
534537
break;
535538
}
536539

537-
PyErr_SyntaxLocation(ste->ste_table->st_filename,
538-
ste->ste_opt_lineno);
540+
PyErr_SyntaxLocationEx(ste->ste_table->st_filename, ste->ste_opt_lineno,
541+
ste->ste_opt_col_offset);
539542
return 0;
540543
}
541544

@@ -873,8 +876,8 @@ symtable_warn(struct symtable *st, char *msg, int lineno)
873876
lineno, NULL, NULL) < 0) {
874877
if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
875878
PyErr_SetString(PyExc_SyntaxError, msg);
876-
PyErr_SyntaxLocation(st->st_filename,
877-
st->st_cur->ste_lineno);
879+
PyErr_SyntaxLocationEx(st->st_filename, st->st_cur->ste_lineno,
880+
st->st_cur->ste_col_offset);
878881
}
879882
return 0;
880883
}
@@ -907,7 +910,7 @@ symtable_exit_block(struct symtable *st, void *ast)
907910

908911
static int
909912
symtable_enter_block(struct symtable *st, identifier name, _Py_block_ty block,
910-
void *ast, int lineno)
913+
void *ast, int lineno, int col_offset)
911914
{
912915
PySTEntryObject *prev = NULL;
913916

@@ -918,7 +921,7 @@ symtable_enter_block(struct symtable *st, identifier name, _Py_block_ty block,
918921
}
919922
Py_DECREF(st->st_cur);
920923
}
921-
st->st_cur = ste_new(st, name, block, ast, lineno);
924+
st->st_cur = ste_new(st, name, block, ast, lineno, col_offset);
922925
if (st->st_cur == NULL)
923926
return 0;
924927
if (name == GET_IDENTIFIER(top))
@@ -963,8 +966,9 @@ symtable_add_def(struct symtable *st, PyObject *name, int flag)
963966
if ((flag & DEF_PARAM) && (val & DEF_PARAM)) {
964967
/* Is it better to use 'mangled' or 'name' here? */
965968
PyErr_Format(PyExc_SyntaxError, DUPLICATE_ARGUMENT, name);
966-
PyErr_SyntaxLocation(st->st_filename,
967-
st->st_cur->ste_lineno);
969+
PyErr_SyntaxLocationEx(st->st_filename,
970+
st->st_cur->ste_lineno,
971+
st->st_cur->ste_col_offset);
968972
goto error;
969973
}
970974
val |= flag;
@@ -1114,7 +1118,8 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
11141118
if (s->v.FunctionDef.decorator_list)
11151119
VISIT_SEQ(st, expr, s->v.FunctionDef.decorator_list);
11161120
if (!symtable_enter_block(st, s->v.FunctionDef.name,
1117-
FunctionBlock, (void *)s, s->lineno))
1121+
FunctionBlock, (void *)s, s->lineno,
1122+
s->col_offset))
11181123
return 0;
11191124
VISIT_IN_BLOCK(st, arguments, s->v.FunctionDef.args, s);
11201125
VISIT_SEQ_IN_BLOCK(st, stmt, s->v.FunctionDef.body, s);
@@ -1134,7 +1139,7 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
11341139
if (s->v.ClassDef.decorator_list)
11351140
VISIT_SEQ(st, expr, s->v.ClassDef.decorator_list);
11361141
if (!symtable_enter_block(st, s->v.ClassDef.name, ClassBlock,
1137-
(void *)s, s->lineno))
1142+
(void *)s, s->lineno, s->col_offset))
11381143
return 0;
11391144
if (!GET_IDENTIFIER(__class__) ||
11401145
!symtable_add_def(st, __class__, DEF_LOCAL) ||
@@ -1158,8 +1163,9 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
11581163
if (st->st_cur->ste_generator) {
11591164
PyErr_SetString(PyExc_SyntaxError,
11601165
RETURN_VAL_IN_GENERATOR);
1161-
PyErr_SyntaxLocation(st->st_filename,
1162-
s->lineno);
1166+
PyErr_SyntaxLocationEx(st->st_filename,
1167+
s->lineno,
1168+
s->col_offset);
11631169
return 0;
11641170
}
11651171
}
@@ -1221,15 +1227,19 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
12211227
VISIT_SEQ(st, alias, s->v.Import.names);
12221228
/* XXX Don't have the lineno available inside
12231229
visit_alias */
1224-
if (st->st_cur->ste_unoptimized && !st->st_cur->ste_opt_lineno)
1230+
if (st->st_cur->ste_unoptimized && !st->st_cur->ste_opt_lineno) {
12251231
st->st_cur->ste_opt_lineno = s->lineno;
1232+
st->st_cur->ste_opt_col_offset = s->col_offset;
1233+
}
12261234
break;
12271235
case ImportFrom_kind:
12281236
VISIT_SEQ(st, alias, s->v.ImportFrom.names);
12291237
/* XXX Don't have the lineno available inside
12301238
visit_alias */
1231-
if (st->st_cur->ste_unoptimized && !st->st_cur->ste_opt_lineno)
1239+
if (st->st_cur->ste_unoptimized && !st->st_cur->ste_opt_lineno) {
12321240
st->st_cur->ste_opt_lineno = s->lineno;
1241+
st->st_cur->ste_opt_col_offset = s->col_offset;
1242+
}
12331243
break;
12341244
case Global_kind: {
12351245
int i;
@@ -1324,7 +1334,8 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
13241334
if (e->v.Lambda.args->defaults)
13251335
VISIT_SEQ(st, expr, e->v.Lambda.args->defaults);
13261336
if (!symtable_enter_block(st, lambda,
1327-
FunctionBlock, (void *)e, e->lineno))
1337+
FunctionBlock, (void *)e, e->lineno,
1338+
e->col_offset))
13281339
return 0;
13291340
VISIT_IN_BLOCK(st, arguments, e->v.Lambda.args, (void*)e);
13301341
VISIT_IN_BLOCK(st, expr, e->v.Lambda.body, (void*)e);
@@ -1367,8 +1378,8 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
13671378
if (st->st_cur->ste_returns_value) {
13681379
PyErr_SetString(PyExc_SyntaxError,
13691380
RETURN_VAL_IN_GENERATOR);
1370-
PyErr_SyntaxLocation(st->st_filename,
1371-
e->lineno);
1381+
PyErr_SyntaxLocationEx(st->st_filename,
1382+
e->lineno, e->col_offset);
13721383
return 0;
13731384
}
13741385
break;
@@ -1557,8 +1568,9 @@ symtable_visit_alias(struct symtable *st, alias_ty a)
15571568
else {
15581569
if (st->st_cur->ste_type != ModuleBlock) {
15591570
int lineno = st->st_cur->ste_lineno;
1571+
int col_offset = st->st_cur->ste_col_offset;
15601572
PyErr_SetString(PyExc_SyntaxError, IMPORT_STAR_WARNING);
1561-
PyErr_SyntaxLocation(st->st_filename, lineno);
1573+
PyErr_SyntaxLocationEx(st->st_filename, lineno, col_offset);
15621574
Py_DECREF(store_name);
15631575
return 0;
15641576
}
@@ -1622,7 +1634,8 @@ symtable_handle_comprehension(struct symtable *st, expr_ty e,
16221634
VISIT(st, expr, outermost->iter);
16231635
/* Create comprehension scope for the rest */
16241636
if (!scope_name ||
1625-
!symtable_enter_block(st, scope_name, FunctionBlock, (void *)e, e->lineno)) {
1637+
!symtable_enter_block(st, scope_name, FunctionBlock, (void *)e,
1638+
e->lineno, e->col_offset)) {
16261639
return 0;
16271640
}
16281641
st->st_cur->ste_generator = is_generator;

0 commit comments

Comments
 (0)