Pretty print kernel exceptions. Improve default lean frontend error messages.

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2013-08-24 13:16:43 -07:00
parent 0b112b6637
commit f0edf2b4a3
5 changed files with 100 additions and 9 deletions

View file

@ -17,6 +17,7 @@ Author: Leonardo de Moura
#include "option_declarations.h"
#include "expr_maps.h"
#include "sstream.h"
#include "kernel_exception.h"
#include "lean_frontend.h"
#include "lean_parser.h"
#include "lean_scanner.h"
@ -1157,13 +1158,31 @@ class parser_fn {
}
/*@}*/
void error(char const * msg, unsigned line, unsigned pos) {
regular(m_frontend) << "Error (line: " << line << ", pos: " << pos << ") " << msg << endl;
void display_error_pos(unsigned line, unsigned pos) { regular(m_frontend) << "Error (line: " << line << ", pos: " << pos << ")"; }
void display_error_pos(pos_info const & p) { display_error_pos(p.first, p.second); }
void display_error_pos(expr const & e) {
if (e) {
auto it = m_expr_pos_info.find(e);
if (it == m_expr_pos_info.end())
return display_error_pos(m_last_cmd_pos);
else
return display_error_pos(it->second);
} else {
return display_error_pos(m_last_cmd_pos);
}
}
void display_error(char const * msg, unsigned line, unsigned pos) {
display_error_pos(line, pos);
regular(m_frontend) << " " << msg << endl;
sync();
}
void error(char const * msg) {
error(msg, m_scanner.get_line(), m_scanner.get_pos());
void display_error(char const * msg) {
display_error(msg, m_scanner.get_line(), m_scanner.get_pos());
}
void display_error(kernel_exception const & ex) {
display_error_pos(ex.get_main_expr());
regular(m_frontend) << " " << ex << endl;
sync();
}
public:
@ -1206,14 +1225,21 @@ public:
if (m_use_exceptions) {
throw parser_exception(ex.what(), ex.m_pos.first, ex.m_pos.second);
} else {
error(ex.what(), ex.m_pos.first, ex.m_pos.second);
display_error(ex.what(), ex.m_pos.first, ex.m_pos.second);
}
} catch (kernel_exception & ex) {
m_found_errors = true;
if (m_use_exceptions) {
throw;
} else {
display_error(ex);
}
} catch (exception & ex) {
m_found_errors = true;
if (m_use_exceptions) {
throw;
} else {
error(ex.what());
display_error(ex.what());
}
}
}

View file

@ -20,6 +20,12 @@ public:
kernel_exception(environment const & env, char const * msg):exception(msg), m_env(env) {}
virtual ~kernel_exception() noexcept {}
environment const & get_environment() const { return m_env; }
/**
\brief Return a reference to the main expression associated with this exception.
Return the null expression if there is none. This information is used to provide
better error messages.
*/
virtual expr const & get_main_expr() const { return expr::null(); }
};
/** \brief Base class for unknown universe or object exceptions. */
@ -96,6 +102,7 @@ public:
virtual ~app_type_mismatch_exception() {}
context const & get_context() const { return m_context; }
expr const & get_application() const { return m_app; }
virtual expr const & get_main_expr() const { return get_application(); }
unsigned get_arg_pos() const { return m_arg_pos; }
expr const & get_expected_type() const { return m_expected_type; }
expr const & get_given_type() const { return m_given_type; }
@ -119,6 +126,7 @@ public:
virtual ~function_expected_exception() {}
context const & get_context() const { return m_context; }
expr const & get_expr() const { return m_expr; }
virtual expr const & get_main_expr() const { return get_expr(); }
virtual char const * what() const noexcept { return "function expected"; }
};
@ -139,6 +147,7 @@ public:
virtual ~type_expected_exception() {}
context const & get_context() const { return m_context; }
expr const & get_expr() const { return m_expr; }
virtual expr const & get_main_expr() const { return get_expr(); }
virtual char const * what() const noexcept { return "type expected"; }
};

View file

