2011-07-14 19:26:10 -05:00
|
|
|
import std::ivec;
|
2011-07-11 01:27:03 -05:00
|
|
|
import std::uint;
|
|
|
|
import std::str;
|
2011-07-12 12:59:18 -05:00
|
|
|
import std::termivec;
|
|
|
|
import std::ioivec;
|
2011-07-05 04:48:19 -05:00
|
|
|
import std::option;
|
|
|
|
import std::option::some;
|
|
|
|
import std::option::none;
|
|
|
|
|
|
|
|
type filename = str;
|
|
|
|
|
|
|
|
/* A codemap is a thing that maps uints to file/line/column positions
|
|
|
|
* in a crate. This to make it possible to represent the positions
|
|
|
|
* with single-word things, rather than passing records all over the
|
|
|
|
* compiler.
|
|
|
|
*/
|
2011-07-14 19:26:10 -05:00
|
|
|
type filemap = @rec(filename name, uint start_pos, mutable uint[] lines);
|
2011-07-05 04:48:19 -05:00
|
|
|
|
2011-07-14 19:26:10 -05:00
|
|
|
type codemap = @rec(mutable filemap[] files);
|
2011-07-05 04:48:19 -05:00
|
|
|
|
|
|
|
type loc = rec(filename filename, uint line, uint col);
|
|
|
|
|
|
|
|
fn new_codemap() -> codemap {
|
2011-07-14 19:26:10 -05:00
|
|
|
let filemap[] files = ~[];
|
2011-07-05 04:48:19 -05:00
|
|
|
ret @rec(mutable files=files);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn new_filemap(filename filename, uint start_pos) -> filemap {
|
2011-07-13 14:19:59 -05:00
|
|
|
ret @rec(name=filename, start_pos=start_pos, mutable lines=[start_pos]);
|
2011-07-05 04:48:19 -05:00
|
|
|
}
|
|
|
|
|
2011-07-14 19:26:10 -05:00
|
|
|
fn next_line(filemap file, uint pos) { file.lines += ~[pos]; }
|
2011-07-05 04:48:19 -05:00
|
|
|
|
|
|
|
fn lookup_pos(codemap map, uint pos) -> loc {
|
|
|
|
auto a = 0u;
|
2011-07-14 19:26:10 -05:00
|
|
|
auto b = ivec::len[filemap](map.files);
|
2011-07-05 04:48:19 -05:00
|
|
|
while (b - a > 1u) {
|
|
|
|
auto m = (a + b) / 2u;
|
|
|
|
if (map.files.(m).start_pos > pos) { b = m; } else { a = m; }
|
|
|
|
}
|
|
|
|
auto f = map.files.(a);
|
|
|
|
a = 0u;
|
2011-07-14 19:26:10 -05:00
|
|
|
b = ivec::len[uint](f.lines);
|
2011-07-05 04:48:19 -05:00
|
|
|
while (b - a > 1u) {
|
|
|
|
auto m = (a + b) / 2u;
|
|
|
|
if (f.lines.(m) > pos) { b = m; } else { a = m; }
|
|
|
|
}
|
|
|
|
ret rec(filename=f.name, line=a + 1u, col=pos - f.lines.(a));
|
|
|
|
}
|
|
|
|
|
|
|
|
type span = rec(uint lo, uint hi);
|
|
|
|
|
|
|
|
fn span_to_str(&span sp, &codemap cm) -> str {
|
|
|
|
auto lo = lookup_pos(cm, sp.lo);
|
|
|
|
auto hi = lookup_pos(cm, sp.hi);
|
|
|
|
ret #fmt("%s:%u:%u:%u:%u", lo.filename, lo.line, lo.col, hi.line, hi.col);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn emit_diagnostic(&option::t[span] sp, &str msg, &str kind, u8 color,
|
|
|
|
&codemap cm) {
|
|
|
|
auto ss = "<input>:0:0:0:0";
|
2011-07-11 01:27:03 -05:00
|
|
|
let option::t[@file_lines] maybe_lines = none;
|
2011-07-05 04:48:19 -05:00
|
|
|
alt (sp) {
|
2011-07-11 01:27:03 -05:00
|
|
|
case (some(?ssp)) {
|
|
|
|
ss = span_to_str(ssp, cm);
|
2011-07-13 09:26:46 -05:00
|
|
|
maybe_lines = some(span_to_lines(ssp, cm));
|
2011-07-11 01:27:03 -05:00
|
|
|
}
|
2011-07-05 04:48:19 -05:00
|
|
|
case (none) { }
|
|
|
|
}
|
2011-07-12 12:59:18 -05:00
|
|
|
ioivec::stdout().write_str(ss + ": ");
|
|
|
|
if (termivec::color_supported()) {
|
|
|
|
termivec::fg(ioivec::stdout().get_buf_writer(), color);
|
2011-07-05 04:48:19 -05:00
|
|
|
}
|
2011-07-12 12:59:18 -05:00
|
|
|
ioivec::stdout().write_str(#fmt("%s:", kind));
|
|
|
|
if (termivec::color_supported()) {
|
|
|
|
termivec::reset(ioivec::stdout().get_buf_writer());
|
2011-07-05 04:48:19 -05:00
|
|
|
}
|
2011-07-12 12:59:18 -05:00
|
|
|
ioivec::stdout().write_str(#fmt(" %s\n", msg));
|
2011-07-11 01:27:03 -05:00
|
|
|
alt (maybe_lines) {
|
|
|
|
case (some(?lines)) {
|
2011-07-11 13:31:57 -05:00
|
|
|
// FIXME: reading in the entire file is the worst possible way to
|
|
|
|
// get access to the necessary lines.
|
2011-07-12 12:59:18 -05:00
|
|
|
auto rdr = ioivec::file_reader(lines.name);
|
|
|
|
auto file = str::unsafe_from_bytes_ivec(rdr.read_whole_stream());
|
2011-07-13 09:18:40 -05:00
|
|
|
auto fm = get_filemap(cm, lines.name);
|
2011-07-11 13:31:57 -05:00
|
|
|
|
|
|
|
// arbitrarily only print up to six lines of the error
|
|
|
|
auto max_lines = 6u;
|
|
|
|
auto elided = false;
|
|
|
|
auto display_lines = lines.lines;
|
2011-07-14 19:26:10 -05:00
|
|
|
if (ivec::len(display_lines) > max_lines) {
|
|
|
|
display_lines = ivec::slice(display_lines, 0u, max_lines);
|
2011-07-11 13:31:57 -05:00
|
|
|
elided = true;
|
|
|
|
}
|
|
|
|
// Print the offending lines
|
|
|
|
for (uint line in display_lines) {
|
2011-07-12 12:59:18 -05:00
|
|
|
ioivec::stdout().write_str(#fmt("%s:%u ", fm.name,
|
|
|
|
line + 1u));
|
2011-07-13 09:18:40 -05:00
|
|
|
auto s = get_line(fm, line as int, file);
|
2011-07-11 01:27:03 -05:00
|
|
|
if (!str::ends_with(s, "\n")) {
|
|
|
|
s += "\n";
|
|
|
|
}
|
2011-07-12 12:59:18 -05:00
|
|
|
ioivec::stdout().write_str(s);
|
2011-07-11 01:27:03 -05:00
|
|
|
}
|
2011-07-11 13:31:57 -05:00
|
|
|
if (elided) {
|
2011-07-14 19:26:10 -05:00
|
|
|
auto last_line = display_lines.(ivec::len(display_lines) -
|
|
|
|
1u);
|
2011-07-11 13:31:57 -05:00
|
|
|
auto s = #fmt("%s:%u ", fm.name, last_line + 1u);
|
|
|
|
auto indent = str::char_len(s);
|
|
|
|
auto out = "";
|
|
|
|
while (indent > 0u) { out += " "; indent -= 1u; }
|
|
|
|
out += "...\n";
|
2011-07-12 12:59:18 -05:00
|
|
|
ioivec::stdout().write_str(out);
|
2011-07-11 13:31:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// If there's one line at fault we can easily point to the problem
|
2011-07-14 19:26:10 -05:00
|
|
|
if (ivec::len(lines.lines) == 1u) {
|
2011-07-11 13:31:57 -05:00
|
|
|
auto lo = codemap::lookup_pos(cm, option::get(sp).lo);
|
2011-07-13 09:18:40 -05:00
|
|
|
auto lo = lookup_pos(cm, option::get(sp).lo);
|
2011-07-11 13:31:57 -05:00
|
|
|
auto digits = 0u;
|
|
|
|
auto num = lines.lines.(0) / 10u;
|
|
|
|
|
|
|
|
// how many digits must be indent past?
|
|
|
|
while (num > 0u) { num /= 10u; digits += 1u; }
|
|
|
|
|
|
|
|
// indent past |name:## | and the 0-offset column location
|
|
|
|
auto left = str::char_len(fm.name) + digits + lo.col + 3u;
|
|
|
|
auto s = "";
|
|
|
|
while (left > 0u) { str::push_char(s, ' '); left -= 1u; }
|
|
|
|
|
|
|
|
s += "^";
|
2011-07-13 09:18:40 -05:00
|
|
|
auto hi = lookup_pos(cm, option::get(sp).hi);
|
2011-07-11 13:31:57 -05:00
|
|
|
if (hi.col != lo.col) {
|
|
|
|
// the ^ already takes up one space
|
|
|
|
auto width = hi.col - lo.col - 1u;
|
|
|
|
while (width > 0u) {
|
|
|
|
str::push_char(s, '~');
|
|
|
|
width -= 1u;
|
|
|
|
}
|
|
|
|
}
|
2011-07-12 12:59:18 -05:00
|
|
|
ioivec::stdout().write_str(s + "\n");
|
2011-07-11 13:31:57 -05:00
|
|
|
}
|
2011-07-11 01:27:03 -05:00
|
|
|
}
|
|
|
|
case (_) {}
|
|
|
|
}
|
2011-07-05 04:48:19 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn emit_warning(&option::t[span] sp, &str msg, &codemap cm) {
|
|
|
|
emit_diagnostic(sp, msg, "warning", 11u8, cm);
|
|
|
|
}
|
|
|
|
fn emit_error(&option::t[span] sp, &str msg, &codemap cm) {
|
|
|
|
emit_diagnostic(sp, msg, "error", 9u8, cm);
|
|
|
|
}
|
|
|
|
fn emit_note(&option::t[span] sp, &str msg, &codemap cm) {
|
|
|
|
emit_diagnostic(sp, msg, "note", 10u8, cm);
|
|
|
|
}
|
|
|
|
|
2011-07-14 19:26:10 -05:00
|
|
|
type file_lines = rec(str name, uint[] lines);
|
2011-07-11 01:27:03 -05:00
|
|
|
|
|
|
|
fn span_to_lines(span sp, codemap::codemap cm) -> @file_lines {
|
|
|
|
auto lo = codemap::lookup_pos(cm, sp.lo);
|
|
|
|
auto hi = codemap::lookup_pos(cm, sp.hi);
|
2011-07-14 19:26:10 -05:00
|
|
|
auto lines = ~[];
|
2011-07-11 01:27:03 -05:00
|
|
|
for each (uint i in uint::range(lo.line - 1u, hi.line as uint)) {
|
2011-07-14 19:26:10 -05:00
|
|
|
lines += ~[i];
|
2011-07-11 01:27:03 -05:00
|
|
|
}
|
|
|
|
ret @rec(name=lo.filename, lines=lines);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_line(filemap fm, int line, &str file) -> str {
|
2011-07-13 13:00:11 -05:00
|
|
|
let uint begin = fm.lines.(line) - fm.start_pos;
|
2011-07-11 01:27:03 -05:00
|
|
|
let uint end;
|
2011-07-13 14:19:59 -05:00
|
|
|
if (line as uint < ivec::len(fm.lines) - 1u) {
|
2011-07-13 13:00:11 -05:00
|
|
|
end = fm.lines.(line + 1) - fm.start_pos;
|
2011-07-13 14:19:59 -05:00
|
|
|
} else {
|
|
|
|
// If we're not done parsing the file, we're at the limit of what's
|
|
|
|
// parsed. If we just slice the rest of the string, we'll print out
|
|
|
|
// the remainder of the file, which is undesirable.
|
|
|
|
end = str::byte_len(file);
|
|
|
|
auto rest = str::slice(file, begin, end);
|
|
|
|
auto newline = str::index(rest, '\n' as u8);
|
|
|
|
if (newline != -1) {
|
|
|
|
end = begin + (newline as uint);
|
|
|
|
}
|
2011-07-11 01:27:03 -05:00
|
|
|
}
|
2011-07-13 09:18:40 -05:00
|
|
|
ret str::slice(file, begin, end);
|
2011-07-11 01:27:03 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_filemap(codemap cm, str filename) -> filemap {
|
|
|
|
for (filemap fm in cm.files) {
|
|
|
|
if (fm.name == filename) {
|
|
|
|
ret fm;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//XXjdm the following triggers a mismatched type bug
|
|
|
|
// (or expected function, found _|_)
|
|
|
|
fail;// ("asking for " + filename + " which we don't know about");
|
|
|
|
}
|
2011-07-05 04:48:19 -05:00
|
|
|
|
|
|
|
//
|
|
|
|
// Local Variables:
|
|
|
|
// mode: rust
|
|
|
|
// fill-column: 78;
|
|
|
|
// indent-tabs-mode: nil
|
|
|
|
// c-basic-offset: 4
|
|
|
|
// buffer-file-coding-system: utf-8-unix
|
|
|
|
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
|
|
|
// End:
|
|
|
|
//
|