Skip to content

Commit

Permalink
Implement function diagnostic origin
Browse files Browse the repository at this point in the history
  • Loading branch information
edusporto committed Aug 14, 2024
1 parent f1d419d commit 4e155cc
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 44 deletions.
53 changes: 44 additions & 9 deletions src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::fun::{display::DisplayFn, Name};
use std::{
collections::BTreeMap,
fmt::{Display, Formatter},
ops::Range,
};

pub const ERR_INDENT_SIZE: usize = 2;
Expand Down Expand Up @@ -43,6 +44,8 @@ pub enum DiagnosticOrigin {
Book,
/// An error in a pattern-matching function definition rule.
Rule(Name),
/// An error when transforming `imp` syntax into `fun`.
Function(Name),
/// An error in a compiled inet.
Inet(String),
/// An error during readback of hvm-core run results.
Expand Down Expand Up @@ -83,13 +86,18 @@ impl Diagnostics {
self.add_diagnostic(err, Severity::Error, DiagnosticOrigin::Book, None);
}

pub fn add_rule_error(&mut self, err: impl std::fmt::Display, def_name: Name) {
pub fn add_rule_error(&mut self, err: impl std::fmt::Display, name: Name) {
self.err_counter += 1;
self.add_diagnostic(err, Severity::Error, DiagnosticOrigin::Rule(name.def_name_from_generated()), None);
}

pub fn add_function_error(&mut self, err: impl std::fmt::Display, name: Name, span: Option<FileSpan>) {
self.err_counter += 1;
self.add_diagnostic(
err,
Severity::Error,
DiagnosticOrigin::Rule(def_name.def_name_from_generated()),
None,
DiagnosticOrigin::Function(name.def_name_from_generated()),
span,
);
}

Expand Down Expand Up @@ -122,7 +130,7 @@ impl Diagnostics {

pub fn add_diagnostic(
&mut self,
msg: impl ToString,
msg: impl std::fmt::Display,
severity: Severity,
orig: DiagnosticOrigin,
range: Option<FileSpan>,
Expand Down Expand Up @@ -214,6 +222,12 @@ impl Diagnostics {
writeln!(f, "{:ERR_INDENT_SIZE$}{err}", "")?;
}
}
DiagnosticOrigin::Function(nam) => {
writeln!(f, "\x1b[1mIn function '\x1b[4m{}\x1b[0m\x1b[1m':\x1b[0m", nam)?;
for err in errs {
writeln!(f, "{:ERR_INDENT_SIZE$}{err}", "")?;
}
}
DiagnosticOrigin::Inet(nam) => {
writeln!(f, "\x1b[1mIn compiled inet '\x1b[4m{}\x1b[0m\x1b[1m':\x1b[0m", nam)?;
for err in errs {
Expand All @@ -236,6 +250,15 @@ impl Diagnostics {
Ok(())
})
}

pub fn display_only_messages(&self) -> impl std::fmt::Display + '_ {
DisplayFn(move |f| {
for err in self.diagnostics.values().flatten() {
writeln!(f, "{err}")?;
}
Ok(())
})
}
}

impl Display for Diagnostics {
Expand Down Expand Up @@ -263,11 +286,15 @@ impl From<String> for Diagnostics {
}

impl From<ParseError> for Diagnostics {
/// Transforms a parse error into `Diagnostics`.
///
/// NOTE: Since `ParseError` does not include the source code, we can't get the `TextLocation` of the error,
/// so it is not included in the diagnostic.
/// range is set as None.
fn from(value: ParseError) -> Self {
Self {
diagnostics: BTreeMap::from_iter([(
DiagnosticOrigin::Parsing,
// TODO: range is not because we're missing the origin file, can we fix this?
vec![Diagnostic { message: value.into(), severity: Severity::Error, span: None }],
)]),
..Default::default()
Expand Down Expand Up @@ -315,6 +342,8 @@ impl Default for DiagnosticsConfig {

impl Display for Diagnostic {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
// TODO: this could be problematic when printing multiple errors from the same file
// currently, there shouldn't be any `Diagnostics` with multiple problems `FileSpan`s
match &self.span {
Some(FileSpan { file: Some(file), .. }) => write!(f, "In {} :\n{}", file, self.message),
_ => write!(f, "{}", self.message),
Expand All @@ -332,6 +361,10 @@ impl Diagnostic {
writeln!(f, "\x1b[1mIn definition '\x1b[4m{}\x1b[0m\x1b[1m':\x1b[0m", nam)?;
writeln!(f, "{:ERR_INDENT_SIZE$}{self}", "")?;
}
DiagnosticOrigin::Function(nam) => {
writeln!(f, "\x1b[1mIn function '\x1b[4m{}\x1b[0m\x1b[1m':\x1b[0m", nam)?;
writeln!(f, "{:ERR_INDENT_SIZE$}{self}", "")?;
}
DiagnosticOrigin::Inet(nam) => {
writeln!(f, "\x1b[1mIn compiled inet '\x1b[4m{}\x1b[0m\x1b[1m':\x1b[0m", nam)?;
writeln!(f, "{:ERR_INDENT_SIZE$}{self}", "")?;
Expand All @@ -357,6 +390,7 @@ impl TextLocation {
TextLocation { line, char }
}

/// Transforms a `usize` byte index on `code` into a `TextLocation`.
pub fn from_byte_loc(code: &str, loc: usize) -> Self {
let code = code.as_bytes();
let mut line = 0;
Expand Down Expand Up @@ -387,9 +421,10 @@ impl TextSpan {
TextSpan { start, end }
}

pub fn from_byte_span(code: &str, span: (usize, usize)) -> Self {
/// Transforms a `usize` byte range on `code` into a `TextLocation`.
pub fn from_byte_span(code: &str, span: Range<usize>) -> Self {
// Will loop for way too long otherwise
assert!(span.0 <= span.1);
assert!(span.start <= span.end);

let code = code.as_bytes();
let mut start_line = 0;
Expand All @@ -398,7 +433,7 @@ impl TextSpan {
let mut end_char;

let mut curr_idx = 0;
while curr_idx < span.0 && curr_idx < code.len() {
while curr_idx < span.start && curr_idx < code.len() {
if code[curr_idx] == b'\n' {
start_line += 1;
start_char = 0;
Expand All @@ -410,7 +445,7 @@ impl TextSpan {

end_line = start_line;
end_char = start_char;
while curr_idx < span.1 && curr_idx < code.len() {
while curr_idx < span.end && curr_idx < code.len() {
if code[curr_idx] == b'\n' {
end_line += 1;
end_char = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/fun/load_book.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub fn do_parse_book(code: &str, origin: &Path, mut book: ParseBook) -> Result<P
book.source = Name::new(origin.to_string_lossy());
TermParser::new(code).parse_book(book, false).map_err(|err| {
let mut diagnostics = Diagnostics::default();
let span = TextSpan::from_byte_span(code, err.span);
let span = TextSpan::from_byte_span(code, err.span.0..err.span.1);
diagnostics.add_parsing_error(err, FileSpan { span, file: Some(origin.to_string_lossy().into()) });
diagnostics
})
Expand Down
16 changes: 8 additions & 8 deletions src/fun/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ impl<'a> TermParser<'a> {
self.index = rewind_index;
let (nam, ctrs) = self.parse_datatype()?;
let end_idx = *self.index();
let span = TextSpan::from_byte_span(self.input(), (ini_idx, end_idx));
let span = TextSpan::from_byte_span(self.input(), ini_idx..end_idx);
let source = if builtin { Source::Builtin } else { Source::Local(span) };
let adt = Adt { ctrs, source };
self.add_fun_type(&mut book, nam, adt, ini_idx..end_idx)?;
Expand Down Expand Up @@ -278,7 +278,7 @@ impl<'a> TermParser<'a> {
let body = p.parse_net()?;
*self.index() = ini_idx + *p.index();
let end_idx = *self.index();
let span = TextSpan::from_byte_span(self.input(), (ini_idx, end_idx));
let span = TextSpan::from_byte_span(self.input(), ini_idx..end_idx);
let source = if builtin { Source::Builtin } else { Source::Local(span) };
let def = HvmDefinition { name: name.clone(), body, source };
Ok(def)
Expand Down Expand Up @@ -653,7 +653,7 @@ impl<'a> TermParser<'a> {
if name == "def" {
// parse the nxt def term.
self.index = nxt_def;
let span = TextSpan::from_byte_span(self.input(), (nxt_def, *self.index()));
let span = TextSpan::from_byte_span(self.input(), nxt_def..*self.index());
let def = FunDefinition::new(name, rules, Source::Local(span));
return Ok(Term::Def { def, nxt: Box::new(self.parse_term()?) });
}
Expand All @@ -672,7 +672,7 @@ impl<'a> TermParser<'a> {
}
}
let nxt = self.parse_term()?;
let span = TextSpan::from_byte_span(self.input(), (nxt_term, *self.index()));
let span = TextSpan::from_byte_span(self.input(), nxt_term..*self.index());
let def = FunDefinition::new(cur_name, rules, Source::Local(span));
return Ok(Term::Def { def, nxt: Box::new(nxt) });
}
Expand Down Expand Up @@ -955,7 +955,7 @@ impl<'a> TermParser<'a> {
// Adding the first rule of a new definition
(None, _) => {
self.check_top_level_redefinition(name, book, span.clone())?;
let span = TextSpan::from_byte_span(self.input(), (span.start, span.end));
let span = TextSpan::from_byte_span(self.input(), span.start..span.end);
let source = if builtin { Source::Builtin } else { Source::Local(span) };
book.fun_defs.insert(name.clone(), FunDefinition::new(name.clone(), vec![rule], source));
}
Expand All @@ -971,7 +971,7 @@ impl<'a> TermParser<'a> {
builtin: bool,
) -> ParseResult<()> {
self.check_top_level_redefinition(&def.name, book, span.clone())?;
let span = TextSpan::from_byte_span(self.input(), (span.start, span.end));
let span = TextSpan::from_byte_span(self.input(), span.start..span.end);
let source = if builtin { Source::Builtin } else { Source::Local(span) };
def.source = source;
book.imp_defs.insert(def.name.clone(), def);
Expand All @@ -992,7 +992,7 @@ impl<'a> TermParser<'a> {
builtin: bool,
) -> ParseResult<()> {
self.check_type_redefinition(&enum_.name, book, span.clone())?;
let text_span = TextSpan::from_byte_span(self.input(), (span.start, span.end));
let text_span = TextSpan::from_byte_span(self.input(), span.start..span.end);
let source = if builtin { Source::Builtin } else { Source::Local(text_span) };
let mut adt = Adt { ctrs: Default::default(), source };
for variant in enum_.variants {
Expand Down Expand Up @@ -1042,7 +1042,7 @@ impl<'a> TermParser<'a> {
) -> ParseResult<()> {
self.check_type_redefinition(&obj.name, book, span.clone())?;
self.check_top_level_redefinition(&obj.name, book, span.clone())?;
let span = TextSpan::from_byte_span(self.input(), (span.start, span.end));
let span = TextSpan::from_byte_span(self.input(), span.start..span.end);
let source = if builtin { Source::Builtin } else { Source::Local(span) };
let mut adt = Adt { ctrs: Default::default(), source };
book.ctrs.insert(obj.name.clone(), obj.name.clone());
Expand Down
Loading

0 comments on commit 4e155cc

Please sign in to comment.