@ -7,7 +7,7 @@ Author: Leonardo de Moura
#include <sstream>
#include "formatter.h"
#include "printer.h"
#include "kernel_exception.h"
namespace lean {
class simple_formatter_cell : public formatter_cell {
public:
@ -34,4 +34,46 @@ public:
formatter mk_simple_formatter() {
return formatter(new simple_formatter_cell());
}
format formatter::operator()(kernel_exception const & ex, options const & opts) {
if (unknown_name_exception const * _ex = dynamic_cast<unknown_name_exception const *>(&ex)) {
return format{format("unknown object '"), format(_ex->get_name()), format("'")};
} else if (already_declared_exception const * _ex = dynamic_cast<already_declared_exception const *>(&ex)) {
return format{format("invalid object declaration, environment already has an object named '"),
format(_ex->get_name()), format("'")};
} else if (app_type_mismatch_exception const * _ex = dynamic_cast<app_type_mismatch_exception const *>(&ex)) {
unsigned indent = get_pp_indent(opts);
format app_f = operator()(_ex->get_context(), _ex->get_application(), false, opts);
format given_f = operator()(_ex->get_context(), _ex->get_given_type(), false, opts);
format expected_f = operator()(_ex->get_context(), _ex->get_expected_type(), false, opts);
unsigned arg_pos = _ex->get_arg_pos();
return format({format("type mismatch at application argument"), space(), format(arg_pos), space(), format("of"),
nest(indent, compose(line(), app_f)),
line(), format("expected type"),
nest(indent, compose(line(), expected_f)),
line(), format("given type"),
nest(indent, compose(line(), given_f))});
} else if (function_expected_exception const * _ex = dynamic_cast<function_expected_exception const *>(&ex)) {
unsigned indent = get_pp_indent(opts);
format expr_f = operator()(_ex->get_context(), _ex->get_expr(), false, opts);
return format({format("function expected at"),
nest(indent, compose(line(), expr_f))});
} else if (type_expected_exception const * _ex = dynamic_cast<type_expected_exception const *>(&ex)) {
unsigned indent = get_pp_indent(opts);
format expr_f = operator()(_ex->get_context(), _ex->get_expr(), false, opts);
return format({format("type expected, got"),
nest(indent, compose(line(), expr_f))});
} else if (def_type_mismatch_exception const * _ex = dynamic_cast<def_type_mismatch_exception const *>(&ex)) {
unsigned indent = get_pp_indent(opts);
format name_f = format(_ex->get_name());
format type_f = operator()(_ex->get_type(), opts);
format value_f = operator()(_ex->get_value_type(), opts);
return format({format("type mismatch at definition '"), name_f, format("', expected type"),
nest(indent, compose(line(), type_f)),
line(), format("given type"),
nest(indent, compose(line(), value_f))});
} else {
return format(ex.what());
}
}
}

View file

@ -35,7 +35,7 @@ public:
/** \brief Format the given environment */
virtual format operator()(environment const & env, options const & opts) = 0;
};
class kernel_exception;
class formatter {
std::shared_ptr<formatter_cell> m_cell;
public:
@ -46,6 +46,8 @@ public:
format operator()(context const & c, expr const & e, bool format_ctx = false, options const & opts = options()) { return (*m_cell)(c, e, format_ctx, opts); }
format operator()(object const & obj, options const & opts = options()) { return (*m_cell)(obj, opts); }
format operator()(environment const & env, options const & opts = options()) { return (*m_cell)(env, opts); }
/** \brief Pretty print a kernel exception using the this formatter */
format operator()(kernel_exception const & ex, options const & opts = options());
};
formatter mk_simple_formatter();

View file

@ -89,6 +89,18 @@ inline diagnostic const & operator<<(diagnostic const & out, object const & obj)
return out;
}
inline regular const & operator<<(regular const & out, kernel_exception const & ex) {
options const & opts = out.m_state.get_options();
out.m_state.get_regular_channel().get_stream() << mk_pair(out.m_state.get_formatter()(ex, opts), opts);
return out;
}
inline diagnostic const & operator<<(diagnostic const & out, kernel_exception const & ex) {
options const & opts = out.m_state.get_options();
out.m_state.get_diagnostic_channel().get_stream() << mk_pair(out.m_state.get_formatter()(ex, opts), opts);
return out;
}
template<typename T>
inline regular const & operator<<(regular const & out, T const & t) {
out.m_state.get_regular_channel().get_stream() << t;