Skip to content

Commit

Permalink
improve error reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
SkymanOne committed Mar 25, 2024
1 parent f541ae2 commit e5d0525
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 38 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ clap = { version ="4.5", features = ["derive"]}
ariadne = { version = "0.4", features = ["auto-color"] }
anyhow = "1.0"
walkdir = "2.5"
yansi = "1.0"
strip-ansi-escapes = "0.2"
3 changes: 3 additions & 0 deletions auto/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Verifiable counter

This is a simple counter smart contract.
40 changes: 40 additions & 0 deletions auto/counter.fol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
state CounterState {
counter: int,
} st [
# example bounds
counter < 1000,
counter > -1000
]

# This is an constructor.
@init
# Anyone can call this function.
@(any)
fn () initialise() when () -> CounterState {
move CounterState : { 0.0 };
}

@(any)
fn () incr_by(value: int) when (CounterState s) -> CounterState
st [
value > 100,
value < 100
] {
let value = s.counter + value + a;
move CounterState : { value };
}

@(any)
fn () decr_by(value: int) when (CounterState s) -> CounterState
st [
value > 100,
value < 100
] {
let value = s.counter - value;
move CounterState : { value, s.counter };
}

@(any)
view(CounterState s) fn int get_value() {
return s.counter;
}
3 changes: 2 additions & 1 deletion crates/diagnostics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ version.workspace = true

