note description: "[ Visitor for Eiffel errors. Currently it visists only the node which are defined in the parser library. All the nodes defined by the Eiffel compiler are descendants of COMPILER_ERROR and are using dynamic binding for visiting. This is why there is some code duplication between the code in COMPILER_ERROR and in ERROR_TRACER for the display routines defined in COMPILER_ERROR. When a complete redesign of the error handling is done in the compiler, this code duplication should disappear. ]" legal: "See notice at end of class." status: "See notice at end of class."; date: "$Date$" revision: "$Revision$" class ERROR_TRACER inherit ERROR_VISITOR EIFFEL_LAYOUT ERROR_CONTEXT_PRINTER export {NONE} all end feature -- Display trace (a_text_formatter: TEXT_FORMATTER; a_error: ERROR) -- Display `a_error' in `a_text_formatter' using regular formatting. require text_formatter_not_void: a_text_formatter /= Void error_not_void: a_error /= Void do trace_with_style (a_text_formatter, a_error, normal) end trace_tersely (a_text_formatter: TEXT_FORMATTER; a_error: ERROR) -- Display `a_error' in `a_text_formatter' tersely in a single line. require text_formatter_not_void: a_text_formatter /= Void error_not_void: a_error /= Void do trace_with_style (a_text_formatter, a_error, single_line) end trace_context (a_text_formatter: TEXT_FORMATTER; a_error: ERROR) -- Display source context of `a_error' in `a_text_formatter'. require text_formatter_not_void: a_text_formatter /= Void error_not_void: a_error /= Void do trace_with_style (a_text_formatter, a_error, context) end trace_error_code (t: TEXT_FORMATTER; e: ERROR) -- Display a short error code of `e` in `t`. require t_attached: attached t e_attached: attached e local n: like {ERROR}.subcode do t.add_error (e, e.code) n := e.subcode if e.subcode /= 0 then t.add_char ('(') t.add_int (n) t.add_char (')') end end feature {NONE} -- Display trace_with_style (a_text_formatter: TEXT_FORMATTER; a_error: ERROR; a_kind: INTEGER) -- Display `a_error' in `a_text_formatter' require text_formatter_not_void: a_text_formatter /= Void error_not_void: a_error /= Void valid_kind: a_kind = normal or a_kind = single_line or a_kind = context local retried: BOOLEAN do if not retried then type := a_kind text_formatter := a_text_formatter a_error.process (Current) end text_formatter := Void type := normal rescue retried := True retry end feature -- Access normal: INTEGER = 0 -- A tracing option to show full (potentially multi-line) error message. single_line: INTEGER = 1 -- A tracing option to show brief single-line error message. context: INTEGER = 2 -- A tracing option to show a context of an errot. feature -- Processing process_bad_character (a_value: BAD_CHARACTER) -- Process object `a_value'. do process_syntax_error (a_value) end process_basic_gen_type_err (a_value: BASIC_GEN_TYPE_ERR) -- Process object `a_value'. do process_syntax_error (a_value) end process_error (a_value: ERROR) -- Process object `a_value'. do inspect type when normal then trace_error (text_formatter, a_value) when single_line then trace_single_line (text_formatter, a_value) when context then trace_primary_context (text_formatter, a_value) else end end process_string_extension (a_value: STRING_EXTENSION) -- Process object `a_value'. do process_syntax_error (a_value) end process_string_uncompleted (a_value: STRING_UNCOMPLETED) -- Process object `a_value'. do process_syntax_error (a_value) end process_syntax_error (a_value: SYNTAX_ERROR) -- Process object `a_value'. local l_msg: STRING do if type = normal then a_value.initialize_output text_formatter.add ("Syntax error at line ") text_formatter.add_int (a_value.line) -- Error happened in a class if attached {CLASS_C} a_value.associated_class as l_class1 then text_formatter.add (" in class ") text_formatter.add_class_syntax (a_value, l_class1, l_class1.class_signature) elseif a_value.file_name /= Void then -- `associated_class' May be void at degree 6 when parsing partial classes text_formatter.add (" in file ") text_formatter.add (a_value.file_name) end if a_value.error_message /= Void and then not a_value.error_message.is_empty then text_formatter.add_new_line text_formatter.add (a_value.error_message) end text_formatter.add_new_line l_msg := a_value.syntax_message if not l_msg.is_empty then text_formatter.add ("(") text_formatter.add (l_msg) text_formatter.add (")") text_formatter.add_new_line end if a_value.has_source_text then text_formatter.add_new_line display_line (text_formatter, a_value.previous_line_32) display_syntax_line (text_formatter, a_value.current_line_32, a_value) display_line (text_formatter, a_value.next_line_32) else text_formatter.add (" (source code is not available)") text_formatter.add_new_line end elseif type = context then if attached {CLASS_C} a_value.associated_class as l_class2 then if attached {TEXT_FORMATTER} text_formatter as l_formatter then print_context_class (l_formatter, l_class2) end elseif a_value.file_name /= Void then process_error (a_value) end elseif type = single_line then a_value.initialize_output if attached a_value.syntax_message as m and then not m.is_empty then text_formatter.add (a_value.syntax_message) else text_formatter.add ("Syntax error at line ") text_formatter.add_int (a_value.line) text_formatter.add (".") end end end process_syntax_warning (a_value: SYNTAX_WARNING) -- Process object `a_value'. local l_w: STRING_32 do if type = normal then a_value.initialize_output text_formatter.add_error (a_value, "Obsolete") text_formatter.add (" syntax used at line ") text_formatter.add_int (a_value.line) -- Error happened in a class if attached {CLASS_C} a_value.associated_class as l_class1 then text_formatter.add (" in class ") text_formatter.add_class_syntax (a_value, l_class1, l_class1.class_signature) end l_w := a_value.warning_message if not l_w.is_empty then text_formatter.add_new_line text_formatter.add (l_w) text_formatter.add_new_line end text_formatter.add_new_line if a_value.has_source_text then display_line (text_formatter, a_value.previous_line_32) display_syntax_line (text_formatter, a_value.current_line_32, a_value) display_line (text_formatter, a_value.next_line_32) else text_formatter.add (" (source code is not available)") text_formatter.add_new_line end elseif type = single_line then a_value.initialize_output text_formatter.add ("Obsolete syntax: ") text_formatter.add (a_value.warning_message) elseif type = context then if attached {CLASS_C} a_value.associated_class as l_class2 then if attached {TEXT_FORMATTER} text_formatter as l_formatter then print_context_class (l_formatter, l_class2) end else trace_primary_context (text_formatter, a_value) end end end process_user_defined_error (a_value: USER_DEFINED_ERROR) -- Process object `a_value'. do if attached {COMPILER_ERROR} a_value as l_compiler_error then inspect type when normal then l_compiler_error.trace (text_formatter) when single_line then l_compiler_error.trace_single_line (text_formatter) when context then l_compiler_error.trace_primary_context (text_formatter) end else process_error (a_value) end end process_verbatim_string_uncompleted (a_value: VERBATIM_STRING_UNCOMPLETED) -- Process object `a_value'. do process_syntax_error (a_value) end process_viin (a_value: VIIN) -- Process object `a_value'. do if type = normal then print_error_message (text_formatter, a_value) a_value.initialize_output if attached {CLASS_C} a_value.associated_class as l_class then text_formatter.add ("Class: ") text_formatter.add_class_syntax (a_value, l_class, l_class.class_signature) text_formatter.add_new_line elseif a_value.file_name /= Void then -- `current_class' May be void at degree 6 when parsing partial classes text_formatter.add ("Error in file ") text_formatter.add (a_value.file_name) text_formatter.add_new_line end text_formatter.add ("Line: ") text_formatter.add_int (a_value.line) -- Error happened in a class text_formatter.add_new_line if a_value.has_source_text then display_line (text_formatter, a_value.previous_line_32) display_syntax_line (text_formatter, a_value.current_line_32, a_value) display_line (text_formatter, a_value.next_line_32) else text_formatter.add (" (source code is not available)") text_formatter.add_new_line end else process_error (a_value) end end process_vvok (a_value: VVOK) -- Process object `a_value'. do if type = normal then print_error_message (text_formatter, a_value) a_value.initialize_output if attached {CLASS_C} a_value.associated_class as l_class then text_formatter.add ("Class: ") text_formatter.add_class_syntax (a_value, l_class, l_class.class_signature) text_formatter.add_new_line elseif a_value.file_name /= Void then -- `current_class' May be void at degree 6 when parsing partial classes text_formatter.add ("Error in file ") text_formatter.add (a_value.file_name) text_formatter.add_new_line end text_formatter.add ("Line: ") text_formatter.add_int (a_value.line) -- Error happened in a class text_formatter.add_new_line if a_value.has_source_text then display_line (text_formatter, a_value.previous_line_32) display_syntax_line (text_formatter, a_value.current_line_32, a_value) display_line (text_formatter, a_value.next_line_32) else text_formatter.add (" (source code is not available)") text_formatter.add_new_line end else process_error (a_value) end end feature {NONE} -- Type of display type: INTEGER -- Type of display requested. text_formatter: TEXT_FORMATTER feature {NONE} -- Trace trace_error (a_text_formatter: TEXT_FORMATTER; a_error: ERROR) -- Display full error message in `a_text_formatter'. require valid_st: a_text_formatter /= Void; error_not_void: a_error /= Void is_defined: a_error.is_defined do print_error_message (a_text_formatter, a_error); if attached {COMPILER_ERROR} a_error as l_compiler_error then l_compiler_error.build_explain (a_text_formatter); end end; trace_single_line (a_text_formatter: TEXT_FORMATTER; a_error: ERROR) -- Display short error, single line message in `a_text_formatter'. require valid_st: a_text_formatter /= Void; error_not_void: a_error /= Void is_defined: a_error.is_defined do print_single_line_error_message (a_text_formatter, a_error) end trace_primary_context (a_text_formatter: TEXT_FORMATTER; a_error: ERROR) -- Build the primary context string so errors can be navigated to require valid_st: a_text_formatter /= Void error_not_void: a_error /= Void is_defined: a_error.is_defined do if a_error.has_associated_file then a_text_formatter.add_string (a_error.file_name) end end feature {NONE} -- Line display_line (a_text_formatter: TEXT_FORMATTER; a_line: STRING_32) -- Display `a_line' in `a_text_formatter'. It translates `%T' accordingly to `a_text_formatter' specification -- which is to call `add_indent'. require st_not_void: a_text_formatter /= Void local i: INTEGER nb: INTEGER c: CHARACTER_32 do if a_line /= Void then from nb := a_line.count until i = nb loop i := i + 1 c := a_line.item (i) if c = '%T' then a_text_formatter.add (" ") a_text_formatter.add (" ") a_text_formatter.add (" ") a_text_formatter.add (" ") else a_text_formatter.add (create {STRING_32}.make_filled (c, 1)) end end a_text_formatter.add_new_line end end display_syntax_line (a_text_formatter: TEXT_FORMATTER; a_line: STRING_32; a_error: ERROR) -- Display `a_line' which does like `display_line' but with an additional -- arrowed line that points out to `column' where syntax issue is located. require st_not_void: a_text_formatter /= Void a_line_not_void: a_line /= Void error_not_void: a_error /= Void local i, nb: INTEGER c: CHARACTER_32 position, nb_tab: INTEGER do from nb := a_line.count until i = nb loop i := i + 1 c := a_line.item (i) if c = '%T' then a_text_formatter.add (" ") a_text_formatter.add (" ") a_text_formatter.add (" ") a_text_formatter.add (" ") if i <= a_error.column then nb_tab := nb_tab + 1 end else a_text_formatter.add (create {STRING_32}.make_filled (c, 1)) end end a_text_formatter.add_new_line if a_error.column > 0 then position := (a_error.column - 1) + 3 * nb_tab else position := 3 * nb_tab end if position = 0 then a_text_formatter.add ("^---------------------------") a_text_formatter.add_new_line else from i := 1 until i > position loop a_text_formatter.add ("-") i := i + 1 end a_text_formatter.add ("^") a_text_formatter.add_new_line end end feature {NONE} -- Implementation print_single_line_error_message (a_text_formatter: TEXT_FORMATTER; a_error: ERROR) -- Displays single line help in `a_text_formatter'. require valid_st: a_text_formatter /= Void error_not_void: a_error /= Void local l_file_name: PATH l_file: PLAIN_TEXT_FILE l_text, l_line: STRING l_stop: BOOLEAN i: INTEGER do l_file_name := eiffel_layout.error_path.extended ("short") if a_error.subcode /= 0 then l_file_name := l_file_name.extended (a_error.help_file_name + a_error.subcode.out) else l_file_name := l_file_name.extended (a_error.help_file_name) end create l_file.make_with_path (l_file_name) if l_file.exists then create l_text.make (255) from l_file.open_read until l_file.end_of_file or l_stop loop l_file.read_line; l_line := l_file.last_string l_stop := not l_text.is_empty and then not l_line.is_empty and then not l_line.item (1).is_space if not l_stop then l_line.prune_all_leading (' ') l_line.prune_all_trailing (' ') if not l_text.is_empty then l_text.append_character (' ') end l_text.append (l_line) end end l_file.close -- Remove error part i := l_text.index_of (':', 1) if i > 0 then l_text.keep_tail (l_text.count - i) end l_text.prune_all_leading (' ') l_text.prune_all_trailing (' ') if not l_text.is_empty then if l_text.item (l_text.count) /= '.' and then l_text.item (l_text.count).is_alpha_numeric then -- Append missing punctuation l_text.append_character ('.') end if l_text.item (1).is_alpha then -- Change initial character to upper case l_text.put (l_text.item (1).as_upper, 1) end end end if l_text = Void or else l_text.is_empty then l_text := once "Unable to retrieve error help information." end a_text_formatter.add (l_text) end print_error_message (a_text_formatter: TEXT_FORMATTER; a_error: ERROR) -- Display error in `a_text_formatter'. require valid_st: a_text_formatter /= Void error_not_void: a_error /= Void do a_text_formatter.add (a_error.error_string); a_text_formatter.add (" code: "); a_text_formatter.add_error (a_error, a_error.code); if a_error.subcode /= 0 then a_text_formatter.add ("("); a_text_formatter.add_int (a_error.subcode); a_text_formatter.add (")"); a_text_formatter.add_new_line else a_text_formatter.add_new_line; end; print_short_help (a_text_formatter, a_error); end; frozen print_short_help (a_text_formatter: TEXT_FORMATTER; a_error: ERROR) -- Display help in `a_text_formatter'. require valid_st: a_text_formatter /= Void error_not_void: a_error /= Void local f_name: PATH file: PLAIN_TEXT_FILE do f_name := eiffel_layout.error_path.extended ("short") if a_error.subcode /= 0 then f_name := f_name.extended (a_error.help_file_name + a_error.subcode.out) else f_name := f_name.extended (a_error.help_file_name) end create file.make_with_path (f_name) if file.exists then from file.open_read until file.end_of_file loop file.read_line a_text_formatter.add (file.last_string.twin) a_text_formatter.add_new_line end file.close else a_text_formatter.add_new_line a_text_formatter.add ("No help available for this error") a_text_formatter.add_new_line a_text_formatter.add ("(cannot read file: ") a_text_formatter.add (f_name.name) a_text_formatter.add (")") a_text_formatter.add_new_line a_text_formatter.add_new_line a_text_formatter.add ("An error message should always be available.") a_text_formatter.add_new_line a_text_formatter.add ("Please contact ISE.") a_text_formatter.add_new_line a_text_formatter.add_new_line end end feature {NONE} -- Implementation frozen print_context_of_error (a_context_class: CLASS_C; a_text_formatter: TEXT_FORMATTER; a_error: ERROR) -- Display the line number in `a_text_formatter'. require st_not_void: a_text_formatter /= Void a_context_class_not_void: a_context_class /= Void error_not_void: a_error /= Void valid_line: a_error.line > 0 do a_error.initialize_output a_text_formatter.add ("Line: ") a_text_formatter.add (a_error.line.out) if a_context_class.lace_class.config_class.has_modification_date_changed then a_text_formatter.add (" (source code has changed)") a_text_formatter.add_new_line elseif not a_error.has_source_text then a_text_formatter.add (" (source code is not available)") a_text_formatter.add_new_line elseif a_error.line > 0 then a_text_formatter.add_new_line a_text_formatter.add (" ") if attached a_error.previous_line_32 as l_line then if not l_line.is_empty then l_line.replace_substring_all ({STRING_32} "%T", {STRING_32} " ") end a_text_formatter.add (l_line) a_text_formatter.add_new_line end a_text_formatter.add ("->") if attached a_error.current_line_32 as l_c_line and then not l_c_line.is_empty then l_c_line.replace_substring_all ({STRING_32} "%T", {STRING_32} " ") a_text_formatter.add (l_c_line) end a_text_formatter.add_new_line if attached a_error.next_line_32 as l_n_line then a_text_formatter.add (" ") if not l_n_line.is_empty then l_n_line.replace_substring_all ({STRING_32} "%T", {STRING_32} " ") end a_text_formatter.add (l_n_line) a_text_formatter.add_new_line end end end note copyright: "Copyright (c) 1984-2020, Eiffel Software" license: "GPL version 2 (see http://www.eiffel.com/licensing/gpl.txt)" licensing_options: "http://www.eiffel.com/licensing" copying: "[ This file is part of Eiffel Software's Eiffel Development Environment. Eiffel Software's Eiffel Development Environment is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2 of the License (available at the URL listed under "license" above). Eiffel Software's Eiffel Development Environment is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Eiffel Software's Eiffel Development Environment; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ]" source: "[ Eiffel Software 5949 Hollister Ave., Goleta, CA 93117 USA Telephone 805-685-1006, Fax 805-685-6869 Website http://www.eiffel.com Customer support http://support.eiffel.com ]" end