diff --git a/CHANGELOG.md b/CHANGELOG.md index 55d820753..d6e042bdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project does not currently adhere to a particular versioning scheme. - Change branches to support ending with ask statements. ([#629][gh-629]) - Improve hexadecimal and binary floating numbers. ([#648][gh-648]) - Change IO functions to return Result. ([#657][gh-657]) +- Revamp the diagnostics system and parser to show more error and warning messages ([#673][gh-673]) ## [0.2.36] - 2024-07-04 @@ -422,6 +423,7 @@ and this project does not currently adhere to a particular versioning scheme. [gh-648]: https://github.com/HigherOrderCO/Bend/issues/648 [gh-657]: https://github.com/HigherOrderCO/Bend/issues/657 [gh-659]: https://github.com/HigherOrderCO/Bend/pull/659 +[gh-673]: https://github.com/HigherOrderCO/Bend/pull/673 [gh-674]: https://github.com/HigherOrderCO/Bend/issues/674 [gh-675]: https://github.com/HigherOrderCO/Bend/issues/675 [Unreleased]: https://github.com/HigherOrderCO/Bend/compare/0.2.36...HEAD diff --git a/Cargo.lock b/Cargo.lock index 1a88c0e47..890183d65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "TSPL" -version = "0.0.12" +version = "0.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dfd6238b1461b99635b26585a85b4e7b9c786cc0481b3c540ae5f590b6dfb6" +checksum = "fe639519d49b56c98fd4fde7a5a7be01b5563862341a783b9bc2eb58f5120d8b" dependencies = [ "highlight_error", ] @@ -192,9 +192,9 @@ checksum = "809e18805660d7b6b2e2b9f316a5099521b5998d5cba4dda11b5157a21aaef03" [[package]] name = "hvm" -version = "2.0.19" +version = "2.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "281675413d2dd76cb1c53ec0d318bb672f5d70fb825929af6bcadd9b7f78c11b" +checksum = "3fafa02949c005e70869074ac9db489ad92eaf92c78b4dcf6c0b45d98982c08d" dependencies = [ "TSPL", "cc", diff --git a/Cargo.toml b/Cargo.toml index ddcbb601f..aa4bdce81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,10 +24,10 @@ default = ["cli"] cli = ["dep:clap"] [dependencies] -TSPL = "0.0.12" +TSPL = "0.0.13" clap = { version = "4.4.1", features = ["derive"], optional = true } highlight_error = "0.1.1" -hvm = "=2.0.19" +hvm = "=2.0.22" indexmap = "2.2.3" interner = "0.2.1" itertools = "0.11.0" diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 2b436e9c3..c8264ae37 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -1,7 +1,10 @@ -use crate::fun::{display::DisplayFn, Name}; +use TSPL::ParseError; + +use crate::fun::{display::DisplayFn, Name, Source}; use std::{ collections::BTreeMap, fmt::{Display, Formatter}, + ops::Range, }; pub const ERR_INDENT_SIZE: usize = 2; @@ -28,16 +31,19 @@ pub struct DiagnosticsConfig { #[derive(Debug, Clone)] pub struct Diagnostic { - message: String, - severity: Severity, + pub message: String, + pub severity: Severity, + pub source: Source, } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum DiagnosticOrigin { + /// An error when parsing source code. + Parsing, /// An error from the relationship between multiple top-level definitions. Book, - /// An error in a pattern-matching function definition rule. - Rule(Name), + /// An error in a function definition. + Function(Name), /// An error in a compiled inet. Inet(String), /// An error during readback of hvm-core run results. @@ -68,27 +74,48 @@ impl Diagnostics { Self { err_counter: 0, diagnostics: Default::default(), config } } + pub fn add_parsing_error(&mut self, err: impl std::fmt::Display, source: Source) { + self.err_counter += 1; + self.add_diagnostic(err, Severity::Error, DiagnosticOrigin::Parsing, source); + } + pub fn add_book_error(&mut self, err: impl std::fmt::Display) { self.err_counter += 1; - self.add_diagnostic(err, Severity::Error, DiagnosticOrigin::Book); + self.add_diagnostic(err, Severity::Error, DiagnosticOrigin::Book, Default::default()); } - pub fn add_rule_error(&mut self, err: impl std::fmt::Display, def_name: Name) { + pub fn add_function_error(&mut self, err: impl std::fmt::Display, name: Name, source: Source) { self.err_counter += 1; - self.add_diagnostic(err, Severity::Error, DiagnosticOrigin::Rule(def_name.def_name_from_generated())); + self.add_diagnostic( + err, + Severity::Error, + DiagnosticOrigin::Function(name.def_name_from_generated()), + source, + ); } pub fn add_inet_error(&mut self, err: impl std::fmt::Display, def_name: String) { self.err_counter += 1; - self.add_diagnostic(err, Severity::Error, DiagnosticOrigin::Inet(def_name)); + self.add_diagnostic(err, Severity::Error, DiagnosticOrigin::Inet(def_name), Default::default()); } - pub fn add_rule_warning(&mut self, warn: impl std::fmt::Display, warn_type: WarningType, def_name: Name) { + pub fn add_function_warning( + &mut self, + warn: impl std::fmt::Display, + warn_type: WarningType, + def_name: Name, + source: Source, + ) { let severity = self.config.warning_severity(warn_type); if severity == Severity::Error { self.err_counter += 1; } - self.add_diagnostic(warn, severity, DiagnosticOrigin::Rule(def_name.def_name_from_generated())); + self.add_diagnostic( + warn, + severity, + DiagnosticOrigin::Function(def_name.def_name_from_generated()), + source, + ); } pub fn add_book_warning(&mut self, warn: impl std::fmt::Display, warn_type: WarningType) { @@ -96,11 +123,17 @@ impl Diagnostics { if severity == Severity::Error { self.err_counter += 1; } - self.add_diagnostic(warn, severity, DiagnosticOrigin::Book); + self.add_diagnostic(warn, severity, DiagnosticOrigin::Book, Default::default()); } - pub fn add_diagnostic(&mut self, msg: impl ToString, severity: Severity, orig: DiagnosticOrigin) { - let diag = Diagnostic { message: msg.to_string(), severity }; + pub fn add_diagnostic( + &mut self, + msg: impl std::fmt::Display, + severity: Severity, + orig: DiagnosticOrigin, + source: Source, + ) { + let diag = Diagnostic { message: msg.to_string(), severity, source }; self.diagnostics.entry(orig).or_default().push(diag) } @@ -112,7 +145,7 @@ impl Diagnostics { match result { Ok(t) => Some(t), Err(e) => { - self.add_rule_error(e, def_name); + self.add_function_error(e, def_name, Default::default()); None } } @@ -159,47 +192,110 @@ impl Diagnostics { /// Returns a Display that prints the diagnostics with one of the given severities. pub fn display_with_severity(&self, severity: Severity) -> impl std::fmt::Display + '_ { DisplayFn(move |f| { - fn filter<'a>( - errs: impl IntoIterator, - severity: Severity, - ) -> impl Iterator { - errs.into_iter().filter(move |err| err.severity == severity) - } + // We want to print diagnostics information somewhat like this: + // ``` + // In file A : + // In definition X : + // {error} + // In definition Y : + // {error} + // + // In file B : + // In compiled Inet Z : + // {error} + // + // Other diagnostics: + // In {...} + // ``` + // The problem is, diagnostics data is currently structured as a mapping from something like + // DiagnosticOrigin to Vec<(DiagnosticMessage, DiagnosticFile)>, and we would need something + // like a mapping from DiagnosticFile to DiagnosticOrigin to Vec in order + // to print it cleanly. We might want to change it later to have this structure, + // but meanwhile, we do the transformations below to make the goal possible. + + // Ignore diagnostics without the desired severity. + let diagnostics = self + .diagnostics + .iter() + .map(|(origin, diags)| (origin, diags.iter().filter(|diag| diag.severity == severity))); - let mut has_msg = false; - for (orig, errs) in &self.diagnostics { - let mut errs = filter(errs, severity).peekable(); - if errs.peek().is_some() { - match orig { - DiagnosticOrigin::Book => { - for err in errs { - writeln!(f, "{err}")?; + // Produce the structure described above. + let groups: BTreeMap<&Option, BTreeMap<&DiagnosticOrigin, Vec<&Diagnostic>>> = diagnostics + .fold(BTreeMap::new(), |mut file_tree, (origin, diags)| { + for diag in diags { + // We need to allow this Clippy warning due to `Name` in `DiagnosticOrigin::Function`. + // We know how it works, so it shouldn't be a problem. + #[allow(clippy::mutable_key_type)] + let file_group_entry = file_tree.entry(&diag.source.file).or_default(); + let origin_group_entry = file_group_entry.entry(origin).or_default(); + origin_group_entry.push(diag); + } + file_tree + }); + // Now, we have a mapping from DiagnosticFile to DiagnosticOrigin to Vec. + + // If the last file is `None`, it means we only have diagnostics with unknown source file. + // In this case, we won't print a special message for them. + let only_unknown_file_diagnostics = groups.keys().next_back() == Some(&&None); + + // Reverse the group iterator so `None` files go last. + for (file, origin_to_diagnostics) in groups.iter().rev() { + if !only_unknown_file_diagnostics { + match &file { + Some(name) => writeln!(f, "\x1b[1mIn \x1b[4m{}\x1b[0m\x1b[1m :\x1b[0m", name)?, + None => writeln!(f, "\x1b[1mOther diagnostics:\x1b[0m")?, + }; + } + + let mut has_msg = false; + for (origin, diagnostics) in origin_to_diagnostics { + let mut diagnostics = diagnostics.iter().peekable(); + if diagnostics.peek().is_some() { + match origin { + DiagnosticOrigin::Parsing => { + for err in diagnostics { + writeln!(f, "{err}")?; + } } - } - DiagnosticOrigin::Rule(nam) => { - writeln!(f, "\x1b[1mIn definition '\x1b[4m{}\x1b[0m\x1b[1m':\x1b[0m", nam)?; - for err in errs { - writeln!(f, "{:ERR_INDENT_SIZE$}{err}", "")?; + DiagnosticOrigin::Book => { + for err in diagnostics { + writeln!(f, "{err}")?; + } } - } - DiagnosticOrigin::Inet(nam) => { - writeln!(f, "\x1b[1mIn compiled inet '\x1b[4m{}\x1b[0m\x1b[1m':\x1b[0m", nam)?; - for err in errs { - writeln!(f, "{:ERR_INDENT_SIZE$}{err}", "")?; + DiagnosticOrigin::Function(nam) => { + writeln!(f, "\x1b[1mIn definition '\x1b[4m{}\x1b[0m\x1b[1m':\x1b[0m", nam)?; + for err in diagnostics { + writeln!(f, "{:ERR_INDENT_SIZE$}{err}", "")?; + } } - } - DiagnosticOrigin::Readback => { - writeln!(f, "\x1b[1mDuring readback:\x1b[0m")?; - 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 diagnostics { + writeln!(f, "{:ERR_INDENT_SIZE$}{err}", "")?; + } + } + DiagnosticOrigin::Readback => { + writeln!(f, "\x1b[1mDuring readback:\x1b[0m")?; + for err in diagnostics { + writeln!(f, "{:ERR_INDENT_SIZE$}{err}", "")?; + } } } + has_msg = true; } - has_msg = true; + } + if has_msg { + writeln!(f)?; } } - if has_msg { - writeln!(f)?; + 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(()) }) @@ -223,7 +319,24 @@ impl From for Diagnostics { Self { diagnostics: BTreeMap::from_iter([( DiagnosticOrigin::Book, - vec![Diagnostic { message: value, severity: Severity::Error }], + vec![Diagnostic { message: value, severity: Severity::Error, source: Default::default() }], + )]), + ..Default::default() + } + } +} + +impl From 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, + vec![Diagnostic { message: value.into(), severity: Severity::Error, source: Default::default() }], )]), ..Default::default() } @@ -273,3 +386,107 @@ impl Display for Diagnostic { write!(f, "{}", self.message) } } + +impl Diagnostic { + pub fn display_with_origin<'a>(&'a self, origin: &'a DiagnosticOrigin) -> impl std::fmt::Display + '_ { + DisplayFn(move |f| { + match origin { + DiagnosticOrigin::Parsing => writeln!(f, "{self}")?, + DiagnosticOrigin::Book => writeln!(f, "{self}")?, + DiagnosticOrigin::Function(nam) => { + writeln!(f, "\x1b[1mIn definition '\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}", "")?; + } + DiagnosticOrigin::Readback => { + writeln!(f, "\x1b[1mDuring readback:\x1b[0m")?; + writeln!(f, "{:ERR_INDENT_SIZE$}{self}", "")?; + } + }; + Ok(()) + }) + } +} + +#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)] +pub struct TextLocation { + pub line: usize, + pub char: usize, +} + +impl TextLocation { + pub fn new(line: usize, char: usize) -> Self { + 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; + let mut char = 0; + let mut cur_idx = 0; + while cur_idx < loc && cur_idx < code.len() { + if code[cur_idx] == b'\n' { + line += 1; + char = 0; + } else { + char += 1; + } + cur_idx += 1; + } + + TextLocation { line, char } + } +} + +#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)] +pub struct TextSpan { + pub start: TextLocation, + pub end: TextLocation, +} + +impl TextSpan { + pub fn new(start: TextLocation, end: TextLocation) -> Self { + TextSpan { start, end } + } + + /// Transforms a `usize` byte range on `code` into a `TextLocation`. + pub fn from_byte_span(code: &str, span: Range) -> Self { + // Will loop for way too long otherwise + assert!(span.start <= span.end); + + let code = code.as_bytes(); + let mut start_line = 0; + let mut start_char = 0; + let mut end_line; + let mut end_char; + + let mut cur_idx = 0; + while cur_idx < span.start && cur_idx < code.len() { + if code[cur_idx] == b'\n' { + start_line += 1; + start_char = 0; + } else { + start_char += 1; + } + cur_idx += 1; + } + + end_line = start_line; + end_char = start_char; + while cur_idx < span.end && cur_idx < code.len() { + if code[cur_idx] == b'\n' { + end_line += 1; + end_char = 0; + } else { + end_char += 1; + } + cur_idx += 1; + } + + TextSpan::new(TextLocation::new(start_line, start_char), TextLocation::new(end_line, end_char)) + } +} diff --git a/src/fun/check/unbound_refs.rs b/src/fun/check/unbound_refs.rs index 837ace1c8..1aa4cd5e5 100644 --- a/src/fun/check/unbound_refs.rs +++ b/src/fun/check/unbound_refs.rs @@ -14,7 +14,11 @@ impl Ctx<'_> { rule.body.check_unbound_refs(self.book, &mut unbounds); } for unbound in unbounds { - self.info.add_rule_error(format!("Reference to undefined function '{unbound}'"), def.name.clone()); + self.info.add_function_error( + format!("Reference to undefined function '{unbound}'"), + def.name.clone(), + def.source.clone(), + ); } } self.info.fatal(()) diff --git a/src/fun/check/unbound_vars.rs b/src/fun/check/unbound_vars.rs index cae0620d5..61b6a36e1 100644 --- a/src/fun/check/unbound_vars.rs +++ b/src/fun/check/unbound_vars.rs @@ -28,7 +28,7 @@ impl Ctx<'_> { } for err in errs { - self.info.add_rule_error(err, def_name.clone()); + self.info.add_function_error(err, def_name.clone(), def.source.clone()); } } diff --git a/src/fun/load_book.rs b/src/fun/load_book.rs index db1bd1211..a88c45097 100644 --- a/src/fun/load_book.rs +++ b/src/fun/load_book.rs @@ -1,9 +1,9 @@ use super::{ parser::{ParseBook, TermParser}, - Book, Name, + Book, Name, Source, SourceKind, }; use crate::{ - diagnostics::{Diagnostics, DiagnosticsConfig}, + diagnostics::{Diagnostics, DiagnosticsConfig, TextSpan}, imports::PackageLoader, }; use std::path::Path; @@ -39,11 +39,18 @@ pub fn load_to_book( book.load_imports(package_loader, diag) } -pub fn do_parse_book(code: &str, origin: &Path, mut book: ParseBook) -> Result { +pub fn do_parse_book(code: &str, origin: &Path, mut book: ParseBook) -> Result { book.source = Name::new(origin.to_string_lossy()); - TermParser::new(code).parse_book(book, false).map_err(|e| format!("In {} :\n{}", origin.display(), e)) + TermParser::new(code).parse_book(book, false).map_err(|err| { + let mut diagnostics = Diagnostics::default(); + let span = TextSpan::from_byte_span(code, err.span.0..err.span.1); + let source = + Source { file: Some(origin.to_string_lossy().into()), span: Some(span), kind: SourceKind::User }; + diagnostics.add_parsing_error(err, source); + diagnostics + }) } -pub fn do_parse_book_default(code: &str, origin: &Path) -> Result { +pub fn do_parse_book_default(code: &str, origin: &Path) -> Result { do_parse_book(code, origin, ParseBook::builtins())?.to_fun() } diff --git a/src/fun/mod.rs b/src/fun/mod.rs index 5d7c687e6..b88b5c246 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -1,16 +1,12 @@ use crate::{ - diagnostics::{Diagnostics, DiagnosticsConfig}, + diagnostics::{Diagnostics, DiagnosticsConfig, TextSpan}, imports::Import, maybe_grow, multi_iterator, ENTRY_POINT, }; use indexmap::{IndexMap, IndexSet}; use interner::global::{GlobalPool, GlobalString}; use itertools::Itertools; -use std::{ - borrow::Cow, - hash::Hash, - ops::{Deref, Range}, -}; +use std::{borrow::Cow, hash::Hash, ops::Deref}; pub mod builtins; pub mod check; @@ -73,14 +69,24 @@ pub struct Definition { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum Source { +pub struct Source { + pub file: Option, + pub span: Option, + pub kind: SourceKind, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum SourceKind { + /// Built into the language. Builtin, /// Was generated by the compiler. Generated, - /// Source code location from the current book. - Local(Range), - /// Imported from another package. + /// Source code from a local book. + User, + /// Source code from an imported book. Imported, + /// Unknown by the compiler at this stage. + Unknown, } /// An HVM native definition. @@ -1025,8 +1031,9 @@ impl Definition { Self { name, rules, source } } - pub fn new_gen(name: Name, rules: Vec, builtin: bool) -> Self { - let source = if builtin { Source::Builtin } else { Source::Generated }; + pub fn new_gen(name: Name, rules: Vec, source: Source) -> Self { + let kind = if source.is_builtin() { SourceKind::Builtin } else { SourceKind::Generated }; + let source = Source { kind, ..source }; Self { name, rules, source } } @@ -1121,11 +1128,17 @@ impl Book { impl Source { pub fn is_builtin(&self) -> bool { - matches!(self, Source::Builtin) + matches!(self.kind, SourceKind::Builtin) } pub fn is_local(&self) -> bool { - matches!(self, Source::Local(..)) + matches!(self.kind, SourceKind::User) + } +} + +impl Default for Source { + fn default() -> Self { + Self { file: None, span: None, kind: SourceKind::Unknown } } } diff --git a/src/fun/net_to_term.rs b/src/fun/net_to_term.rs index 290e689ea..764b4d333 100644 --- a/src/fun/net_to_term.rs +++ b/src/fun/net_to_term.rs @@ -550,7 +550,12 @@ impl Reader<'_> { for (err, count) in err_counts { let count_msg = if count > 1 { format!(" ({count} occurrences)") } else { "".to_string() }; let msg = format!("{}{}", err, count_msg); - diagnostics.add_diagnostic(msg.as_str(), Severity::Warning, DiagnosticOrigin::Readback); + diagnostics.add_diagnostic( + msg.as_str(), + Severity::Warning, + DiagnosticOrigin::Readback, + Default::default(), + ); } } diff --git a/src/fun/parser.rs b/src/fun/parser.rs index 97c9e8267..fadea7430 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -1,6 +1,7 @@ use std::ops::Range; use crate::{ + diagnostics::{TextLocation, TextSpan}, fun::{ display::DisplayFn, Adt, Adts, Constructors, CtrField, FanKind, HvmDefinition, HvmDefinitions, MatchRule, Name, Num, Op, Pattern, Rule, Source, Tag, Term, STRINGS, @@ -12,7 +13,9 @@ use crate::{ use highlight_error::highlight_error; use indexmap::IndexMap; use itertools::Itertools; -use TSPL::Parser; +use TSPL::{ParseError, Parser}; + +use super::SourceKind; type FunDefinition = super::Definition; type ImpDefinition = crate::imp::Definition; @@ -97,7 +100,7 @@ impl ParseBook { // ::= ([0-9]+ | "0x"[0-9a-fA-F]+ | "0b"[01]+) // ::= ( "+" | "-" | "*" | "/" | "%" | "==" | "!=" | "<<" | ">>" | "<" | ">" | "&" | "|" | "^" | "**" ) -pub type ParseResult = std::result::Result; +pub type ParseResult = std::result::Result; pub struct TermParser<'i> { input: &'i str, @@ -164,7 +167,12 @@ impl<'a> TermParser<'a> { self.index = rewind_index; let (nam, ctrs) = self.parse_datatype()?; let end_idx = *self.index(); - let source = if builtin { Source::Builtin } else { Source::Local(ini_idx..end_idx) }; + + let span = Some(TextSpan::from_byte_span(self.input(), ini_idx..end_idx)); + let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; + let file = Some(book.source.to_string()); + let source = Source { file, span, kind }; + let adt = Adt { ctrs, source }; self.add_fun_type(&mut book, nam, adt, ini_idx..end_idx)?; indent = self.advance_newlines()?; @@ -252,7 +260,8 @@ impl<'a> TermParser<'a> { let fields = self.list_like(parse_field, "", ")", "", false, 0)?; if let Some(field) = fields.find_repeated_names().into_iter().next() { - return Err(format!("Found a repeated field '{field}' in constructor {ctr_name}.")); + let msg = format!("Found a repeated field '{field}' in constructor {ctr_name}."); + return self.expected_message(&msg)?; } Ok((ctr_name, fields)) } else { @@ -275,12 +284,17 @@ impl<'a> TermParser<'a> { let body = p.parse_net()?; *self.index() = ini_idx + *p.index(); let end_idx = *self.index(); - let source = if builtin { Source::Builtin } else { Source::Local(ini_idx..end_idx) }; + + let span = Some(TextSpan::from_byte_span(self.input(), ini_idx..end_idx)); + let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; + let file = None; // should we pass the book's source here? + let source = Source { file, span, kind }; + let def = HvmDefinition { name: name.clone(), body, source }; Ok(def) } - fn parse_from_import(&mut self) -> Result { + fn parse_from_import(&mut self) -> ParseResult { // from path import package // from path import (a, b) // from path import * @@ -302,7 +316,7 @@ impl<'a> TermParser<'a> { Ok(Import::new(path, ImportType::Single(import, alias), relative)) } - fn parse_import(&mut self) -> Result, String> { + fn parse_import(&mut self) -> ParseResult> { // import path // import (path/a, path/b) @@ -653,7 +667,9 @@ impl<'a> TermParser<'a> { if name == "def" { // parse the nxt def term. self.index = nxt_def; - let def = FunDefinition::new(name, rules, Source::Local(nxt_def..*self.index())); + let span = Some(TextSpan::from_byte_span(self.input(), nxt_def..*self.index())); + let source = Source { span, file: None, kind: SourceKind::User }; + let def = FunDefinition::new(name, rules, source); return Ok(Term::Def { def, nxt: Box::new(self.parse_term()?) }); } if name == cur_name { @@ -671,7 +687,8 @@ impl<'a> TermParser<'a> { } } let nxt = self.parse_term()?; - let def = FunDefinition::new(cur_name, rules, Source::Local(nxt_term..*self.index())); + let span = Some(TextSpan::from_byte_span(self.input(), nxt_term..*self.index())); + let def = FunDefinition::new(cur_name, rules, Source { span, file: None, kind: SourceKind::User }); return Ok(Term::Def { def, nxt: Box::new(nxt) }); } @@ -848,7 +865,7 @@ impl<'a> TermParser<'a> { /// Parses a tag where it may or may not be valid. /// /// If it is not valid, the returned callback can be used to issue an error. - fn parse_tag(&mut self) -> ParseResult<(Option, impl FnOnce(&mut Self) -> Result<(), String>)> { + fn parse_tag(&mut self) -> ParseResult<(Option, impl FnOnce(&mut Self) -> Result<(), ParseError>)> { let index = self.index; self.skip_trivia(); let tag = if self.peek_one() == Some('#') @@ -936,8 +953,8 @@ impl<'a> TermParser<'a> { // Continuing with a new rule to the current definition (Some(def), Some(last_rule)) if last_rule == name => { def.rules.push(rule); - if let Source::Local(s) = &mut def.source { - s.end = span.end; + if let Some(s) = &mut def.source.span { + s.end = TextLocation::from_byte_loc(self.input(), span.end); } } // Trying to add a new rule to a previous definition, coming from a different rule. @@ -953,7 +970,10 @@ impl<'a> TermParser<'a> { // Adding the first rule of a new definition (None, _) => { self.check_top_level_redefinition(name, book, span.clone())?; - let source = if builtin { Source::Builtin } else { Source::Local(span) }; + let span = Some(TextSpan::from_byte_span(self.input(), span.start..span.end)); + let file = Some(book.source.to_string()); + let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; + let source = Source { file, span, kind }; book.fun_defs.insert(name.clone(), FunDefinition::new(name.clone(), vec![rule], source)); } } @@ -968,7 +988,10 @@ impl<'a> TermParser<'a> { builtin: bool, ) -> ParseResult<()> { self.check_top_level_redefinition(&def.name, book, span.clone())?; - let source = if builtin { Source::Builtin } else { Source::Local(span) }; + let span = Some(TextSpan::from_byte_span(self.input(), span.start..span.end)); + let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; + let file = Some(book.source.to_string()); + let source = Source { file, span, kind }; def.source = source; book.imp_defs.insert(def.name.clone(), def); Ok(()) @@ -984,14 +1007,19 @@ impl<'a> TermParser<'a> { &mut self, enum_: Enum, book: &mut ParseBook, - span: Range, + range: Range, builtin: bool, ) -> ParseResult<()> { - self.check_type_redefinition(&enum_.name, book, span.clone())?; - let source = if builtin { Source::Builtin } else { Source::Local(span.clone()) }; + self.check_type_redefinition(&enum_.name, book, range.clone())?; + + let span = Some(TextSpan::from_byte_span(self.input(), range.start..range.end)); + let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; + let file = Some(book.source.to_string()); + let source = Source { file, span, kind }; + let mut adt = Adt { ctrs: Default::default(), source }; for variant in enum_.variants { - self.check_top_level_redefinition(&enum_.name, book, span.clone())?; + self.check_top_level_redefinition(&enum_.name, book, range.clone())?; book.ctrs.insert(variant.name.clone(), enum_.name.clone()); adt.ctrs.insert(variant.name, variant.fields); } @@ -1012,7 +1040,8 @@ impl<'a> TermParser<'a> { } else { for ctr in adt.ctrs.keys() { if let Some(builtin) = book.contains_builtin_def(ctr) { - return Err(TermParser::redefinition_of_function_msg(builtin, ctr)); + let msg = TermParser::redefinition_of_function_msg(builtin, ctr); + return self.expected_and("function", &msg); } match book.ctrs.entry(ctr.clone()) { indexmap::map::Entry::Vacant(e) => _ = e.insert(nam.clone()), @@ -1036,7 +1065,12 @@ 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 source = if builtin { Source::Builtin } else { Source::Local(span) }; + + let span = Some(TextSpan::from_byte_span(self.input(), span.start..span.end)); + let kind = if builtin { SourceKind::Builtin } else { SourceKind::User }; + let file = Some(book.source.to_string()); + let source = Source { file, span, kind }; + let mut adt = Adt { ctrs: Default::default(), source }; book.ctrs.insert(obj.name.clone(), obj.name.clone()); adt.ctrs.insert(obj.name.clone(), obj.fields); @@ -1097,6 +1131,15 @@ impl<'a> Parser<'a> for TermParser<'a> { self.expected_spanned(exp, ini_idx..end_idx) } + /// Generates an error message with an additional custom message. + /// + /// Override to have our own error message. + fn expected_and(&mut self, exp: &str, msg: &str) -> ParseResult { + let ini_idx = *self.index(); + let end_idx = *self.index() + 1; + self.expected_spanned_and(exp, msg, ini_idx..end_idx) + } + /// Consumes an instance of the given string, erroring if it is not found. /// /// Override to have our own error message. @@ -1184,6 +1227,17 @@ impl Indent { impl<'a> ParserCommons<'a> for TermParser<'a> {} pub trait ParserCommons<'a>: Parser<'a> { + /// Generates an error message that does not print expected terms. + fn expected_message(&mut self, msg: &str) -> ParseResult { + let ini_idx = *self.index(); + let end_idx = *self.index() + 1; + + let is_eof = self.is_eof(); + let detected = DisplayFn(|f| if is_eof { write!(f, " end of input") } else { Ok(()) }); + let msg = format!("\x1b[1m- information:\x1b[0m {}\n\x1b[1m- location:\x1b[0m{}", msg, detected); + self.with_ctx(Err(msg), ini_idx..end_idx) + } + fn labelled(&mut self, parser: impl Fn(&mut Self) -> ParseResult, label: &str) -> ParseResult { match parser(self) { Ok(val) => Ok(val), @@ -1230,7 +1284,7 @@ pub trait ParserCommons<'a>: Parser<'a> { } } - fn parse_import_name(&mut self, label: &str) -> Result<(Name, Option, bool), String> { + fn parse_import_name(&mut self, label: &str) -> ParseResult<(Name, Option, bool)> { let (import, alias) = self.parse_name_maybe_alias(label)?; let relative = import.starts_with("./") | import.starts_with("../"); Ok((import, alias, relative)) @@ -1335,10 +1389,21 @@ pub trait ParserCommons<'a>: Parser<'a> { self.with_ctx(Err(msg), span) } + fn expected_spanned_and(&mut self, exp: &str, msg: &str, span: Range) -> ParseResult { + let is_eof = self.is_eof(); + let detected = DisplayFn(|f| if is_eof { write!(f, " end of input") } else { Ok(()) }); + let msg = format!( + "\x1b[1m- information:\x1b[0m {}\n\x1b[1m- expected:\x1b[0m {}\n\x1b[1m- detected:\x1b[0m{}", + msg, exp, detected, + ); + self.with_ctx(Err(msg), span) + } + fn with_ctx(&mut self, res: Result, span: Range) -> ParseResult { res.map_err(|msg| { let ctx = highlight_error(span.start, span.end, self.input()); - format!("{msg}\n{ctx}") + let msg = format!("{msg}\n{ctx}"); + ParseError::new((span.start, span.end), msg) }) } @@ -1537,7 +1602,8 @@ pub trait ParserCommons<'a>: Parser<'a> { if next_is_hex || num_str.is_empty() { self.expected(format!("valid {radix} digit").as_str()) } else { - u32::from_str_radix(&num_str, radix as u32).map_err(|e| e.to_string()) + u32::from_str_radix(&num_str, radix as u32) + .map_err(|e| self.expected_and::("integer", &e.to_string()).unwrap_err()) } } @@ -1548,7 +1614,8 @@ pub trait ParserCommons<'a>: Parser<'a> { if next_is_hex || num_str.is_empty() { self.expected(format!("valid {radix} digit").as_str()) } else { - u32::from_str_radix(&num_str, radix as u32).map_err(|e| e.to_string()) + u32::from_str_radix(&num_str, radix as u32) + .map_err(|e| self.expected_and::("integer", &e.to_string()).unwrap_err()) } } @@ -1577,7 +1644,8 @@ pub trait ParserCommons<'a>: Parser<'a> { self.advance_one(); let fra_str = self.take_while(|c| c.is_digit(radix as u32) || c == '_'); let fra_str = fra_str.chars().filter(|c| *c != '_').collect::(); - let fra = u32::from_str_radix(&fra_str, radix as u32).map_err(|e| e.to_string())?; + let fra = u32::from_str_radix(&fra_str, radix as u32) + .map_err(|e| self.expected_and::("integer", &e.to_string()).unwrap_err())?; let fra = fra as f32 / (radix.to_f32()).powi(fra_str.len() as i32); Some(fra) } else { diff --git a/src/fun/transform/apply_args.rs b/src/fun/transform/apply_args.rs index 23201a2e1..f1bf8b9a3 100644 --- a/src/fun/transform/apply_args.rs +++ b/src/fun/transform/apply_args.rs @@ -23,9 +23,10 @@ impl Ctx<'_> { // Since we fatal error, no need to exit early let n_rules = main_def.rules.len(); if n_rules != 1 { - self.info.add_rule_error( + self.info.add_function_error( format!("Expected the entrypoint function to have only one rule, found {n_rules}."), entrypoint.clone(), + main_def.source.clone(), ); } @@ -35,9 +36,10 @@ impl Ctx<'_> { if let Pattern::Var(var) = pat { main_body = Term::lam(Pattern::Var(var.clone()), main_body); } else { - self.info.add_rule_error( + self.info.add_function_error( format!("Expected the entrypoint function to only have variable patterns, found '{pat}'."), entrypoint.clone(), + main_def.source.clone(), ); } } diff --git a/src/fun/transform/definition_merge.rs b/src/fun/transform/definition_merge.rs index a58befbea..54e9b8ef5 100644 --- a/src/fun/transform/definition_merge.rs +++ b/src/fun/transform/definition_merge.rs @@ -12,7 +12,7 @@ impl Book { /// Merges definitions that have the same structure into one definition. /// Expects variables to be linear. /// - /// Ignores origin of the rules when merging, + /// Some of the origins of the rules will be lost in this stage, /// Should not be preceded by passes that cares about the origins. pub fn merge_definitions(&mut self) { let defs: Vec<_> = self.defs.keys().cloned().collect(); @@ -34,13 +34,18 @@ impl Book { // def1_$_def2_$_def3 let new_name = Name::new(equal_defs.iter().join(MERGE_SEPARATOR)); - // Builtin origin takes precedence - let builtin = equal_defs.iter().any(|nam| self.defs[nam].is_builtin()); - if equal_defs.len() > 1 { // Merging some defs + + // The source of the generated definition will be based on the first one we get from `equal_defs`. + // In the future, we might want to change this to point to every source of every definition + // it's based on. + // This could be done by having SourceKind::Generated contain a Vec or Vec. + let any_def_name = equal_defs.iter().next().unwrap(); // we know we can unwrap since equal_defs.len() > 1 + let source = self.defs[any_def_name].source.clone(); + // Add the merged def - let new_def = Definition::new_gen(new_name.clone(), vec![Rule { pats: vec![], body: term }], builtin); + let new_def = Definition::new_gen(new_name.clone(), vec![Rule { pats: vec![], body: term }], source); self.defs.insert(new_name.clone(), new_def); // Remove the old ones and write the map of old names to new ones. for name in equal_defs { diff --git a/src/fun/transform/definition_pruning.rs b/src/fun/transform/definition_pruning.rs index 11a575802..8f7b9b586 100644 --- a/src/fun/transform/definition_pruning.rs +++ b/src/fun/transform/definition_pruning.rs @@ -1,6 +1,6 @@ use crate::{ diagnostics::WarningType, - fun::{Book, Ctx, Name, Source, Term}, + fun::{Book, Ctx, Name, SourceKind, Term}, maybe_grow, }; use hvm::ast::{Net, Tree}; @@ -77,8 +77,13 @@ impl Ctx<'_> { // Prune if `prune_all`, otherwise show a warning. if prune_all { rm_def(self.book, &def); - } else if !def.is_generated() && !matches!(src, Source::Generated) { - self.info.add_rule_warning("Definition is unused.", WarningType::UnusedDefinition, def); + } else if !def.is_generated() && !matches!(src.kind, SourceKind::Generated) { + self.info.add_function_warning( + "Definition is unused.", + WarningType::UnusedDefinition, + def, + src, + ); } } Used::Ctr => { diff --git a/src/fun/transform/desugar_bend.rs b/src/fun/transform/desugar_bend.rs index fd9d9c723..3d0753c1f 100644 --- a/src/fun/transform/desugar_bend.rs +++ b/src/fun/transform/desugar_bend.rs @@ -16,7 +16,7 @@ impl Ctx<'_> { let mut fresh = 0; for rule in def.rules.iter_mut() { if let Err(err) = rule.body.desugar_bend(&def.name, &mut fresh, &mut new_defs, &def.source) { - self.info.add_rule_error(err, def.name.clone()); + self.info.add_function_error(err, def.name.clone(), def.source.clone()); break; } } diff --git a/src/fun/transform/desugar_fold.rs b/src/fun/transform/desugar_fold.rs index 0ab970835..492cc9ed1 100644 --- a/src/fun/transform/desugar_fold.rs +++ b/src/fun/transform/desugar_fold.rs @@ -43,7 +43,7 @@ impl Ctx<'_> { &def.source, ); if let Err(e) = res { - self.info.add_rule_error(e, def.name.clone()); + self.info.add_function_error(e, def.name.clone(), def.source.clone()); } } } diff --git a/src/fun/transform/desugar_match_defs.rs b/src/fun/transform/desugar_match_defs.rs index 72e147bbc..da7939d0b 100644 --- a/src/fun/transform/desugar_match_defs.rs +++ b/src/fun/transform/desugar_match_defs.rs @@ -23,10 +23,15 @@ impl Ctx<'_> { match err { DesugarMatchDefErr::AdtNotExhaustive { .. } | DesugarMatchDefErr::NumMissingDefault - | DesugarMatchDefErr::TypeMismatch { .. } => self.info.add_rule_error(err, def_name.clone()), - DesugarMatchDefErr::RepeatedBind { .. } => { - self.info.add_rule_warning(err, WarningType::RepeatedBind, def_name.clone()) + | DesugarMatchDefErr::TypeMismatch { .. } => { + self.info.add_function_error(err, def_name.clone(), def.source.clone()) } + DesugarMatchDefErr::RepeatedBind { .. } => self.info.add_function_warning( + err, + WarningType::RepeatedBind, + def_name.clone(), + def.source.clone(), + ), } } } diff --git a/src/fun/transform/desugar_open.rs b/src/fun/transform/desugar_open.rs index 016f1f739..0c662f162 100644 --- a/src/fun/transform/desugar_open.rs +++ b/src/fun/transform/desugar_open.rs @@ -11,7 +11,7 @@ impl Ctx<'_> { for def in self.book.defs.values_mut() { for rule in def.rules.iter_mut() { if let Err(err) = rule.body.desugar_open(&self.book.adts) { - self.info.add_rule_error(err, def.name.clone()); + self.info.add_function_error(err, def.name.clone(), def.source.clone()); } } } diff --git a/src/fun/transform/desugar_with_blocks.rs b/src/fun/transform/desugar_with_blocks.rs index 14df5781e..e163781f5 100644 --- a/src/fun/transform/desugar_with_blocks.rs +++ b/src/fun/transform/desugar_with_blocks.rs @@ -15,7 +15,7 @@ impl Ctx<'_> { for def in self.book.defs.values_mut() { for rule in def.rules.iter_mut() { if let Err(e) = rule.body.desugar_with_blocks(None, &def_names) { - self.info.add_rule_error(e, def.name.clone()); + self.info.add_function_error(e, def.name.clone(), def.source.clone()); } } } diff --git a/src/fun/transform/encode_adts.rs b/src/fun/transform/encode_adts.rs index d937c0c16..9ef6c62cd 100644 --- a/src/fun/transform/encode_adts.rs +++ b/src/fun/transform/encode_adts.rs @@ -1,5 +1,5 @@ use crate::{ - fun::{Book, Definition, Name, Num, Pattern, Rule, Term}, + fun::{Book, Definition, Name, Num, Pattern, Rule, Source, Term}, AdtEncoding, }; @@ -18,7 +18,7 @@ impl Book { AdtEncoding::NumScott => { let tag = make_tag(adt_name == ctr_name, ctr_name); let body = encode_ctr_num_scott(fields.iter().map(|f| &f.nam), &tag); - let tag_def = make_tag_def(ctr_idx, &tag, adt.source.is_builtin()); + let tag_def = make_tag_def(ctr_idx, &tag, adt.source.clone()); tags.push((tag, tag_def)); body } @@ -65,7 +65,7 @@ fn encode_ctr_num_scott<'a>(ctr_args: impl DoubleEndedIterator Term::rfold_lams(term, ctr_args.cloned().map(Some)) } -fn make_tag_def(ctr_idx: usize, tag: &Name, builtin: bool) -> Definition { +fn make_tag_def(ctr_idx: usize, tag: &Name, source: Source) -> Definition { let tag_rule = vec![Rule { pats: vec![], body: Term::Num { val: Num::U24(ctr_idx as u32) } }]; - Definition::new_gen(tag.clone(), tag_rule, builtin) + Definition::new_gen(tag.clone(), tag_rule, source) } diff --git a/src/fun/transform/fix_match_defs.rs b/src/fun/transform/fix_match_defs.rs index 299fef471..c7a6e1b1f 100644 --- a/src/fun/transform/fix_match_defs.rs +++ b/src/fun/transform/fix_match_defs.rs @@ -19,7 +19,7 @@ impl Ctx<'_> { } for err in errs { - self.info.add_rule_error(err, def.name.clone()); + self.info.add_function_error(err, def.name.clone(), def.source.clone()); } } diff --git a/src/fun/transform/fix_match_terms.rs b/src/fun/transform/fix_match_terms.rs index 149741198..754cc88f8 100644 --- a/src/fun/transform/fix_match_terms.rs +++ b/src/fun/transform/fix_match_terms.rs @@ -51,17 +51,26 @@ impl Ctx<'_> { for err in errs { match err { FixMatchErr::AdtMismatch { .. } | FixMatchErr::NonExhaustiveMatch { .. } => { - self.info.add_rule_error(err, def.name.clone()) - } - FixMatchErr::IrrefutableMatch { .. } => { - self.info.add_rule_warning(err, WarningType::IrrefutableMatch, def.name.clone()) - } - FixMatchErr::UnreachableMatchArms { .. } => { - self.info.add_rule_warning(err, WarningType::UnreachableMatch, def.name.clone()) - } - FixMatchErr::RedundantArm { .. } => { - self.info.add_rule_warning(err, WarningType::RedundantMatch, def.name.clone()) + self.info.add_function_error(err, def.name.clone(), def.source.clone()) } + FixMatchErr::IrrefutableMatch { .. } => self.info.add_function_warning( + err, + WarningType::IrrefutableMatch, + def.name.clone(), + def.source.clone(), + ), + FixMatchErr::UnreachableMatchArms { .. } => self.info.add_function_warning( + err, + WarningType::UnreachableMatch, + def.name.clone(), + def.source.clone(), + ), + FixMatchErr::RedundantArm { .. } => self.info.add_function_warning( + err, + WarningType::RedundantMatch, + def.name.clone(), + def.source.clone(), + ), } } } diff --git a/src/fun/transform/float_combinators.rs b/src/fun/transform/float_combinators.rs index 30bdb0134..f7870cc5e 100644 --- a/src/fun/transform/float_combinators.rs +++ b/src/fun/transform/float_combinators.rs @@ -1,5 +1,5 @@ use crate::{ - fun::{Book, Definition, Name, Pattern, Rule, Term}, + fun::{Book, Definition, Name, Pattern, Rule, Source, Term}, maybe_grow, multi_iterator, }; use std::collections::{BTreeMap, HashSet}; @@ -44,11 +44,11 @@ impl Book { } } - let builtin = def.is_builtin(); + let source = def.source.clone(); let body = &mut def.rule_mut().body; ctx.reset(); ctx.def_size = body.size(); - body.float_combinators(&mut ctx, def_name, builtin); + body.float_combinators(&mut ctx, def_name, source.clone()); } self.defs.extend(ctx.combinators.into_iter().map(|(nam, (_, def))| (nam, def))); @@ -84,11 +84,11 @@ impl<'b> FloatCombinatorsCtx<'b> { } impl Term { - fn float_combinators(&mut self, ctx: &mut FloatCombinatorsCtx, def_name: &Name, builtin: bool) { + fn float_combinators(&mut self, ctx: &mut FloatCombinatorsCtx, def_name: &Name, source: Source) { maybe_grow(|| { // Recursively float the grandchildren terms. for child in self.float_children_mut() { - child.float_combinators(ctx, def_name, builtin); + child.float_combinators(ctx, def_name, source.clone()); } let mut size = self.size(); @@ -104,14 +104,14 @@ impl Term { if child.is_combinator() && child_size > 0 && (!child_is_safe || extract_for_size) { ctx.def_size -= child_size; size -= child_size; - child.float(ctx, def_name, builtin, child_is_safe); + child.float(ctx, def_name, source.clone(), child_is_safe); } } }) } /// Inserts a new definition for the given term in the combinators map. - fn float(&mut self, ctx: &mut FloatCombinatorsCtx, def_name: &Name, builtin: bool, is_safe: bool) { + fn float(&mut self, ctx: &mut FloatCombinatorsCtx, def_name: &Name, source: Source, is_safe: bool) { let comb_name = Name::new(format!("{}{}{}", def_name, NAME_SEP, ctx.name_gen)); ctx.name_gen += 1; @@ -119,7 +119,7 @@ impl Term { let extracted_term = std::mem::replace(self, comb_ref); let rules = vec![Rule { body: extracted_term, pats: Vec::new() }]; - let rule = Definition::new_gen(comb_name.clone(), rules, builtin); + let rule = Definition::new_gen(comb_name.clone(), rules, source); ctx.combinators.insert(comb_name, (is_safe, rule)); } } diff --git a/src/fun/transform/lift_local_defs.rs b/src/fun/transform/lift_local_defs.rs index feb1e2beb..4cff00dd5 100644 --- a/src/fun/transform/lift_local_defs.rs +++ b/src/fun/transform/lift_local_defs.rs @@ -41,11 +41,12 @@ impl Term { defs.keys().filter(|name| name.starts_with(local_name.as_ref())).cloned().collect::>(); let (r#use, fvs, mut rules) = gen_use(inner_defs, &local_name, &def.name, nxt, std::mem::take(&mut def.rules)); + let source = std::mem::take(&mut def.source); *self = r#use; apply_closure(&mut rules, &fvs); - let new_def = Definition::new_gen(local_name.clone(), rules, false); + let new_def = Definition::new_gen(local_name.clone(), rules, source); defs.insert(local_name.clone(), new_def); } _ => { diff --git a/src/hvm/check_net_size.rs b/src/hvm/check_net_size.rs index f524ff121..82a2abedf 100644 --- a/src/hvm/check_net_size.rs +++ b/src/hvm/check_net_size.rs @@ -20,9 +20,10 @@ pub fn check_net_sizes( for (name, net) in &book.defs { let nodes = count_nodes(net); if nodes > net_size_bound { - diagnostics.add_rule_error( + diagnostics.add_function_error( format!("Definition is too large for HVM {target_lang} (size={nodes}, max size={net_size_bound}). Please break it into smaller pieces."), Name::new(name), + Default::default() ); } } diff --git a/src/imp/parser.rs b/src/imp/parser.rs index fe6b94bbc..dde651ba5 100644 --- a/src/imp/parser.rs +++ b/src/imp/parser.rs @@ -1,7 +1,7 @@ use crate::{ fun::{ parser::{is_num_char, Indent, ParseResult, ParserCommons}, - CtrField, Name, Num, Op, STRINGS, + CtrField, Name, Num, Op, Source, SourceKind, STRINGS, }, imp::{AssignPattern, Definition, Enum, Expr, InPlaceOp, MatchArm, Stmt, Variant}, maybe_grow, @@ -1037,7 +1037,8 @@ impl<'a> PyParser<'a> { indent.exit_level(); // Temporary source, should be overwritten later - let def = Definition { name, params, body, source: crate::fun::Source::Generated }; + let source = Source { file: None, span: None, kind: SourceKind::Generated }; + let def = Definition { name, params, body, source }; Ok((def, nxt_indent)) } @@ -1080,7 +1081,8 @@ impl<'a> PyParser<'a> { fields = self.list_like(|p| p.parse_variant_field(), "{", "}", ",", true, 0)?; } if let Some(field) = fields.find_repeated_names().into_iter().next() { - return Err(format!("Found a repeated field '{field}' in constructor {ctr_name}.")); + let msg = format!("Found a repeated field '{field}' in constructor {ctr_name}."); + return self.expected_message(&msg); } Ok(Variant { name: ctr_name, fields }) } @@ -1101,7 +1103,8 @@ impl<'a> PyParser<'a> { vec![] }; if let Some(field) = fields.find_repeated_names().into_iter().next() { - return Err(format!("Found a repeated field '{field}' in object {name}.")); + let msg = format!("Found a repeated field '{field}' in object {name}."); + return self.expected_message(&msg); } if !self.is_eof() { self.consume_new_line()?; diff --git a/src/imp/to_fun.rs b/src/imp/to_fun.rs index 1c419c1ec..114427e79 100644 --- a/src/imp/to_fun.rs +++ b/src/imp/to_fun.rs @@ -1,13 +1,17 @@ use super::{AssignPattern, Definition, Expr, InPlaceOp, Stmt}; -use crate::fun::{ - self, - builtins::{LCONS, LNIL}, - parser::ParseBook, - Book, Name, +use crate::{ + diagnostics::Diagnostics, + fun::{ + self, + builtins::{LCONS, LNIL}, + parser::ParseBook, + Book, Name, + }, }; impl ParseBook { - pub fn to_fun(mut self) -> Result { + // TODO: Change all functions to return diagnostics + pub fn to_fun(mut self) -> Result { for (name, mut def) in std::mem::take(&mut self.imp_defs) { def.order_kwargs(&self)?; def.gen_map_get(); @@ -25,12 +29,23 @@ impl ParseBook { } impl Definition { - pub fn to_fun(self) -> Result { - let body = self.body.into_fun().map_err(|e| format!("In function '{}': {}", self.name, e))?; + pub fn to_fun(self) -> Result { + let body = self.body.into_fun().map_err(|e| { + let mut diags = Diagnostics::default(); + diags.add_function_error(e, self.name.clone(), self.source.clone()); + diags + })?; + let body = match body { StmtToFun::Return(term) => term, StmtToFun::Assign(..) => { - return Err(format!("Function '{}' doesn't end with a return statement", self.name)); + let mut diags = Diagnostics::default(); + diags.add_function_error( + "Function doesn't end with a return statement", + self.name, + self.source.clone(), + ); + return Err(diags); } }; @@ -100,7 +115,7 @@ impl Stmt { wrap(nxt_pat, term, ask) } Stmt::Assign { pat: AssignPattern::MapSet(..), val: _, nxt: None } => { - return Err("Branch ends with map assignment.".to_string()); + return Err("Branch ends with map assignment.".to_string())?; } Stmt::Assign { pat, val, nxt: Some(nxt) } => { let pat = pat.into_fun(); @@ -179,17 +194,17 @@ impl Stmt { (ask && ask_, Some(tp), t, e) } (StmtToFun::Assign(..), StmtToFun::Assign(..)) => { - return Err("'if' branches end with different assignments.".to_string()); + return Err("'if' branches end with different assignments.".to_string())?; } (StmtToFun::Return(..), StmtToFun::Assign(..)) => { return Err( "Expected 'else' branch from 'if' to return, but it ends with assignment.".to_string(), - ); + )?; } (StmtToFun::Assign(..), StmtToFun::Return(..)) => { return Err( "Expected 'else' branch from 'if' to end with assignment, but it returns.".to_string(), - ); + )?; } }; let arms = vec![else_, then]; @@ -215,13 +230,13 @@ impl Stmt { let (arm_ask, arm_pat, arm_rgt) = take(arm.rgt)?; match (&arm_pat, &fst_pat) { (Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat || arm_ask != fst_ask => { - return Err("'match' arms end with different assignments.".to_string()); + return Err("'match' arms end with different assignments.".to_string())?; } (Some(_), None) => { - return Err("Expected 'match' arms to end with assignment, but it returns.".to_string()); + return Err("Expected 'match' arms to end with assignment, but it returns.".to_string())?; } (None, Some(_)) => { - return Err("Expected 'match' arms to return, but it ends with assignment.".to_string()); + return Err("Expected 'match' arms to return, but it ends with assignment.".to_string())?; } (Some(_), Some(_)) => fun_arms.push((arm.lft, vec![], arm_rgt)), (None, None) => fun_arms.push((arm.lft, vec![], arm_rgt)), @@ -242,13 +257,13 @@ impl Stmt { let (arm_ask, arm_pat, arm) = take(arm)?; match (&arm_pat, &fst_pat) { (Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat || arm_ask != fst_ask => { - return Err("'switch' arms end with different assignments.".to_string()); + return Err("'switch' arms end with different assignments.".to_string())?; } (Some(_), None) => { - return Err("Expected 'switch' arms to end with assignment, but it returns.".to_string()); + return Err("Expected 'switch' arms to end with assignment, but it returns.".to_string())?; } (None, Some(_)) => { - return Err("Expected 'switch' arms to return, but it ends with assignment.".to_string()); + return Err("Expected 'switch' arms to return, but it ends with assignment.".to_string())?; } (Some(_), Some(_)) => fun_arms.push(arm), (None, None) => fun_arms.push(arm), @@ -270,13 +285,13 @@ impl Stmt { let (arm_ask, arm_pat, arm_rgt) = take(arm.rgt)?; match (&arm_pat, &fst_pat) { (Some(arm_pat), Some(fst_pat)) if arm_pat != fst_pat || arm_ask != fst_ask => { - return Err("'fold' arms end with different assignments.".to_string()); + return Err("'fold' arms end with different assignments.".to_string())?; } (Some(_), None) => { - return Err("Expected 'fold' arms to end with assignment, but it returns.".to_string()); + return Err("Expected 'fold' arms to end with assignment, but it returns.".to_string())?; } (None, Some(_)) => { - return Err("Expected 'fold' arms to return, but it ends with assignment.".to_string()); + return Err("Expected 'fold' arms to return, but it ends with assignment.".to_string())?; } (Some(_), Some(_)) => fun_arms.push((arm.lft, vec![], arm_rgt)), (None, None) => fun_arms.push((arm.lft, vec![], arm_rgt)), @@ -294,17 +309,17 @@ impl Stmt { (aa && ba, Some(sp), s, b) } (StmtToFun::Assign(..), StmtToFun::Assign(..)) => { - return Err("'bend' branches end with different assignments.".to_string()); + return Err("'bend' branches end with different assignments.".to_string())?; } (StmtToFun::Return(..), StmtToFun::Assign(..)) => { return Err( "Expected 'else' branch from 'bend' to return, but it ends with assignment.".to_string(), - ); + )?; } (StmtToFun::Assign(..), StmtToFun::Return(..)) => { return Err( "Expected 'else' branch from 'bend' to end with assignment, but it returns.".to_string(), - ); + )?; } }; let term = @@ -340,7 +355,7 @@ impl Stmt { Stmt::Return { term } => StmtToFun::Return(term.to_fun()), Stmt::LocalDef { def, nxt } => { let (ask, nxt_pat, nxt) = take(*nxt)?; - let def = def.to_fun()?; + let def = def.to_fun().map_err(|e| e.display_only_messages().to_string())?; let term = fun::Term::Def { def, nxt: Box::new(nxt) }; wrap(nxt_pat, term, ask) } @@ -466,7 +481,7 @@ fn wrap_nxt_assign_stmt( }; Ok(wrap(nxt_pat, term, ask_nxt)) } else { - Err("Statement ends with return but is not at end of function.".to_string()) + Err("Statement ends with return but is not at end of function.".to_string())? } } else if let Some(pat) = pat { Ok(StmtToFun::Assign(ask, pat, term)) diff --git a/src/imports/book.rs b/src/imports/book.rs index 3c2c7b7b8..c6ea1676e 100644 --- a/src/imports/book.rs +++ b/src/imports/book.rs @@ -1,7 +1,7 @@ use super::{BindMap, ImportsMap, PackageLoader}; use crate::{ diagnostics::{Diagnostics, DiagnosticsConfig}, - fun::{parser::ParseBook, Adt, Book, Definition, HvmDefinition, Name, Rule, Source, Term}, + fun::{parser::ParseBook, Adt, Book, Definition, HvmDefinition, Name, Rule, Source, SourceKind, Term}, imp::{self, Expr, Stmt}, imports::packages::Packages, }; @@ -167,7 +167,7 @@ impl ParseBook { // starting with `__` if not imported by the main book. for (mut name, mut adt) in adts { if adt.source.is_local() { - adt.source = Source::Imported; + adt.source.kind = SourceKind::Imported; name = Name::new(format!("{}/{}", src, name)); let mangle_name = !main_imports.contains_source(&name); @@ -220,7 +220,7 @@ impl ParseBook { // Applies the binds for the new names for every definition for (_, def) in self.local_defs_mut() { def.apply_binds(false, &canonical_map); - *def.source_mut() = Source::Imported; + def.source_mut().kind = SourceKind::Imported; } } } diff --git a/tests/golden_tests.rs b/tests/golden_tests.rs index 8374e64fe..618e55270 100644 --- a/tests/golden_tests.rs +++ b/tests/golden_tests.rs @@ -480,7 +480,7 @@ fn examples() -> Result<(), Diagnostics> { run_book(book, RunOpts::default(), compile_opts, diagnostics_cfg, None, "run-c")?.unwrap(); format!("{diags}{term}") } - Err(e) => e, + Err(e) => e.to_string(), }; let mut settings = insta::Settings::clone_current(); diff --git a/tests/snapshots/cli__desugar_merge.bend.snap b/tests/snapshots/cli__desugar_merge.bend.snap index 15fe92082..3df1b698b 100644 --- a/tests/snapshots/cli__desugar_merge.bend.snap +++ b/tests/snapshots/cli__desugar_merge.bend.snap @@ -3,6 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_merge.bend --- Warnings: +In tests/golden_tests/cli/desugar_merge.bend : In definition 'Z': Definition is unused. diff --git a/tests/snapshots/cli__desugar_pretty.bend.snap b/tests/snapshots/cli__desugar_pretty.bend.snap index bd6dca093..bd4e91c29 100644 --- a/tests/snapshots/cli__desugar_pretty.bend.snap +++ b/tests/snapshots/cli__desugar_pretty.bend.snap @@ -3,6 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/desugar_pretty.bend --- Warnings: +In tests/golden_tests/cli/desugar_pretty.bend : In definition 'Foo': Definition is unused. diff --git a/tests/snapshots/cli__warn_and_err.bend.snap b/tests/snapshots/cli__warn_and_err.bend.snap index e972dc58c..33ca82179 100644 --- a/tests/snapshots/cli__warn_and_err.bend.snap +++ b/tests/snapshots/cli__warn_and_err.bend.snap @@ -3,9 +3,11 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/cli/warn_and_err.bend --- Warnings: +In tests/golden_tests/cli/warn_and_err.bend : In definition 'Foo': Repeated bind in pattern matching rule: 'a'. Errors: +In tests/golden_tests/cli/warn_and_err.bend : In definition 'Main': Unbound variable 'a'. diff --git a/tests/snapshots/compile_file__360_no_scope.bend.snap b/tests/snapshots/compile_file__360_no_scope.bend.snap index 905f60e6e..6037e7207 100644 --- a/tests/snapshots/compile_file__360_no_scope.bend.snap +++ b/tests/snapshots/compile_file__360_no_scope.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/360_no_scope.bend --- Errors: -In tests/golden_tests/compile_file/360_no_scope.bend : +In tests/golden_tests/compile_file/360_no_scope.bend : - expected: '=' - detected: end of input  6 |   diff --git a/tests/snapshots/compile_file__ask_outside_do.bend.snap b/tests/snapshots/compile_file__ask_outside_do.bend.snap index d8c0ab4b8..34f00a67d 100644 --- a/tests/snapshots/compile_file__ask_outside_do.bend.snap +++ b/tests/snapshots/compile_file__ask_outside_do.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/ask_outside_do.bend --- Errors: +In tests/golden_tests/compile_file/ask_outside_do.bend : In definition 'main': Monadic bind operation 'x <- ...' used outside of a `do` block. diff --git a/tests/snapshots/compile_file__elif_no_else.bend.snap b/tests/snapshots/compile_file__elif_no_else.bend.snap index b812d1cb3..2db861c9c 100644 --- a/tests/snapshots/compile_file__elif_no_else.bend.snap +++ b/tests/snapshots/compile_file__elif_no_else.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/elif_no_else.bend --- Errors: -In tests/golden_tests/compile_file/elif_no_else.bend : +In tests/golden_tests/compile_file/elif_no_else.bend : - expected: 'else' or 'elif' - detected: end of input  6 |   diff --git a/tests/snapshots/compile_file__error_data_def_name.bend.snap b/tests/snapshots/compile_file__error_data_def_name.bend.snap index c9baf72df..cd3ddfcf8 100644 --- a/tests/snapshots/compile_file__error_data_def_name.bend.snap +++ b/tests/snapshots/compile_file__error_data_def_name.bend.snap @@ -3,6 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/error_data_def_name.bend --- Errors: -In tests/golden_tests/compile_file/error_data_def_name.bend : +In tests/golden_tests/compile_file/error_data_def_name.bend : Redefinition of constructor 'A/A'.  2 | A/A = 0 diff --git a/tests/snapshots/compile_file__error_messages.bend.snap b/tests/snapshots/compile_file__error_messages.bend.snap index 272abe75a..ec228477f 100644 --- a/tests/snapshots/compile_file__error_messages.bend.snap +++ b/tests/snapshots/compile_file__error_messages.bend.snap @@ -3,6 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/error_messages.bend --- Errors: +In tests/golden_tests/compile_file/error_messages.bend : In definition 'Foo': Unbound constructor 'C' in pattern matching rule. Unbound constructor 'D' in pattern matching rule. diff --git a/tests/snapshots/compile_file__just_a_name.bend.snap b/tests/snapshots/compile_file__just_a_name.bend.snap index 363618199..83bbedf79 100644 --- a/tests/snapshots/compile_file__just_a_name.bend.snap +++ b/tests/snapshots/compile_file__just_a_name.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/just_a_name.bend --- Errors: -In tests/golden_tests/compile_file/just_a_name.bend : +In tests/golden_tests/compile_file/just_a_name.bend : - expected: pattern or '=' - detected: end of input  1 | asdf diff --git a/tests/snapshots/compile_file__just_data.bend.snap b/tests/snapshots/compile_file__just_data.bend.snap index 60d5214bf..e8c8a198d 100644 --- a/tests/snapshots/compile_file__just_data.bend.snap +++ b/tests/snapshots/compile_file__just_data.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/just_data.bend --- Errors: -In tests/golden_tests/compile_file/just_data.bend : +In tests/golden_tests/compile_file/just_data.bend : - expected: datatype name - detected: end of input  1 | type  diff --git a/tests/snapshots/compile_file__just_paren.bend.snap b/tests/snapshots/compile_file__just_paren.bend.snap index ed73d4b5a..473cdde5a 100644 --- a/tests/snapshots/compile_file__just_paren.bend.snap +++ b/tests/snapshots/compile_file__just_paren.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/just_paren.bend --- Errors: -In tests/golden_tests/compile_file/just_paren.bend : +In tests/golden_tests/compile_file/just_paren.bend : - expected: function name - detected: end of input  2 | (  diff --git a/tests/snapshots/compile_file__just_rule_paren.bend.snap b/tests/snapshots/compile_file__just_rule_paren.bend.snap index 9a98bcb38..65efb13d9 100644 --- a/tests/snapshots/compile_file__just_rule_paren.bend.snap +++ b/tests/snapshots/compile_file__just_rule_paren.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/just_rule_paren.bend --- Errors: -In tests/golden_tests/compile_file/just_rule_paren.bend : +In tests/golden_tests/compile_file/just_rule_paren.bend : - expected: '=' - detected: end of input  1 | (rule)  diff --git a/tests/snapshots/compile_file__mismatched_ask_statements.bend.snap b/tests/snapshots/compile_file__mismatched_ask_statements.bend.snap index 57957a02e..af79fef61 100644 --- a/tests/snapshots/compile_file__mismatched_ask_statements.bend.snap +++ b/tests/snapshots/compile_file__mismatched_ask_statements.bend.snap @@ -3,4 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/mismatched_ask_statements.bend --- Errors: -In function 'main': 'match' arms end with different assignments. +In tests/golden_tests/compile_file/mismatched_ask_statements.bend : +In definition 'main': + 'match' arms end with different assignments. diff --git a/tests/snapshots/compile_file__missing_adt_eq.bend.snap b/tests/snapshots/compile_file__missing_adt_eq.bend.snap index 031163813..d73423579 100644 --- a/tests/snapshots/compile_file__missing_adt_eq.bend.snap +++ b/tests/snapshots/compile_file__missing_adt_eq.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/missing_adt_eq.bend --- Errors: -In tests/golden_tests/compile_file/missing_adt_eq.bend : +In tests/golden_tests/compile_file/missing_adt_eq.bend : - expected: '=' - detected: end of input  1 | type Adt  diff --git a/tests/snapshots/compile_file__missing_ctrs.bend.snap b/tests/snapshots/compile_file__missing_ctrs.bend.snap index 3c58706dd..820876baa 100644 --- a/tests/snapshots/compile_file__missing_ctrs.bend.snap +++ b/tests/snapshots/compile_file__missing_ctrs.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/missing_ctrs.bend --- Errors: -In tests/golden_tests/compile_file/missing_ctrs.bend : +In tests/golden_tests/compile_file/missing_ctrs.bend : - expected: datatype constructor name - detected: end of input  1 | type Adt =  diff --git a/tests/snapshots/compile_file__missing_pat.bend.snap b/tests/snapshots/compile_file__missing_pat.bend.snap index b5ab00d92..1d83f97e2 100644 --- a/tests/snapshots/compile_file__missing_pat.bend.snap +++ b/tests/snapshots/compile_file__missing_pat.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/missing_pat.bend --- Errors: -In tests/golden_tests/compile_file/missing_pat.bend : +In tests/golden_tests/compile_file/missing_pat.bend : - expected: name or '*' - detected:  2 | : * diff --git a/tests/snapshots/compile_file__nested_ctr_wrong_arity.bend.snap b/tests/snapshots/compile_file__nested_ctr_wrong_arity.bend.snap index 3a150fb56..1727dfc9f 100644 --- a/tests/snapshots/compile_file__nested_ctr_wrong_arity.bend.snap +++ b/tests/snapshots/compile_file__nested_ctr_wrong_arity.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/nested_ctr_wrong_arity.bend --- Errors: +In tests/golden_tests/compile_file/nested_ctr_wrong_arity.bend : In definition 'fst_fst': Incorrect arity for constructor 'Pair/Pair' of type 'Pair' in pattern matching rule. Expected 2 fields, found 1 diff --git a/tests/snapshots/compile_file__number_too_large.bend.snap b/tests/snapshots/compile_file__number_too_large.bend.snap index 9f7ea27e9..fefe7c825 100644 --- a/tests/snapshots/compile_file__number_too_large.bend.snap +++ b/tests/snapshots/compile_file__number_too_large.bend.snap @@ -3,6 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/number_too_large.bend --- Errors: -In tests/golden_tests/compile_file/number_too_large.bend : +In tests/golden_tests/compile_file/number_too_large.bend : Number literal outside of range for U24.  1 | main = 0x10000000 diff --git a/tests/snapshots/compile_file__repeated_bind_rule.bend.snap b/tests/snapshots/compile_file__repeated_bind_rule.bend.snap index d6fa3989d..246885b17 100644 --- a/tests/snapshots/compile_file__repeated_bind_rule.bend.snap +++ b/tests/snapshots/compile_file__repeated_bind_rule.bend.snap @@ -3,6 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/repeated_bind_rule.bend --- Warnings: +In tests/golden_tests/compile_file/repeated_bind_rule.bend : In definition 'Foo': Repeated bind in pattern matching rule: 'a'. diff --git a/tests/snapshots/compile_file__switch_all_patterns.bend.snap b/tests/snapshots/compile_file__switch_all_patterns.bend.snap index 3478deee2..e28f72819 100644 --- a/tests/snapshots/compile_file__switch_all_patterns.bend.snap +++ b/tests/snapshots/compile_file__switch_all_patterns.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/switch_all_patterns.bend --- Errors: -In tests/golden_tests/compile_file/switch_all_patterns.bend : +In tests/golden_tests/compile_file/switch_all_patterns.bend : - expected: '0' - detected:  7 | _: x-1 diff --git a/tests/snapshots/compile_file__switch_incomplete.bend.snap b/tests/snapshots/compile_file__switch_incomplete.bend.snap index e12910f6d..e1963c608 100644 --- a/tests/snapshots/compile_file__switch_incomplete.bend.snap +++ b/tests/snapshots/compile_file__switch_incomplete.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/switch_incomplete.bend --- Errors: -In tests/golden_tests/compile_file/switch_incomplete.bend : +In tests/golden_tests/compile_file/switch_incomplete.bend : - expected: term - detected:  2 | main = switch {} diff --git a/tests/snapshots/compile_file__top_level_name_slashslash.bend.snap b/tests/snapshots/compile_file__top_level_name_slashslash.bend.snap index 058840a03..671f5a591 100644 --- a/tests/snapshots/compile_file__top_level_name_slashslash.bend.snap +++ b/tests/snapshots/compile_file__top_level_name_slashslash.bend.snap @@ -3,6 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/top_level_name_slashslash.bend --- Errors: -In tests/golden_tests/compile_file/top_level_name_slashslash.bend : +In tests/golden_tests/compile_file/top_level_name_slashslash.bend : Top-level names are not allowed to start with "//".  4 | def //thisshouldfail(): diff --git a/tests/snapshots/compile_file__unbound_unscoped_var.bend.snap b/tests/snapshots/compile_file__unbound_unscoped_var.bend.snap index 50012bebc..f338bc8b1 100644 --- a/tests/snapshots/compile_file__unbound_unscoped_var.bend.snap +++ b/tests/snapshots/compile_file__unbound_unscoped_var.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/unbound_unscoped_var.bend --- Errors: +In tests/golden_tests/compile_file/unbound_unscoped_var.bend : In definition 'main': Unbound unscoped variable '$a'. diff --git a/tests/snapshots/compile_file__unbound_var.bend.snap b/tests/snapshots/compile_file__unbound_var.bend.snap index a53e420c1..57558fd1b 100644 --- a/tests/snapshots/compile_file__unbound_var.bend.snap +++ b/tests/snapshots/compile_file__unbound_var.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/unbound_var.bend --- Errors: +In tests/golden_tests/compile_file/unbound_var.bend : In definition 'main': Unbound variable 'a'. diff --git a/tests/snapshots/compile_file__unbound_var_scope.bend.snap b/tests/snapshots/compile_file__unbound_var_scope.bend.snap index 2976e0743..26bdad031 100644 --- a/tests/snapshots/compile_file__unbound_var_scope.bend.snap +++ b/tests/snapshots/compile_file__unbound_var_scope.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/unbound_var_scope.bend --- Errors: +In tests/golden_tests/compile_file/unbound_var_scope.bend : In definition 'main': Unbound variable 'b'. diff --git a/tests/snapshots/compile_file__unbound_with_tup_pattern.bend.snap b/tests/snapshots/compile_file__unbound_with_tup_pattern.bend.snap index 549c276db..310955d13 100644 --- a/tests/snapshots/compile_file__unbound_with_tup_pattern.bend.snap +++ b/tests/snapshots/compile_file__unbound_with_tup_pattern.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/unbound_with_tup_pattern.bend --- Errors: +In tests/golden_tests/compile_file/unbound_with_tup_pattern.bend : In definition 'Foo': Unbound variable 'a'. diff --git a/tests/snapshots/compile_file__unexpected_top_char.bend.snap b/tests/snapshots/compile_file__unexpected_top_char.bend.snap index a847ea43f..73a648b3f 100644 --- a/tests/snapshots/compile_file__unexpected_top_char.bend.snap +++ b/tests/snapshots/compile_file__unexpected_top_char.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/unexpected_top_char.bend --- Errors: -In tests/golden_tests/compile_file/unexpected_top_char.bend : +In tests/golden_tests/compile_file/unexpected_top_char.bend : - expected: top-level definition - detected:  1 | * diff --git a/tests/snapshots/compile_file__unscoped_dup_use.bend.snap b/tests/snapshots/compile_file__unscoped_dup_use.bend.snap index 1c4ed17ce..14369581f 100644 --- a/tests/snapshots/compile_file__unscoped_dup_use.bend.snap +++ b/tests/snapshots/compile_file__unscoped_dup_use.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/unscoped_dup_use.bend --- Errors: +In tests/golden_tests/compile_file/unscoped_dup_use.bend : In definition 'main': Unscoped variable '$a' used more than once. diff --git a/tests/snapshots/compile_file__unused_unscoped_bind.bend.snap b/tests/snapshots/compile_file__unused_unscoped_bind.bend.snap index b2b5f265a..986661ae2 100644 --- a/tests/snapshots/compile_file__unused_unscoped_bind.bend.snap +++ b/tests/snapshots/compile_file__unused_unscoped_bind.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/unused_unscoped_bind.bend --- Errors: +In tests/golden_tests/compile_file/unused_unscoped_bind.bend : In definition 'main': Unscoped variable from lambda 'λ$a' is never used. diff --git a/tests/snapshots/compile_file__variable_name_double_underscore.bend.snap b/tests/snapshots/compile_file__variable_name_double_underscore.bend.snap index 667887ed5..75c737f84 100644 --- a/tests/snapshots/compile_file__variable_name_double_underscore.bend.snap +++ b/tests/snapshots/compile_file__variable_name_double_underscore.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/variable_name_double_underscore.bend --- Errors: -In tests/golden_tests/compile_file/variable_name_double_underscore.bend : +In tests/golden_tests/compile_file/variable_name_double_underscore.bend : - expected: expression - detected:  2 | return __this_should_fail__(*) diff --git a/tests/snapshots/compile_file__warn_and_err.bend.snap b/tests/snapshots/compile_file__warn_and_err.bend.snap index 3e331175a..7bfd6e05f 100644 --- a/tests/snapshots/compile_file__warn_and_err.bend.snap +++ b/tests/snapshots/compile_file__warn_and_err.bend.snap @@ -3,9 +3,11 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/warn_and_err.bend --- Warnings: +In tests/golden_tests/compile_file/warn_and_err.bend : In definition 'Foo': Repeated bind in pattern matching rule: 'a'. Errors: +In tests/golden_tests/compile_file/warn_and_err.bend : In definition 'Main': Unbound variable 'a'. diff --git a/tests/snapshots/compile_file__with_clause_parse_err.bend.snap b/tests/snapshots/compile_file__with_clause_parse_err.bend.snap index 94e4475f7..9969ae9e0 100644 --- a/tests/snapshots/compile_file__with_clause_parse_err.bend.snap +++ b/tests/snapshots/compile_file__with_clause_parse_err.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/with_clause_parse_err.bend --- Errors: -In tests/golden_tests/compile_file/with_clause_parse_err.bend : +In tests/golden_tests/compile_file/with_clause_parse_err.bend : - expected: '{' - detected:  1 | main = @a @b switch b witha{ diff --git a/tests/snapshots/compile_file__wrong_ctr_arity.bend.snap b/tests/snapshots/compile_file__wrong_ctr_arity.bend.snap index 1f47876cd..a1cc9d9bc 100644 --- a/tests/snapshots/compile_file__wrong_ctr_arity.bend.snap +++ b/tests/snapshots/compile_file__wrong_ctr_arity.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/wrong_ctr_arity.bend --- Errors: +In tests/golden_tests/compile_file/wrong_ctr_arity.bend : In definition 'Bar': Incorrect arity for constructor 'Boxed/Box' of type 'Boxed' in pattern matching rule. Expected 1 fields, found 2 diff --git a/tests/snapshots/compile_file__wrong_ctr_var_arity.bend.snap b/tests/snapshots/compile_file__wrong_ctr_var_arity.bend.snap index 60591d401..2b6732e62 100644 --- a/tests/snapshots/compile_file__wrong_ctr_var_arity.bend.snap +++ b/tests/snapshots/compile_file__wrong_ctr_var_arity.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/wrong_ctr_var_arity.bend --- Errors: +In tests/golden_tests/compile_file/wrong_ctr_var_arity.bend : In definition 'foo': Incorrect arity for constructor 'Tup/pair' of type 'Tup' in pattern matching rule. Expected 2 fields, found 0 diff --git a/tests/snapshots/compile_file__wrong_nums.bend.snap b/tests/snapshots/compile_file__wrong_nums.bend.snap index f8a2a1533..caf34f8bc 100644 --- a/tests/snapshots/compile_file__wrong_nums.bend.snap +++ b/tests/snapshots/compile_file__wrong_nums.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/wrong_nums.bend --- Errors: -In tests/golden_tests/compile_file/wrong_nums.bend : +In tests/golden_tests/compile_file/wrong_nums.bend : - expected: valid binary digit - detected:  1 | main = (+ 0b012345 0FA) diff --git a/tests/snapshots/compile_file__wrong_unicode_escape.bend.snap b/tests/snapshots/compile_file__wrong_unicode_escape.bend.snap index 682d74204..42553937f 100644 --- a/tests/snapshots/compile_file__wrong_unicode_escape.bend.snap +++ b/tests/snapshots/compile_file__wrong_unicode_escape.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file/wrong_unicode_escape.bend --- Errors: -In tests/golden_tests/compile_file/wrong_unicode_escape.bend : +In tests/golden_tests/compile_file/wrong_unicode_escape.bend : - expected: '}' - detected:  1 | main = (String.cons '\u{1' "\u2}\u{zxcx}") diff --git a/tests/snapshots/compile_file_o_all__adt_string.bend.snap b/tests/snapshots/compile_file_o_all__adt_string.bend.snap index f5d4470c6..c491410d8 100644 --- a/tests/snapshots/compile_file_o_all__adt_string.bend.snap +++ b/tests/snapshots/compile_file_o_all__adt_string.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file_o_all/adt_string.bend --- Errors: -In tests/golden_tests/compile_file_o_all/adt_string.bend : +In tests/golden_tests/compile_file_o_all/adt_string.bend : Redefinition of builtin (type) 'String'.  1 | type String = S  2 |  diff --git a/tests/snapshots/compile_file_o_all__bad_parens_making_erased_let.bend.snap b/tests/snapshots/compile_file_o_all__bad_parens_making_erased_let.bend.snap index 726c4d9a4..ada4253c7 100644 --- a/tests/snapshots/compile_file_o_all__bad_parens_making_erased_let.bend.snap +++ b/tests/snapshots/compile_file_o_all__bad_parens_making_erased_let.bend.snap @@ -3,6 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file_o_all/bad_parens_making_erased_let.bend --- Errors: +In tests/golden_tests/compile_file_o_all/bad_parens_making_erased_let.bend : In definition 'main': Unbound variable 'two'. Unbound variable 'qua'. diff --git a/tests/snapshots/compile_file_o_all__cyclic_dup.bend.snap b/tests/snapshots/compile_file_o_all__cyclic_dup.bend.snap index 6114b7b49..7a95980d8 100644 --- a/tests/snapshots/compile_file_o_all__cyclic_dup.bend.snap +++ b/tests/snapshots/compile_file_o_all__cyclic_dup.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file_o_all/cyclic_dup.bend --- Errors: +In tests/golden_tests/compile_file_o_all/cyclic_dup.bend : In definition 'main': Unbound variable 'y1'. diff --git a/tests/snapshots/compile_file_o_all__match_adt_non_exhaustive.bend.snap b/tests/snapshots/compile_file_o_all__match_adt_non_exhaustive.bend.snap index b3463d452..e1ddd5deb 100644 --- a/tests/snapshots/compile_file_o_all__match_adt_non_exhaustive.bend.snap +++ b/tests/snapshots/compile_file_o_all__match_adt_non_exhaustive.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file_o_all/match_adt_non_exhaustive.bend --- Errors: +In tests/golden_tests/compile_file_o_all/match_adt_non_exhaustive.bend : In definition 'main': Non-exhaustive 'match' expression of type 'Maybe'. Case 'Maybe/Some' not covered. diff --git a/tests/snapshots/compile_file_o_all__non_exhaustive_and.bend.snap b/tests/snapshots/compile_file_o_all__non_exhaustive_and.bend.snap index 84cd4c29b..d75690cbd 100644 --- a/tests/snapshots/compile_file_o_all__non_exhaustive_and.bend.snap +++ b/tests/snapshots/compile_file_o_all__non_exhaustive_and.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file_o_all/non_exhaustive_and.bend --- Errors: +In tests/golden_tests/compile_file_o_all/non_exhaustive_and.bend : In definition 'Bool.and': Non-exhaustive pattern matching rule. Constructor 'Bool/F' of type 'Bool' not covered diff --git a/tests/snapshots/compile_file_o_all__non_exhaustive_different_types.bend.snap b/tests/snapshots/compile_file_o_all__non_exhaustive_different_types.bend.snap index 2ced975ff..44e9f6202 100644 --- a/tests/snapshots/compile_file_o_all__non_exhaustive_different_types.bend.snap +++ b/tests/snapshots/compile_file_o_all__non_exhaustive_different_types.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file_o_all/non_exhaustive_different_types.bend --- Errors: +In tests/golden_tests/compile_file_o_all/non_exhaustive_different_types.bend : In definition 'foo': Non-exhaustive pattern matching rule. Constructor 'b3/t3' of type 'b3' not covered diff --git a/tests/snapshots/compile_file_o_all__non_exhaustive_pattern.bend.snap b/tests/snapshots/compile_file_o_all__non_exhaustive_pattern.bend.snap index 0e73f2e07..a9a6ef2e1 100644 --- a/tests/snapshots/compile_file_o_all__non_exhaustive_pattern.bend.snap +++ b/tests/snapshots/compile_file_o_all__non_exhaustive_pattern.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file_o_all/non_exhaustive_pattern.bend --- Errors: +In tests/golden_tests/compile_file_o_all/non_exhaustive_pattern.bend : In definition 'Foo': Non-exhaustive pattern matching rule. Constructor 'Type/A' of type 'Type' not covered diff --git a/tests/snapshots/compile_file_o_all__non_exhaustive_tree.bend.snap b/tests/snapshots/compile_file_o_all__non_exhaustive_tree.bend.snap index dca4a8145..90327fd2f 100644 --- a/tests/snapshots/compile_file_o_all__non_exhaustive_tree.bend.snap +++ b/tests/snapshots/compile_file_o_all__non_exhaustive_tree.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file_o_all/non_exhaustive_tree.bend --- Errors: +In tests/golden_tests/compile_file_o_all/non_exhaustive_tree.bend : In definition 'Warp': Non-exhaustive pattern matching rule. Constructor 'Tree/Leaf' of type 'Tree' not covered diff --git a/tests/snapshots/compile_file_o_all__tagged_dup.bend.snap b/tests/snapshots/compile_file_o_all__tagged_dup.bend.snap index a2ba6e42f..e22c0d777 100644 --- a/tests/snapshots/compile_file_o_all__tagged_dup.bend.snap +++ b/tests/snapshots/compile_file_o_all__tagged_dup.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file_o_all/tagged_dup.bend --- Errors: -In tests/golden_tests/compile_file_o_all/tagged_dup.bend : +In tests/golden_tests/compile_file_o_all/tagged_dup.bend : - expected: '=' - detected:  4 | let #i {e f} = @x x; diff --git a/tests/snapshots/compile_file_o_all__tagged_lam.bend.snap b/tests/snapshots/compile_file_o_all__tagged_lam.bend.snap index d76c7274b..f2b735c90 100644 --- a/tests/snapshots/compile_file_o_all__tagged_lam.bend.snap +++ b/tests/snapshots/compile_file_o_all__tagged_lam.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file_o_all/tagged_lam.bend --- Errors: -In tests/golden_tests/compile_file_o_all/tagged_lam.bend : +In tests/golden_tests/compile_file_o_all/tagged_lam.bend : - expected: term - detected: end of input  2 |   diff --git a/tests/snapshots/compile_file_o_all__tagged_sup.bend.snap b/tests/snapshots/compile_file_o_all__tagged_sup.bend.snap index d6a53503d..8b8c76fa7 100644 --- a/tests/snapshots/compile_file_o_all__tagged_sup.bend.snap +++ b/tests/snapshots/compile_file_o_all__tagged_sup.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/compile_file_o_all/tagged_sup.bend --- Errors: -In tests/golden_tests/compile_file_o_all/tagged_sup.bend : +In tests/golden_tests/compile_file_o_all/tagged_sup.bend : - expected: top-level definition - detected:  2 | b = #i {λx x λx x} diff --git a/tests/snapshots/desugar_file__non_exaustive_limit.bend.snap b/tests/snapshots/desugar_file__non_exaustive_limit.bend.snap index f19cb796f..72a3dd7e7 100644 --- a/tests/snapshots/desugar_file__non_exaustive_limit.bend.snap +++ b/tests/snapshots/desugar_file__non_exaustive_limit.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/desugar_file/non_exaustive_limit.bend --- Errors: +In tests/golden_tests/desugar_file/non_exaustive_limit.bend : In definition 'Bar': Non-exhaustive pattern matching rule. Constructor 'Foo/B' of type 'Foo' not covered diff --git a/tests/snapshots/parse_file__bad_floating.bend.snap b/tests/snapshots/parse_file__bad_floating.bend.snap index 08a1de753..bacd0e93f 100644 --- a/tests/snapshots/parse_file__bad_floating.bend.snap +++ b/tests/snapshots/parse_file__bad_floating.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/bad_floating.bend --- Errors: -In tests/golden_tests/parse_file/bad_floating.bend : +In tests/golden_tests/parse_file/bad_floating.bend : - expected: newline - detected:  2 | return 0xA.0xA diff --git a/tests/snapshots/parse_file__bend_missing_else.bend.snap b/tests/snapshots/parse_file__bend_missing_else.bend.snap index e00a8c9fd..3da8c38cb 100644 --- a/tests/snapshots/parse_file__bend_missing_else.bend.snap +++ b/tests/snapshots/parse_file__bend_missing_else.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/bend_missing_else.bend --- Errors: -In tests/golden_tests/parse_file/bend_missing_else.bend : +In tests/golden_tests/parse_file/bend_missing_else.bend : - expected: 'else' - detected:  14 | def main(): diff --git a/tests/snapshots/parse_file__fold_missing_case.bend.snap b/tests/snapshots/parse_file__fold_missing_case.bend.snap index 81926ecc5..362af2f63 100644 --- a/tests/snapshots/parse_file__fold_missing_case.bend.snap +++ b/tests/snapshots/parse_file__fold_missing_case.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/fold_missing_case.bend --- Errors: -In tests/golden_tests/parse_file/fold_missing_case.bend : +In tests/golden_tests/parse_file/fold_missing_case.bend : - expected: 'case' - detected: end of input  4 |   diff --git a/tests/snapshots/parse_file__fun_def_name.bend.snap b/tests/snapshots/parse_file__fun_def_name.bend.snap index 996c633f5..e2a6af2e6 100644 --- a/tests/snapshots/parse_file__fun_def_name.bend.snap +++ b/tests/snapshots/parse_file__fun_def_name.bend.snap @@ -3,6 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/fun_def_name.bend --- Errors: -In tests/golden_tests/parse_file/fun_def_name.bend : +In tests/golden_tests/parse_file/fun_def_name.bend : Expected a rule with name 'aux'.  4 | aux2 (List/Cons head tail) = (+ head (aux tail)) diff --git a/tests/snapshots/parse_file__if_missing_else.bend.snap b/tests/snapshots/parse_file__if_missing_else.bend.snap index 8756e211d..c5b35e0f4 100644 --- a/tests/snapshots/parse_file__if_missing_else.bend.snap +++ b/tests/snapshots/parse_file__if_missing_else.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/if_missing_else.bend --- Errors: -In tests/golden_tests/parse_file/if_missing_else.bend : +In tests/golden_tests/parse_file/if_missing_else.bend : - expected: 'else' or 'elif' - detected: end of input  5 |   diff --git a/tests/snapshots/parse_file__match_missing_case.bend.snap b/tests/snapshots/parse_file__match_missing_case.bend.snap index 5c7ad9e29..700673b10 100644 --- a/tests/snapshots/parse_file__match_missing_case.bend.snap +++ b/tests/snapshots/parse_file__match_missing_case.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/match_missing_case.bend --- Errors: -In tests/golden_tests/parse_file/match_missing_case.bend : +In tests/golden_tests/parse_file/match_missing_case.bend : - expected: 'case' - detected: end of input  4 |   diff --git a/tests/snapshots/parse_file__redefinition_builtin.bend.snap b/tests/snapshots/parse_file__redefinition_builtin.bend.snap index 8d444e7fb..ee51c4004 100644 --- a/tests/snapshots/parse_file__redefinition_builtin.bend.snap +++ b/tests/snapshots/parse_file__redefinition_builtin.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/redefinition_builtin.bend --- Errors: -In tests/golden_tests/parse_file/redefinition_builtin.bend : +In tests/golden_tests/parse_file/redefinition_builtin.bend : Redefinition of builtin (function) 'Map/get'.  1 | def Map/get(m):  2 |  return m diff --git a/tests/snapshots/parse_file__redefinition_ctr_with_fun.bend.snap b/tests/snapshots/parse_file__redefinition_ctr_with_fun.bend.snap index 04f507209..5c6e24669 100644 --- a/tests/snapshots/parse_file__redefinition_ctr_with_fun.bend.snap +++ b/tests/snapshots/parse_file__redefinition_ctr_with_fun.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/redefinition_ctr_with_fun.bend --- Errors: -In tests/golden_tests/parse_file/redefinition_ctr_with_fun.bend : +In tests/golden_tests/parse_file/redefinition_ctr_with_fun.bend : Redefinition of builtin (constructor) 'String/Cons'.  1 | def String/Cons(x):  2 |  return x diff --git a/tests/snapshots/parse_file__redefinition_fun_imp.bend.snap b/tests/snapshots/parse_file__redefinition_fun_imp.bend.snap index 01b3a8b59..b31d21b8a 100644 --- a/tests/snapshots/parse_file__redefinition_fun_imp.bend.snap +++ b/tests/snapshots/parse_file__redefinition_fun_imp.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/redefinition_fun_imp.bend --- Errors: -In tests/golden_tests/parse_file/redefinition_fun_imp.bend : +In tests/golden_tests/parse_file/redefinition_fun_imp.bend : Redefinition of function 'A'.  3 | def A:  4 |  return 0 diff --git a/tests/snapshots/parse_file__redefinition_imp_fun.bend.snap b/tests/snapshots/parse_file__redefinition_imp_fun.bend.snap index 39d574ed1..da1ddb800 100644 --- a/tests/snapshots/parse_file__redefinition_imp_fun.bend.snap +++ b/tests/snapshots/parse_file__redefinition_imp_fun.bend.snap @@ -3,6 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/redefinition_imp_fun.bend --- Errors: -In tests/golden_tests/parse_file/redefinition_imp_fun.bend : +In tests/golden_tests/parse_file/redefinition_imp_fun.bend : Redefinition of function 'A'.  5 | (A) = 1 diff --git a/tests/snapshots/parse_file__redefinition_type_with_object.bend.snap b/tests/snapshots/parse_file__redefinition_type_with_object.bend.snap index 18d84fd3d..189f008db 100644 --- a/tests/snapshots/parse_file__redefinition_type_with_object.bend.snap +++ b/tests/snapshots/parse_file__redefinition_type_with_object.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/redefinition_type_with_object.bend --- Errors: -In tests/golden_tests/parse_file/redefinition_type_with_object.bend : +In tests/golden_tests/parse_file/redefinition_type_with_object.bend : Redefinition of builtin (type) 'IO'.  1 | object IO { run }  diff --git a/tests/snapshots/parse_file__redefinition_with_def_between.bend.snap b/tests/snapshots/parse_file__redefinition_with_def_between.bend.snap index 25c124613..807d09e32 100644 --- a/tests/snapshots/parse_file__redefinition_with_def_between.bend.snap +++ b/tests/snapshots/parse_file__redefinition_with_def_between.bend.snap @@ -3,6 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/redefinition_with_def_between.bend --- Errors: -In tests/golden_tests/parse_file/redefinition_with_def_between.bend : +In tests/golden_tests/parse_file/redefinition_with_def_between.bend : Redefinition of function 'A'.  4 | (A) = @x x diff --git a/tests/snapshots/parse_file__redefinition_with_object_between.bend.snap b/tests/snapshots/parse_file__redefinition_with_object_between.bend.snap index 707c7f137..005d6a4a2 100644 --- a/tests/snapshots/parse_file__redefinition_with_object_between.bend.snap +++ b/tests/snapshots/parse_file__redefinition_with_object_between.bend.snap @@ -3,6 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/redefinition_with_object_between.bend --- Errors: -In tests/golden_tests/parse_file/redefinition_with_object_between.bend : +In tests/golden_tests/parse_file/redefinition_with_object_between.bend : Redefinition of function 'A'.  3 | A = 1 diff --git a/tests/snapshots/parse_file__redefinition_with_type_between.bend.snap b/tests/snapshots/parse_file__redefinition_with_type_between.bend.snap index ccdc4328f..7280f6a08 100644 --- a/tests/snapshots/parse_file__redefinition_with_type_between.bend.snap +++ b/tests/snapshots/parse_file__redefinition_with_type_between.bend.snap @@ -3,6 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/redefinition_with_type_between.bend --- Errors: -In tests/golden_tests/parse_file/redefinition_with_type_between.bend : +In tests/golden_tests/parse_file/redefinition_with_type_between.bend : Redefinition of function 'A'.  3 | A = 1 diff --git a/tests/snapshots/parse_file__repeated_adt_name.bend.snap b/tests/snapshots/parse_file__repeated_adt_name.bend.snap index 0caef38e2..434fc2a0c 100644 --- a/tests/snapshots/parse_file__repeated_adt_name.bend.snap +++ b/tests/snapshots/parse_file__repeated_adt_name.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/repeated_adt_name.bend --- Errors: -In tests/golden_tests/parse_file/repeated_adt_name.bend : +In tests/golden_tests/parse_file/repeated_adt_name.bend : Redefinition of type 'Foo'.  2 | type Foo = B  3 |  diff --git a/tests/snapshots/parse_file__repeated_datatype_name.bend.snap b/tests/snapshots/parse_file__repeated_datatype_name.bend.snap index ea0a83853..8e23b5667 100644 --- a/tests/snapshots/parse_file__repeated_datatype_name.bend.snap +++ b/tests/snapshots/parse_file__repeated_datatype_name.bend.snap @@ -3,5 +3,8 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/repeated_datatype_name.bend --- Errors: -In tests/golden_tests/parse_file/repeated_datatype_name.bend : -Found a repeated field 'Expr' in constructor Expr/Plus. +In tests/golden_tests/parse_file/repeated_datatype_name.bend : +- information: Found a repeated field 'Expr' in constructor Expr/Plus. +- location: + 3 | | (Plus Expr Expr) + diff --git a/tests/snapshots/parse_file__strange_pattern.bend.snap b/tests/snapshots/parse_file__strange_pattern.bend.snap index e15daa07b..70c43138f 100644 --- a/tests/snapshots/parse_file__strange_pattern.bend.snap +++ b/tests/snapshots/parse_file__strange_pattern.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/strange_pattern.bend --- Errors: -In tests/golden_tests/parse_file/strange_pattern.bend : +In tests/golden_tests/parse_file/strange_pattern.bend : - expected: pattern or '=' - detected:  1 | main & = (a b c) diff --git a/tests/snapshots/parse_file__tab.bend.snap b/tests/snapshots/parse_file__tab.bend.snap index 22c0aa835..a41337cec 100644 --- a/tests/snapshots/parse_file__tab.bend.snap +++ b/tests/snapshots/parse_file__tab.bend.snap @@ -3,6 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/tab.bend --- Errors: -In tests/golden_tests/parse_file/tab.bend : +In tests/golden_tests/parse_file/tab.bend : Tabs are not accepted for indentation.  2 |  x = 2 diff --git a/tests/snapshots/parse_file__tuple_need_parens.bend.snap b/tests/snapshots/parse_file__tuple_need_parens.bend.snap index 61bf25ac6..17e7cd8fc 100644 --- a/tests/snapshots/parse_file__tuple_need_parens.bend.snap +++ b/tests/snapshots/parse_file__tuple_need_parens.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/parse_file/tuple_need_parens.bend --- Errors: -In tests/golden_tests/parse_file/tuple_need_parens.bend : +In tests/golden_tests/parse_file/tuple_need_parens.bend : - expected: ':' - detected:  2 | if 1, 2: diff --git a/tests/snapshots/run_file__360_no_scope.bend.snap b/tests/snapshots/run_file__360_no_scope.bend.snap index 377c47cec..b3767b0a3 100644 --- a/tests/snapshots/run_file__360_no_scope.bend.snap +++ b/tests/snapshots/run_file__360_no_scope.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/360_no_scope.bend --- Errors: -In tests/golden_tests/run_file/360_no_scope.bend : +In tests/golden_tests/run_file/360_no_scope.bend : - expected: '=' - detected: end of input  6 |   diff --git a/tests/snapshots/run_file__adt_match_wrong_tag.bend.snap b/tests/snapshots/run_file__adt_match_wrong_tag.bend.snap index 3de3d1f92..c5e00e445 100644 --- a/tests/snapshots/run_file__adt_match_wrong_tag.bend.snap +++ b/tests/snapshots/run_file__adt_match_wrong_tag.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/adt_match_wrong_tag.bend --- Errors: -In tests/golden_tests/run_file/adt_match_wrong_tag.bend : +In tests/golden_tests/run_file/adt_match_wrong_tag.bend : - expected: term - detected: end of input  4 |   diff --git a/tests/snapshots/run_file__adt_wrong_tag.bend.snap b/tests/snapshots/run_file__adt_wrong_tag.bend.snap index 6de0184ea..548d7d018 100644 --- a/tests/snapshots/run_file__adt_wrong_tag.bend.snap +++ b/tests/snapshots/run_file__adt_wrong_tag.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/adt_wrong_tag.bend --- Errors: -In tests/golden_tests/run_file/adt_wrong_tag.bend : +In tests/golden_tests/run_file/adt_wrong_tag.bend : - expected: term - detected: end of input  4 |   diff --git a/tests/snapshots/run_file__def_bool_num.bend.snap b/tests/snapshots/run_file__def_bool_num.bend.snap index 27dbe0f04..5e3846d7a 100644 --- a/tests/snapshots/run_file__def_bool_num.bend.snap +++ b/tests/snapshots/run_file__def_bool_num.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/def_bool_num.bend --- Errors: +In tests/golden_tests/run_file/def_bool_num.bend : In definition 'go': Non-exhaustive pattern matching rule. Default case of number type not covered. diff --git a/tests/snapshots/run_file__def_num_bool.bend.snap b/tests/snapshots/run_file__def_num_bool.bend.snap index 420ccdb91..769d29c15 100644 --- a/tests/snapshots/run_file__def_num_bool.bend.snap +++ b/tests/snapshots/run_file__def_num_bool.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/def_num_bool.bend --- Errors: +In tests/golden_tests/run_file/def_num_bool.bend : In definition 'go': Non-exhaustive pattern matching rule. Default case of number type not covered. diff --git a/tests/snapshots/run_file__match_sup.bend.snap b/tests/snapshots/run_file__match_sup.bend.snap index 99a099b12..9db5fccfa 100644 --- a/tests/snapshots/run_file__match_sup.bend.snap +++ b/tests/snapshots/run_file__match_sup.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/match_sup.bend --- Errors: -In tests/golden_tests/run_file/match_sup.bend : +In tests/golden_tests/run_file/match_sup.bend : - expected: term - detected: end of input  7 |   diff --git a/tests/snapshots/run_file__match_vars.bend.snap b/tests/snapshots/run_file__match_vars.bend.snap index 866c9aefa..173e62ae2 100644 --- a/tests/snapshots/run_file__match_vars.bend.snap +++ b/tests/snapshots/run_file__match_vars.bend.snap @@ -3,6 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/match_vars.bend --- Errors: +In tests/golden_tests/run_file/match_vars.bend : In definition 'main': Irrefutable 'match' expression. All cases after variable pattern 'true' will be ignored. Note that to use a 'match' expression, the matched constructors need to be defined in a 'data' definition. diff --git a/tests/snapshots/run_file__nat_add.bend.snap b/tests/snapshots/run_file__nat_add.bend.snap index 31b36eaae..f2a2dc696 100644 --- a/tests/snapshots/run_file__nat_add.bend.snap +++ b/tests/snapshots/run_file__nat_add.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/nat_add.bend --- Errors: -In tests/golden_tests/run_file/nat_add.bend : +In tests/golden_tests/run_file/nat_add.bend : - expected: term - detected: end of input  5 |   diff --git a/tests/snapshots/run_file__nat_add_num.bend.snap b/tests/snapshots/run_file__nat_add_num.bend.snap index 343188ce8..5f333b67b 100644 --- a/tests/snapshots/run_file__nat_add_num.bend.snap +++ b/tests/snapshots/run_file__nat_add_num.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/nat_add_num.bend --- Errors: -In tests/golden_tests/run_file/nat_add_num.bend : +In tests/golden_tests/run_file/nat_add_num.bend : - expected: term - detected: end of input  5 |   diff --git a/tests/snapshots/run_file__num_match_missing_var.bend.snap b/tests/snapshots/run_file__num_match_missing_var.bend.snap index 1fb2700cc..eac6d9c5a 100644 --- a/tests/snapshots/run_file__num_match_missing_var.bend.snap +++ b/tests/snapshots/run_file__num_match_missing_var.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/num_match_missing_var.bend --- Errors: -In tests/golden_tests/run_file/num_match_missing_var.bend : +In tests/golden_tests/run_file/num_match_missing_var.bend : - expected: '}' - detected:  7 | _: f diff --git a/tests/snapshots/run_file__open_too_many_ctrs.bend.snap b/tests/snapshots/run_file__open_too_many_ctrs.bend.snap index 5cc1c09f6..8e6a53f97 100644 --- a/tests/snapshots/run_file__open_too_many_ctrs.bend.snap +++ b/tests/snapshots/run_file__open_too_many_ctrs.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/open_too_many_ctrs.bend --- Errors: +In tests/golden_tests/run_file/open_too_many_ctrs.bend : In definition 'main': Type 'MyTree' of an 'open' has more than one constructor diff --git a/tests/snapshots/run_file__open_undefined_type.bend.snap b/tests/snapshots/run_file__open_undefined_type.bend.snap index a942c18d9..73df03a33 100644 --- a/tests/snapshots/run_file__open_undefined_type.bend.snap +++ b/tests/snapshots/run_file__open_undefined_type.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/open_undefined_type.bend --- Errors: +In tests/golden_tests/run_file/open_undefined_type.bend : In definition 'main': Type 'MyType' of an 'open' is not defined diff --git a/tests/snapshots/run_file__override_list_ctr.bend.snap b/tests/snapshots/run_file__override_list_ctr.bend.snap index a0b5243a2..789f8c5ef 100644 --- a/tests/snapshots/run_file__override_list_ctr.bend.snap +++ b/tests/snapshots/run_file__override_list_ctr.bend.snap @@ -3,6 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/override_list_ctr.bend --- Errors: -In tests/golden_tests/run_file/override_list_ctr.bend : +In tests/golden_tests/run_file/override_list_ctr.bend : Redefinition of builtin (constructor) 'List/Nil'.  1 | List/Nil = * diff --git a/tests/snapshots/run_file__override_str_ctr.bend.snap b/tests/snapshots/run_file__override_str_ctr.bend.snap index 995b1c5d4..34d01367a 100644 --- a/tests/snapshots/run_file__override_str_ctr.bend.snap +++ b/tests/snapshots/run_file__override_str_ctr.bend.snap @@ -3,6 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/override_str_ctr.bend --- Errors: -In tests/golden_tests/run_file/override_str_ctr.bend : +In tests/golden_tests/run_file/override_str_ctr.bend : Redefinition of builtin (constructor) 'String/Cons'.  1 | String/Cons = * diff --git a/tests/snapshots/run_file__recursive_combinator_nested.bend.snap b/tests/snapshots/run_file__recursive_combinator_nested.bend.snap index e45fa3c3f..599376883 100644 --- a/tests/snapshots/run_file__recursive_combinator_nested.bend.snap +++ b/tests/snapshots/run_file__recursive_combinator_nested.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/recursive_combinator_nested.bend --- Errors: -In tests/golden_tests/run_file/recursive_combinator_nested.bend : +In tests/golden_tests/run_file/recursive_combinator_nested.bend : - expected: term - detected:  4 | _: a-1 diff --git a/tests/snapshots/run_file__str_inc.bend.snap b/tests/snapshots/run_file__str_inc.bend.snap index 265738757..edc1bc2fc 100644 --- a/tests/snapshots/run_file__str_inc.bend.snap +++ b/tests/snapshots/run_file__str_inc.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/str_inc.bend --- Errors: -In tests/golden_tests/run_file/str_inc.bend : +In tests/golden_tests/run_file/str_inc.bend : - expected: ')' - detected:  3 | (StrGo 0 str) = str diff --git a/tests/snapshots/run_file__str_inc_eta.bend.snap b/tests/snapshots/run_file__str_inc_eta.bend.snap index 89c3b993b..547ec8991 100644 --- a/tests/snapshots/run_file__str_inc_eta.bend.snap +++ b/tests/snapshots/run_file__str_inc_eta.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/str_inc_eta.bend --- Errors: -In tests/golden_tests/run_file/str_inc_eta.bend : +In tests/golden_tests/run_file/str_inc_eta.bend : - expected: ')' - detected:  3 | (StrGo 0 (head, tail)) = (head, tail) diff --git a/tests/snapshots/run_file__tagged_lam.bend.snap b/tests/snapshots/run_file__tagged_lam.bend.snap index 1f1db3205..11ade896f 100644 --- a/tests/snapshots/run_file__tagged_lam.bend.snap +++ b/tests/snapshots/run_file__tagged_lam.bend.snap @@ -3,7 +3,7 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/tagged_lam.bend --- Errors: -In tests/golden_tests/run_file/tagged_lam.bend : +In tests/golden_tests/run_file/tagged_lam.bend : - expected: term - detected: end of input  2 |   diff --git a/tests/snapshots/run_file__unbound_wrap.bend.snap b/tests/snapshots/run_file__unbound_wrap.bend.snap index a154a25f4..552967160 100644 --- a/tests/snapshots/run_file__unbound_wrap.bend.snap +++ b/tests/snapshots/run_file__unbound_wrap.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/unbound_wrap.bend --- Errors: +In tests/golden_tests/run_file/unbound_wrap.bend : In definition 'main': Reference to undefined function 'Maybe/wrap' diff --git a/tests/snapshots/run_file__unscoped_never_used.bend.snap b/tests/snapshots/run_file__unscoped_never_used.bend.snap index aeb93b145..4f5f367a1 100644 --- a/tests/snapshots/run_file__unscoped_never_used.bend.snap +++ b/tests/snapshots/run_file__unscoped_never_used.bend.snap @@ -3,5 +3,6 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/run_file/unscoped_never_used.bend --- Errors: +In tests/golden_tests/run_file/unscoped_never_used.bend : In definition 'main': Unscoped variable from lambda 'λ$x' is never used. diff --git a/tests/snapshots/simplify_matches__wrong_fn_arity.bend.snap b/tests/snapshots/simplify_matches__wrong_fn_arity.bend.snap index 0420c05e2..d75933f7f 100644 --- a/tests/snapshots/simplify_matches__wrong_fn_arity.bend.snap +++ b/tests/snapshots/simplify_matches__wrong_fn_arity.bend.snap @@ -3,6 +3,9 @@ source: tests/golden_tests.rs input_file: tests/golden_tests/simplify_matches/wrong_fn_arity.bend --- Errors: -File has no 'main' definition. +In tests/golden_tests/simplify_matches/wrong_fn_arity.bend : In definition 'Foo': Incorrect pattern matching rule arity. Expected 3 args, found 0. + +Other diagnostics: +File has no 'main' definition.