[dependencies]
lalrpop-util = { workspace = true }
ariadne = { workspace = true }
ariadne = { workspace = true }
yansi = { workspace = true }
4 changes: 2 additions & 2 deletions crates/diagnostics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use std::{

pub type Span = Range<usize>;

pub use ariadne::{
pub use yansi::{
Color,
Fmt,
Paint,
};

#[derive(Debug, Clone)]
Expand Down
3 changes: 2 additions & 1 deletion crates/folidity/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ folidity-diagnostics = { workspace = true }
clap = { workspace = true }
ariadne = { workspace = true }
anyhow = { workspace = true }
walkdir = { workspace = true }
walkdir = { workspace = true }
yansi = { workspace = true }
2 changes: 1 addition & 1 deletion crates/folidity/src/cmd/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl CheckCommand {
&def.diagnostics,
self.contract.to_str().unwrap(),
);
anyhow::bail!("Error during checking")
anyhow::bail!("Syntactical checking failed.")
}
}
Err(errors) => {
Expand Down
3 changes: 2 additions & 1 deletion crates/folidity/src/cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use anyhow::{
};
use clap::Subcommand;
use folidity_diagnostics::Report;
use yansi::Paint;

use self::{
check::CheckCommand,
Expand Down Expand Up @@ -60,7 +61,7 @@ pub fn read_contract(path_str: &OsString) -> Result<String> {
pub fn build_report(content: &str, diagnostics: &[Report], file_name: &str) {
for r in diagnostics {
PrettyReport::build(r.level.clone().into(), file_name, r.loc.start)
.with_message(format!("{} detected.", r.error_type))
.with_message(format!("{} detected.", r.error_type.cyan()))
.with_label(
Label::new((file_name, r.loc.clone()))
.with_message(r.message.clone())
Expand Down
10 changes: 9 additions & 1 deletion crates/folidity/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use clap::Parser;
use cmd::Commands;
use yansi::{
Color,
Paint,
};

mod cmd;

Expand All @@ -15,7 +19,11 @@ fn main() {
match cli.command.run() {
Ok(()) => {}
Err(err) => {
eprintln!("{err:?}");
eprintln!(
"{} {}",
"ERROR:".fg(Color::Red).bold(),
err.to_string().fg(Color::Red)
);
std::process::exit(1);
}
}
Expand Down
5 changes: 4 additions & 1 deletion crates/semantics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ num-bigint = { workspace = true }
num-rational = { workspace = true }
num-traits = { workspace = true }
algonaut_core = { workspace = true }
hex = { workspace = true }
hex = { workspace = true }

[dev-dependencies]
strip-ansi-escapes = { workspace = true }
43 changes: 25 additions & 18 deletions crates/semantics/src/expression/complex.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Resolve complex expressions.

use folidity_diagnostics::Report;
use folidity_diagnostics::{
Paint,
Report,
};
use folidity_parser::{
ast::{
self as parsed_ast,
Expand Down Expand Up @@ -188,10 +191,7 @@ pub fn resolve_func_call(
) -> Result<Expression, ()> {
let func = find_func(ident, contract)?;
if func.params.len() != args.len() {
contract.diagnostics.push(Report::semantic_error(
loc.clone(),
String::from("Function call has invalid number of arguments."),
));
report_mismatched_args_len(&loc, func.params.len(), args.len(), contract);
return Err(());
}

Expand Down Expand Up @@ -598,10 +598,7 @@ pub fn resolve_struct_init(
fields.extend(model_decl.fields);

if fields.len() != args.len() {
contract.diagnostics.push(Report::semantic_error(
loc.clone(),
String::from("Invalid number of arguments."),
));
report_mismatched_args_len(&loc, fields.len(), args.len(), contract);
return Err(());
}
let (parsed_args, error_args) = parse_args(args, &fields, scope, contract);
Expand Down Expand Up @@ -643,10 +640,7 @@ pub fn resolve_struct_init(

let struct_decl = contract.structs[s.i].clone();
if struct_decl.fields.len() != args.len() {
contract.diagnostics.push(Report::semantic_error(
loc.clone(),
String::from("Invalid number of arguments."),
));
report_mismatched_args_len(&loc, struct_decl.fields.len(), args.len(), contract);
return Err(());
}
let (parsed_args, error_args) = parse_args(args, &struct_decl.fields, scope, contract);
Expand Down Expand Up @@ -709,10 +703,7 @@ pub fn resolve_struct_init(
let (parsed_args, parent) = match body {
StateBody::Raw(fields) => {
if fields.len() != args.len() {
contract.diagnostics.push(Report::semantic_error(
loc.clone(),
String::from("Invalid number of arguments."),
));
report_mismatched_args_len(&loc, fields.len(), args.len(), contract);
return Err(());
}
let (parsed_args, error_args) = parse_args(args, fields, scope, contract);
Expand Down Expand Up @@ -826,6 +817,22 @@ fn find_func(ident: &Identifier, contract: &mut ContractDefinition) -> Result<Fu
Ok(func.clone())
}

fn report_mismatched_args_len(
loc: &Span,
expected: usize,
got: usize,
contract: &mut ContractDefinition,
) {
contract.diagnostics.push(Report::semantic_error(
loc.clone(),
format!(
"Invalid number of arguments. Expected {}, got {}",
expected.green().bold(),
got.red().bold()
),
));
}

fn find_var(
ident: &Identifier,
contract: &mut ContractDefinition,
Expand All @@ -836,7 +843,7 @@ fn find_var(
ident.loc.clone(),
format!(
"`{}`: Variable is not declared or inaccessible.",
ident.name
ident.name.yellow().bold()
),
));
return Err(());
Expand Down
19 changes: 11 additions & 8 deletions crates/semantics/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,33 +246,36 @@ fn test_err_program() {

let contract = resolve_semantics(tree);
// assert_eq!(contract.diagnostics.len(), 0, "{:#?}", contract.diagnostics);
let mut errors = contract.diagnostics.iter();
let mut errors = contract
.diagnostics
.iter()
.map(|r| strip_ansi_escapes::strip_str(&r.message));
assert_eq!(
"Expected function to return a value of type bool",
&errors.next().unwrap().message
&errors.next().unwrap()
);
assert_eq!(
"Variable is immutable. Annotate with `mut` keyword to allow mutation.",
&errors.next().unwrap().message
&errors.next().unwrap()
);
assert_eq!(
"Mismatched types: expected to resolve to int, but expression can only resolve to string",
&errors.next().unwrap().message
&errors.next().unwrap()
);
assert_eq!(
"Mismatched types: expected to resolve to string, but expression can only resolve to int",
&errors.next().unwrap().message
&errors.next().unwrap()
);
assert_eq!(
"Mismatched types: expected to resolve to string, but expression can only resolve to int",
&errors.next().unwrap().message
&errors.next().unwrap()
);
assert_eq!(
"List elements appear to be of different types.",
&errors.next().unwrap().message
&errors.next().unwrap()
);
assert_eq!(
"Expected function to transition to states [StartState]",
&errors.next().unwrap().message
&errors.next().unwrap()
);
}
7 changes: 3 additions & 4 deletions crates/semantics/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ use crate::{
global_symbol::GlobalSymbol,
};
use folidity_diagnostics::{
Color,
Fmt,
Paint,
Report,
};
use folidity_parser::{
Expand Down Expand Up @@ -418,13 +417,13 @@ pub(super) fn report_type_mismatch(
contract: &mut ContractDefinition,
) {
let actual = actual.iter().fold(String::new(), |acc, x| {
format!("{}, {}", acc, x.display(contract).fg(Color::Magenta))
format!("{}, {}", acc, x.display(contract).cyan().bold())
});
contract.diagnostics.push(Report::type_error(
loc.clone(),
format!(
"Mismatched types: expected to resolve to {}, but expression can only resolve to {}",
expected.display(contract).fg(Color::Green),
expected.display(contract).magenta().bold(),
actual.trim_start_matches(", ")
),
));
Expand Down

0 comments on commit e5d0525

Please sign in to comment.