From cfc4c338015cb65a35228706c44485dd57ec238e Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 29 Jul 2015 22:16:01 +0000 Subject: [PATCH] py/compile: Give more precise line number for compile errors. Previous to this patch there were some cases where line numbers for errors were 0 (unknown). Now the compiler attempts to give a better line number where possible, in some cases giving the line number of the closest statement, and other cases the line number of the inner-most scope of the error (eg the line number of the start of the function). This helps to give good (and sometimes exact) line numbers for ViperTypeError exceptions. This patch also makes sure that the first compile error (eg SyntaxError) that is encountered is reported (previously it was the last one that was reported). --- py/compile.c | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/py/compile.c b/py/compile.c index 12134d4a9d..a124203e4b 100644 --- a/py/compile.c +++ b/py/compile.c @@ -81,8 +81,8 @@ typedef struct _compiler_t { uint8_t have_star; // try to keep compiler clean from nlr - // this is set to an exception object if we have a compile error - mp_obj_t compile_error; + mp_obj_t compile_error; // set to an exception object if there's an error + mp_uint_t compile_error_line; // set to best guess of line of error uint next_label; @@ -108,20 +108,19 @@ typedef struct _compiler_t { #endif } compiler_t; -STATIC void compile_error_add_traceback(compiler_t *comp, mp_parse_node_t pn) { - mp_uint_t line; - if (MP_PARSE_NODE_IS_STRUCT(pn)) { - line = (mp_uint_t)((mp_parse_node_struct_t*)pn)->source_line; - } else { - // we don't have a line number, so just pass 0 - line = 0; +STATIC void compile_error_set_line(compiler_t *comp, mp_parse_node_t pn) { + // if the line of the error is unknown then try to update it from the pn + if (comp->compile_error_line == 0 && MP_PARSE_NODE_IS_STRUCT(pn)) { + comp->compile_error_line = (mp_uint_t)((mp_parse_node_struct_t*)pn)->source_line; } - mp_obj_exception_add_traceback(comp->compile_error, comp->source_file, line, comp->scope_cur->simple_name); } STATIC void compile_syntax_error(compiler_t *comp, mp_parse_node_t pn, const char *msg) { - comp->compile_error = mp_obj_new_exception_msg(&mp_type_SyntaxError, msg); - compile_error_add_traceback(comp, pn); + // only register the error if there has been no other error + if (comp->compile_error == MP_OBJ_NULL) { + comp->compile_error = mp_obj_new_exception_msg(&mp_type_SyntaxError, msg); + compile_error_set_line(comp, pn); + } } #if MICROPY_COMP_MODULE_CONST @@ -421,6 +420,11 @@ STATIC void compile_generic_all_nodes(compiler_t *comp, mp_parse_node_struct_t * int num_nodes = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); for (int i = 0; i < num_nodes; i++) { compile_node(comp, pns->nodes[i]); + if (comp->compile_error != MP_OBJ_NULL) { + // add line info for the error in case it didn't have a line number + compile_error_set_line(comp, pns->nodes[i]); + return; + } } } @@ -3535,9 +3539,9 @@ STATIC void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind } if (comp->compile_error != MP_OBJ_NULL) { - // inline assembler had an error; add traceback to its exception + // inline assembler had an error; set line for its exception inline_asm_error: - mp_obj_exception_add_traceback(comp->compile_error, comp->source_file, (mp_uint_t)pns->source_line, comp->scope_cur->simple_name); + comp->compile_error_line = pns->source_line; } } #endif @@ -3822,16 +3826,18 @@ mp_obj_t mp_compile(mp_parse_node_t pn, qstr source_file, uint emit_opt, bool is if (comp->compile_error == MP_OBJ_NULL) { compile_scope(comp, s, MP_PASS_EMIT); } - - #if MICROPY_EMIT_NATIVE - // if viper had an error then add traceback - if (comp->compile_error != MP_OBJ_NULL && s->emit_options == MP_EMIT_OPT_VIPER) { - compile_error_add_traceback(comp, s->pn); - } - #endif } } + if (comp->compile_error != MP_OBJ_NULL) { + // if there is no line number for the error then use the line + // number for the start of this scope + compile_error_set_line(comp, comp->scope_cur->pn); + // add a traceback to the exception using relevant source info + mp_obj_exception_add_traceback(comp->compile_error, comp->source_file, + comp->compile_error_line, comp->scope_cur->simple_name); + } + // free the emitters #if MICROPY_EMIT_CPYTHON