From 04f20b9283213a81e865111f4e2861941236bdd7 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Wed, 23 Aug 2023 16:59:58 +0100 Subject: [PATCH 01/34] test(should_fail) --- crates/lsp/src/lib.rs | 2 +- crates/nargo_cli/src/cli/test_cmd.rs | 64 +++++++++++++------ crates/noirc_frontend/src/ast/function.rs | 2 +- crates/noirc_frontend/src/hir/def_map/mod.rs | 14 +++- crates/noirc_frontend/src/hir/mod.rs | 17 +++-- .../src/hir/resolution/resolver.rs | 4 +- crates/noirc_frontend/src/lexer/token.rs | 20 ++++-- 7 files changed, 84 insertions(+), 39 deletions(-) diff --git a/crates/lsp/src/lib.rs b/crates/lsp/src/lib.rs index 802ecab5798..1cd2b79b5f2 100644 --- a/crates/lsp/src/lib.rs +++ b/crates/lsp/src/lib.rs @@ -199,7 +199,7 @@ fn on_code_lens_request( let tests = context .get_all_test_functions_in_crate_matching(&crate_id, FunctionNameMatch::Anything); - for (func_name, func_id) in tests { + for (func_name, func_id, _should_fail) in tests { let location = context.function_meta(&func_id).name.location; let file_id = location.file; diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index 94c8ff86dcd..cdf08fc0801 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -89,12 +89,20 @@ fn run_tests( let writer = StandardStream::stderr(ColorChoice::Always); let mut writer = writer.lock(); - for (test_name, test_function) in test_functions { + for (test_name, test_function, should_fail) in test_functions { write!(writer, "[{}] Testing {test_name}... ", package.name) .expect("Failed to write to stdout"); writer.flush().expect("Failed to flush writer"); - match run_test(backend, &test_name, test_function, &context, show_output, compile_options) { + match run_test( + backend, + &test_name, + test_function, + should_fail, + &context, + show_output, + compile_options, + ) { Ok(_) => { writer .set_color(ColorSpec::new().set_fg(Some(Color::Green))) @@ -124,29 +132,43 @@ fn run_test( backend: &B, test_name: &str, main: FuncId, + should_fail: bool, context: &Context, show_output: bool, config: &CompileOptions, ) -> Result<(), CliError> { - let mut program = compile_no_check(context, config, main).map_err(|err| { - noirc_errors::reporter::report_all(&context.file_manager, &[err], config.deny_warnings); - CliError::Generic(format!("Test '{test_name}' failed to compile")) - })?; - - // Note: We could perform this test using the unoptimized ACIR as generated by `compile_no_check`. - program.circuit = optimize_circuit(backend, program.circuit).unwrap().0; - - // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, - // otherwise constraints involving these expressions will not error. - match execute_circuit(backend, program.circuit, WitnessMap::new(), show_output) { - Ok(_) => Ok(()), - Err(error) => { - let writer = StandardStream::stderr(ColorChoice::Always); - let mut writer = writer.lock(); - writer.set_color(ColorSpec::new().set_fg(Some(Color::Red))).ok(); - writeln!(writer, "failed").ok(); - writer.reset().ok(); - Err(error.into()) + if !should_fail { + let mut program = compile_no_check(context, config, main).map_err(|err| { + noirc_errors::reporter::report_all(&context.file_manager, &[err], config.deny_warnings); + CliError::Generic(format!("Test '{test_name}' failed to compile")) + })?; + + // Note: We could perform this test using the unoptimized ACIR as generated by `compile_no_check`. + program.circuit = optimize_circuit(backend, program.circuit).unwrap().0; + + // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, + // otherwise constraints involving these expressions will not error. + match execute_circuit(backend, program.circuit, WitnessMap::new(), show_output) { + Ok(_) => Ok(()), + Err(error) => { + let writer = StandardStream::stderr(ColorChoice::Always); + let mut writer = writer.lock(); + writer.set_color(ColorSpec::new().set_fg(Some(Color::Red))).ok(); + writeln!(writer, "failed").ok(); + writer.reset().ok(); + Err(error.into()) + } + } + } else { + let program = compile_no_check(context, config, main); + if program.is_err() { + return Ok(()); + } + let mut program = program.unwrap(); + program.circuit = optimize_circuit(backend, program.circuit).unwrap().0; + match execute_circuit(backend, program.circuit, WitnessMap::new(), show_output) { + Ok(_) => Err(CliError::Generic(format!("Test '{test_name}' should fail"))), + Err(_) => Ok(()), } } } diff --git a/crates/noirc_frontend/src/ast/function.rs b/crates/noirc_frontend/src/ast/function.rs index 377e6aa77ad..001687e10a0 100644 --- a/crates/noirc_frontend/src/ast/function.rs +++ b/crates/noirc_frontend/src/ast/function.rs @@ -83,7 +83,7 @@ impl From for NoirFunction { let kind = match fd.attribute { Some(Attribute::Builtin(_)) => FunctionKind::Builtin, Some(Attribute::Foreign(_)) => FunctionKind::LowLevel, - Some(Attribute::Test) => FunctionKind::Normal, + Some(Attribute::Test(_)) => FunctionKind::Normal, Some(Attribute::Oracle(_)) => FunctionKind::Oracle, Some(Attribute::Deprecated(_)) | None => FunctionKind::Normal, Some(Attribute::Custom(_)) => FunctionKind::Normal, diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index 2dc8c5ec96f..9a90920eb2e 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -129,12 +129,22 @@ impl CrateDefMap { pub fn get_all_test_functions<'a>( &'a self, interner: &'a NodeInterner, - ) -> impl Iterator + 'a { + ) -> impl Iterator + 'a { self.modules.iter().flat_map(|(_, module)| { module .value_definitions() .filter_map(|id| id.as_function()) - .filter(|id| interner.function_meta(id).attributes == Some(Attribute::Test)) + .filter(|id| { + interner.function_meta(id).attributes == Some(Attribute::Test(true)) + || interner.function_meta(id).attributes == Some(Attribute::Test(false)) + }) + .map(|id| { + if interner.function_meta(&id).attributes == Some(Attribute::Test(true)) { + (id, true) + } else { + (id, false) + } + }) }) } diff --git a/crates/noirc_frontend/src/hir/mod.rs b/crates/noirc_frontend/src/hir/mod.rs index d0b24e90a76..84869ab124e 100644 --- a/crates/noirc_frontend/src/hir/mod.rs +++ b/crates/noirc_frontend/src/hir/mod.rs @@ -107,22 +107,21 @@ impl Context { &self, crate_id: &CrateId, pattern: FunctionNameMatch, - ) -> Vec<(String, FuncId)> { + ) -> Vec<(String, FuncId, bool)> { let interner = &self.def_interner; let def_map = self.def_map(crate_id).expect("The local crate should be analyzed already"); def_map .get_all_test_functions(interner) - .filter_map(|id| { + .filter_map(|(id, not_fail)| { let fully_qualified_name = self.fully_qualified_function_name(crate_id, &id); match &pattern { - FunctionNameMatch::Anything => Some((fully_qualified_name, id)), - FunctionNameMatch::Exact(pattern) => { - (&fully_qualified_name == pattern).then_some((fully_qualified_name, id)) - } - FunctionNameMatch::Contains(pattern) => { - fully_qualified_name.contains(pattern).then_some((fully_qualified_name, id)) - } + FunctionNameMatch::Anything => Some((fully_qualified_name, id, not_fail)), + FunctionNameMatch::Exact(pattern) => (&fully_qualified_name == pattern) + .then_some((fully_qualified_name, id, not_fail)), + FunctionNameMatch::Contains(pattern) => fully_qualified_name + .contains(pattern) + .then_some((fully_qualified_name, id, not_fail)), } }) .collect() diff --git a/crates/noirc_frontend/src/hir/resolution/resolver.rs b/crates/noirc_frontend/src/hir/resolution/resolver.rs index 6ce06d4a1ae..24962b70aeb 100644 --- a/crates/noirc_frontend/src/hir/resolution/resolver.rs +++ b/crates/noirc_frontend/src/hir/resolution/resolver.rs @@ -700,7 +700,9 @@ impl<'a> Resolver<'a> { self.push_err(ResolverError::DistinctNotAllowed { ident: func.name_ident().clone() }); } - if attributes == Some(Attribute::Test) && !parameters.is_empty() { + if (attributes == Some(Attribute::Test(true)) || attributes == Some(Attribute::Test(false))) + && !parameters.is_empty() + { self.push_err(ResolverError::TestFunctionHasParameters { span: func.name_ident().span(), }); diff --git a/crates/noirc_frontend/src/lexer/token.rs b/crates/noirc_frontend/src/lexer/token.rs index 6291ac4de12..8227a078a25 100644 --- a/crates/noirc_frontend/src/lexer/token.rs +++ b/crates/noirc_frontend/src/lexer/token.rs @@ -325,7 +325,8 @@ pub enum Attribute { Builtin(String), Oracle(String), Deprecated(Option), - Test, + // Boolean flag if the test should fail + Test(bool), Custom(String), } @@ -335,7 +336,8 @@ impl fmt::Display for Attribute { Attribute::Foreign(ref k) => write!(f, "#[foreign({k})]"), Attribute::Builtin(ref k) => write!(f, "#[builtin({k})]"), Attribute::Oracle(ref k) => write!(f, "#[oracle({k})]"), - Attribute::Test => write!(f, "#[test]"), + Attribute::Test(false) => write!(f, "#[test]"), + Attribute::Test(true) => write!(f, "#[test(should_fail)]"), Attribute::Deprecated(None) => write!(f, "#[deprecated]"), Attribute::Deprecated(Some(ref note)) => write!(f, r#"#[deprecated("{note}")]"#), Attribute::Custom(ref k) => write!(f, "#[{k}]"), @@ -391,7 +393,17 @@ impl Attribute { Attribute::Deprecated(name.trim_matches('"').to_string().into()) } - ["test"] => Attribute::Test, + ["test"] => Attribute::Test(false), + ["test", name] => { + if name != &"should_fail" { + return Err(LexerErrorKind::MalformedFuncAttribute { + span, + found: word.to_owned(), + }); + } + + Attribute::Test(true) + } tokens => { tokens.iter().try_for_each(|token| validate(token))?; Attribute::Custom(word.to_owned()) @@ -431,7 +443,7 @@ impl AsRef for Attribute { Attribute::Builtin(string) => string, Attribute::Oracle(string) => string, Attribute::Deprecated(Some(string)) => string, - Attribute::Test | Attribute::Deprecated(None) => "", + Attribute::Test(_) | Attribute::Deprecated(None) => "", Attribute::Custom(string) => string, } } From d6848795e03e3904bd8495c4d9779252e0db0155 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Wed, 23 Aug 2023 17:10:40 +0100 Subject: [PATCH 02/34] update comment --- crates/noirc_frontend/src/lexer/token.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/noirc_frontend/src/lexer/token.rs b/crates/noirc_frontend/src/lexer/token.rs index 8227a078a25..6449e2f6a63 100644 --- a/crates/noirc_frontend/src/lexer/token.rs +++ b/crates/noirc_frontend/src/lexer/token.rs @@ -325,7 +325,7 @@ pub enum Attribute { Builtin(String), Oracle(String), Deprecated(Option), - // Boolean flag if the test should fail + // Boolean flag that represents if the test should fail Test(bool), Custom(String), } From b8dc4f51228affc5f2a19154dbd215f2dbf65977 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Wed, 23 Aug 2023 19:23:44 +0100 Subject: [PATCH 03/34] make only unsatisfied constraint success --- crates/nargo_cli/src/cli/test_cmd.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index cdf08fc0801..6ad89f4763a 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -161,14 +161,26 @@ fn run_test( } } else { let program = compile_no_check(context, config, main); - if program.is_err() { - return Ok(()); - } - let mut program = program.unwrap(); - program.circuit = optimize_circuit(backend, program.circuit).unwrap().0; - match execute_circuit(backend, program.circuit, WitnessMap::new(), show_output) { - Ok(_) => Err(CliError::Generic(format!("Test '{test_name}' should fail"))), - Err(_) => Ok(()), + match program { + Ok(mut program) => { + program.circuit = optimize_circuit(backend, program.circuit).unwrap().0; + match execute_circuit(backend, program.circuit, WitnessMap::new(), show_output) { + Ok(_) => Err(CliError::Generic(format!("Test '{test_name}' should fail"))), + Err(_) => Ok(()), + } + } + Err(err) => { + if !err.diagnostic.message.contains("Failed constraint") { + noirc_errors::reporter::report_all( + &context.file_manager, + &[err], + config.deny_warnings, + ); + Err(CliError::Generic(format!("Test '{test_name}' failed to compile"))) + } else { + Ok(()) + } + } } } } From 54137b1d239f616c7d27c49105405444ba7d1be8 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Wed, 23 Aug 2023 19:31:03 +0100 Subject: [PATCH 04/34] testfunc type alias to replace tuple --- crates/lsp/src/lib.rs | 4 ++-- crates/nargo_cli/src/cli/test_cmd.rs | 6 +++--- crates/noirc_frontend/src/hir/def_map/mod.rs | 4 ++-- crates/noirc_frontend/src/hir/mod.rs | 10 +++++----- crates/noirc_frontend/src/node_interner.rs | 2 ++ 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/crates/lsp/src/lib.rs b/crates/lsp/src/lib.rs index 1cd2b79b5f2..64a60b7964d 100644 --- a/crates/lsp/src/lib.rs +++ b/crates/lsp/src/lib.rs @@ -199,8 +199,8 @@ fn on_code_lens_request( let tests = context .get_all_test_functions_in_crate_matching(&crate_id, FunctionNameMatch::Anything); - for (func_name, func_id, _should_fail) in tests { - let location = context.function_meta(&func_id).name.location; + for (func_name, test_function) in tests { + let location = context.function_meta(&test_function.0).name.location; let file_id = location.file; // Ignore diagnostics for any file that wasn't the file we saved diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index 6ad89f4763a..288841d3581 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -89,7 +89,7 @@ fn run_tests( let writer = StandardStream::stderr(ColorChoice::Always); let mut writer = writer.lock(); - for (test_name, test_function, should_fail) in test_functions { + for (test_name, test_function) in test_functions { write!(writer, "[{}] Testing {test_name}... ", package.name) .expect("Failed to write to stdout"); writer.flush().expect("Failed to flush writer"); @@ -97,8 +97,8 @@ fn run_tests( match run_test( backend, &test_name, - test_function, - should_fail, + test_function.0, + test_function.1, &context, show_output, compile_options, diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index 9a90920eb2e..e31214a425d 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -1,7 +1,7 @@ use crate::graph::CrateId; use crate::hir::def_collector::dc_crate::DefCollector; use crate::hir::Context; -use crate::node_interner::{FuncId, NodeInterner}; +use crate::node_interner::{FuncId, NodeInterner, TestFunc}; use crate::parser::{parse_program, ParsedModule}; use crate::token::Attribute; use arena::{Arena, Index}; @@ -129,7 +129,7 @@ impl CrateDefMap { pub fn get_all_test_functions<'a>( &'a self, interner: &'a NodeInterner, - ) -> impl Iterator + 'a { + ) -> impl Iterator + 'a { self.modules.iter().flat_map(|(_, module)| { module .value_definitions() diff --git a/crates/noirc_frontend/src/hir/mod.rs b/crates/noirc_frontend/src/hir/mod.rs index 84869ab124e..128902f070f 100644 --- a/crates/noirc_frontend/src/hir/mod.rs +++ b/crates/noirc_frontend/src/hir/mod.rs @@ -6,7 +6,7 @@ pub mod type_check; use crate::graph::{CrateGraph, CrateId}; use crate::hir_def::function::FuncMeta; -use crate::node_interner::{FuncId, NodeInterner}; +use crate::node_interner::{FuncId, NodeInterner, TestFunc}; use def_map::{Contract, CrateDefMap}; use fm::FileManager; use std::collections::HashMap; @@ -107,7 +107,7 @@ impl Context { &self, crate_id: &CrateId, pattern: FunctionNameMatch, - ) -> Vec<(String, FuncId, bool)> { + ) -> Vec<(String, TestFunc)> { let interner = &self.def_interner; let def_map = self.def_map(crate_id).expect("The local crate should be analyzed already"); @@ -116,12 +116,12 @@ impl Context { .filter_map(|(id, not_fail)| { let fully_qualified_name = self.fully_qualified_function_name(crate_id, &id); match &pattern { - FunctionNameMatch::Anything => Some((fully_qualified_name, id, not_fail)), + FunctionNameMatch::Anything => Some((fully_qualified_name, (id, not_fail))), FunctionNameMatch::Exact(pattern) => (&fully_qualified_name == pattern) - .then_some((fully_qualified_name, id, not_fail)), + .then_some((fully_qualified_name, (id, not_fail))), FunctionNameMatch::Contains(pattern) => fully_qualified_name .contains(pattern) - .then_some((fully_qualified_name, id, not_fail)), + .then_some((fully_qualified_name, (id, not_fail))), } }) .collect() diff --git a/crates/noirc_frontend/src/node_interner.rs b/crates/noirc_frontend/src/node_interner.rs index 5a45cfee42b..155a42cbafc 100644 --- a/crates/noirc_frontend/src/node_interner.rs +++ b/crates/noirc_frontend/src/node_interner.rs @@ -138,6 +138,8 @@ impl FuncId { } } +pub type TestFunc = (FuncId, /* should fail */ bool); + #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] pub struct StructId(pub ModuleId); From 5ee5dfb7b381b2ade35c2210f9a9ddbf7207bc22 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Wed, 23 Aug 2023 19:32:36 +0100 Subject: [PATCH 05/34] inline comment --- crates/noirc_frontend/src/lexer/token.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/noirc_frontend/src/lexer/token.rs b/crates/noirc_frontend/src/lexer/token.rs index 6449e2f6a63..7ad0d7f198d 100644 --- a/crates/noirc_frontend/src/lexer/token.rs +++ b/crates/noirc_frontend/src/lexer/token.rs @@ -325,8 +325,7 @@ pub enum Attribute { Builtin(String), Oracle(String), Deprecated(Option), - // Boolean flag that represents if the test should fail - Test(bool), + Test(/* should fail: */ bool), Custom(String), } From 2268b1e43b831302897d402afe20c7cd6d3c47c7 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Wed, 23 Aug 2023 19:35:51 +0100 Subject: [PATCH 06/34] change to expect failure --- crates/noirc_frontend/src/lexer/token.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/noirc_frontend/src/lexer/token.rs b/crates/noirc_frontend/src/lexer/token.rs index 7ad0d7f198d..939d88ac0a8 100644 --- a/crates/noirc_frontend/src/lexer/token.rs +++ b/crates/noirc_frontend/src/lexer/token.rs @@ -325,7 +325,7 @@ pub enum Attribute { Builtin(String), Oracle(String), Deprecated(Option), - Test(/* should fail: */ bool), + Test(/* expect failure: */ bool), Custom(String), } From 4c5145f979a606c854523ca4883d6b4fae5b079e Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Wed, 23 Aug 2023 21:07:06 +0100 Subject: [PATCH 07/34] change Test(bool) to Test{} --- crates/noirc_frontend/src/ast/function.rs | 2 +- crates/noirc_frontend/src/hir/def_map/mod.rs | 10 +++++++--- .../src/hir/resolution/resolver.rs | 3 ++- crates/noirc_frontend/src/lexer/token.rs | 17 +++++++++++------ 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/crates/noirc_frontend/src/ast/function.rs b/crates/noirc_frontend/src/ast/function.rs index 001687e10a0..cedb48b1714 100644 --- a/crates/noirc_frontend/src/ast/function.rs +++ b/crates/noirc_frontend/src/ast/function.rs @@ -83,7 +83,7 @@ impl From for NoirFunction { let kind = match fd.attribute { Some(Attribute::Builtin(_)) => FunctionKind::Builtin, Some(Attribute::Foreign(_)) => FunctionKind::LowLevel, - Some(Attribute::Test(_)) => FunctionKind::Normal, + Some(Attribute::Test { .. }) => FunctionKind::Normal, Some(Attribute::Oracle(_)) => FunctionKind::Oracle, Some(Attribute::Deprecated(_)) | None => FunctionKind::Normal, Some(Attribute::Custom(_)) => FunctionKind::Normal, diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index e31214a425d..3d6c0078838 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -135,11 +135,15 @@ impl CrateDefMap { .value_definitions() .filter_map(|id| id.as_function()) .filter(|id| { - interner.function_meta(id).attributes == Some(Attribute::Test(true)) - || interner.function_meta(id).attributes == Some(Attribute::Test(false)) + interner.function_meta(id).attributes + == Some(Attribute::Test { expect_failure: true }) + || interner.function_meta(id).attributes + == Some(Attribute::Test { expect_failure: true }) }) .map(|id| { - if interner.function_meta(&id).attributes == Some(Attribute::Test(true)) { + if interner.function_meta(&id).attributes + == Some(Attribute::Test { expect_failure: true }) + { (id, true) } else { (id, false) diff --git a/crates/noirc_frontend/src/hir/resolution/resolver.rs b/crates/noirc_frontend/src/hir/resolution/resolver.rs index 24962b70aeb..a54aca25ba2 100644 --- a/crates/noirc_frontend/src/hir/resolution/resolver.rs +++ b/crates/noirc_frontend/src/hir/resolution/resolver.rs @@ -700,7 +700,8 @@ impl<'a> Resolver<'a> { self.push_err(ResolverError::DistinctNotAllowed { ident: func.name_ident().clone() }); } - if (attributes == Some(Attribute::Test(true)) || attributes == Some(Attribute::Test(false))) + if ((attributes == Some(Attribute::Test { expect_failure: true })) + || (attributes == Some(Attribute::Test { expect_failure: false }))) && !parameters.is_empty() { self.push_err(ResolverError::TestFunctionHasParameters { diff --git a/crates/noirc_frontend/src/lexer/token.rs b/crates/noirc_frontend/src/lexer/token.rs index 939d88ac0a8..b3d54a941a8 100644 --- a/crates/noirc_frontend/src/lexer/token.rs +++ b/crates/noirc_frontend/src/lexer/token.rs @@ -325,7 +325,7 @@ pub enum Attribute { Builtin(String), Oracle(String), Deprecated(Option), - Test(/* expect failure: */ bool), + Test { expect_failure: bool }, Custom(String), } @@ -335,8 +335,13 @@ impl fmt::Display for Attribute { Attribute::Foreign(ref k) => write!(f, "#[foreign({k})]"), Attribute::Builtin(ref k) => write!(f, "#[builtin({k})]"), Attribute::Oracle(ref k) => write!(f, "#[oracle({k})]"), - Attribute::Test(false) => write!(f, "#[test]"), - Attribute::Test(true) => write!(f, "#[test(should_fail)]"), + Attribute::Test { expect_failure } => { + if expect_failure == false { + write!(f, "#[test]") + } else { + write!(f, "#[test(should_fail)]") + } + } Attribute::Deprecated(None) => write!(f, "#[deprecated]"), Attribute::Deprecated(Some(ref note)) => write!(f, r#"#[deprecated("{note}")]"#), Attribute::Custom(ref k) => write!(f, "#[{k}]"), @@ -392,7 +397,7 @@ impl Attribute { Attribute::Deprecated(name.trim_matches('"').to_string().into()) } - ["test"] => Attribute::Test(false), + ["test"] => Attribute::Test { expect_failure: false }, ["test", name] => { if name != &"should_fail" { return Err(LexerErrorKind::MalformedFuncAttribute { @@ -401,7 +406,7 @@ impl Attribute { }); } - Attribute::Test(true) + Attribute::Test { expect_failure: true } } tokens => { tokens.iter().try_for_each(|token| validate(token))?; @@ -442,7 +447,7 @@ impl AsRef for Attribute { Attribute::Builtin(string) => string, Attribute::Oracle(string) => string, Attribute::Deprecated(Some(string)) => string, - Attribute::Test(_) | Attribute::Deprecated(None) => "", + Attribute::Test { .. } | Attribute::Deprecated(None) => "", Attribute::Custom(string) => string, } } From 4bd422e25fb9657bd10f1f5d1b99b4caf0803284 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Wed, 23 Aug 2023 21:20:51 +0100 Subject: [PATCH 08/34] clippy --- crates/noirc_frontend/src/lexer/token.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/noirc_frontend/src/lexer/token.rs b/crates/noirc_frontend/src/lexer/token.rs index b3d54a941a8..f16f46fa5d1 100644 --- a/crates/noirc_frontend/src/lexer/token.rs +++ b/crates/noirc_frontend/src/lexer/token.rs @@ -336,7 +336,7 @@ impl fmt::Display for Attribute { Attribute::Builtin(ref k) => write!(f, "#[builtin({k})]"), Attribute::Oracle(ref k) => write!(f, "#[oracle({k})]"), Attribute::Test { expect_failure } => { - if expect_failure == false { + if !expect_failure { write!(f, "#[test]") } else { write!(f, "#[test(should_fail)]") From 09b07994e0b2dcf2e12f32cb738fd3494b35f7f9 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Wed, 23 Aug 2023 21:28:42 +0100 Subject: [PATCH 09/34] remove duplication --- crates/nargo_cli/src/cli/test_cmd.rs | 58 ++++++++++++++-------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index 288841d3581..cc6c587ab30 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -137,39 +137,34 @@ fn run_test( show_output: bool, config: &CompileOptions, ) -> Result<(), CliError> { - if !should_fail { - let mut program = compile_no_check(context, config, main).map_err(|err| { - noirc_errors::reporter::report_all(&context.file_manager, &[err], config.deny_warnings); - CliError::Generic(format!("Test '{test_name}' failed to compile")) - })?; - - // Note: We could perform this test using the unoptimized ACIR as generated by `compile_no_check`. - program.circuit = optimize_circuit(backend, program.circuit).unwrap().0; - - // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, - // otherwise constraints involving these expressions will not error. - match execute_circuit(backend, program.circuit, WitnessMap::new(), show_output) { - Ok(_) => Ok(()), - Err(error) => { - let writer = StandardStream::stderr(ColorChoice::Always); - let mut writer = writer.lock(); - writer.set_color(ColorSpec::new().set_fg(Some(Color::Red))).ok(); - writeln!(writer, "failed").ok(); - writer.reset().ok(); - Err(error.into()) - } - } - } else { - let program = compile_no_check(context, config, main); - match program { - Ok(mut program) => { - program.circuit = optimize_circuit(backend, program.circuit).unwrap().0; + let program = compile_no_check(context, config, main); + match program { + Ok(mut program) => { + // Note: We could perform this test using the unoptimized ACIR as generated by `compile_no_check`. + program.circuit = optimize_circuit(backend, program.circuit).unwrap().0; + if should_fail { match execute_circuit(backend, program.circuit, WitnessMap::new(), show_output) { Ok(_) => Err(CliError::Generic(format!("Test '{test_name}' should fail"))), Err(_) => Ok(()), } + } else { + // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, + // otherwise constraints involving these expressions will not error. + match execute_circuit(backend, program.circuit, WitnessMap::new(), show_output) { + Ok(_) => Ok(()), + Err(error) => { + let writer = StandardStream::stderr(ColorChoice::Always); + let mut writer = writer.lock(); + writer.set_color(ColorSpec::new().set_fg(Some(Color::Red))).ok(); + writeln!(writer, "failed").ok(); + writer.reset().ok(); + Err(error.into()) + } + } } - Err(err) => { + } + Err(err) => { + if should_fail { if !err.diagnostic.message.contains("Failed constraint") { noirc_errors::reporter::report_all( &context.file_manager, @@ -180,6 +175,13 @@ fn run_test( } else { Ok(()) } + } else { + noirc_errors::reporter::report_all( + &context.file_manager, + &[err], + config.deny_warnings, + ); + Err(CliError::Generic(format!("Test '{test_name}' failed to compile"))) } } } From 148ab5c69201d33e1de59693df10f5e53649a1de Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Wed, 23 Aug 2023 21:31:14 +0100 Subject: [PATCH 10/34] remove duplication --- crates/nargo_cli/src/cli/test_cmd.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index cc6c587ab30..8633fec7247 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -137,6 +137,10 @@ fn run_test( show_output: bool, config: &CompileOptions, ) -> Result<(), CliError> { + let report_error = |err| { + noirc_errors::reporter::report_all(&context.file_manager, &[err], config.deny_warnings); + Err(CliError::Generic(format!("Test '{test_name}' failed to compile"))) + }; let program = compile_no_check(context, config, main); match program { Ok(mut program) => { @@ -166,22 +170,12 @@ fn run_test( Err(err) => { if should_fail { if !err.diagnostic.message.contains("Failed constraint") { - noirc_errors::reporter::report_all( - &context.file_manager, - &[err], - config.deny_warnings, - ); - Err(CliError::Generic(format!("Test '{test_name}' failed to compile"))) + report_error(err) } else { Ok(()) } } else { - noirc_errors::reporter::report_all( - &context.file_manager, - &[err], - config.deny_warnings, - ); - Err(CliError::Generic(format!("Test '{test_name}' failed to compile"))) + report_error(err) } } } From f165f48f2b520d59b43e5c2dbf672fc6b39d0b9e Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Wed, 23 Aug 2023 21:33:31 +0100 Subject: [PATCH 11/34] remove duplication --- crates/nargo_cli/src/cli/test_cmd.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index 8633fec7247..200150521ad 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -146,15 +146,17 @@ fn run_test( Ok(mut program) => { // Note: We could perform this test using the unoptimized ACIR as generated by `compile_no_check`. program.circuit = optimize_circuit(backend, program.circuit).unwrap().0; + // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, + // otherwise constraints involving these expressions will not error. + let circuit_execution = + execute_circuit(backend, program.circuit, WitnessMap::new(), show_output); if should_fail { - match execute_circuit(backend, program.circuit, WitnessMap::new(), show_output) { + match circuit_execution { Ok(_) => Err(CliError::Generic(format!("Test '{test_name}' should fail"))), Err(_) => Ok(()), } } else { - // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, - // otherwise constraints involving these expressions will not error. - match execute_circuit(backend, program.circuit, WitnessMap::new(), show_output) { + match circuit_execution { Ok(_) => Ok(()), Err(error) => { let writer = StandardStream::stderr(ColorChoice::Always); From 52ca47fdbccd536158f7ec12d02fc6177b4fe4dd Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Wed, 23 Aug 2023 21:34:35 +0100 Subject: [PATCH 12/34] add some spacing --- crates/nargo_cli/src/cli/test_cmd.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index 200150521ad..c4d9640a0ba 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -141,15 +141,18 @@ fn run_test( noirc_errors::reporter::report_all(&context.file_manager, &[err], config.deny_warnings); Err(CliError::Generic(format!("Test '{test_name}' failed to compile"))) }; + let program = compile_no_check(context, config, main); match program { Ok(mut program) => { // Note: We could perform this test using the unoptimized ACIR as generated by `compile_no_check`. program.circuit = optimize_circuit(backend, program.circuit).unwrap().0; + // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, // otherwise constraints involving these expressions will not error. let circuit_execution = execute_circuit(backend, program.circuit, WitnessMap::new(), show_output); + if should_fail { match circuit_execution { Ok(_) => Err(CliError::Generic(format!("Test '{test_name}' should fail"))), From 131eec53349d90bcd8aacf10b71ea44c71c78ffe Mon Sep 17 00:00:00 2001 From: Ethan-000 Date: Wed, 23 Aug 2023 21:35:09 +0100 Subject: [PATCH 13/34] Update crates/noirc_frontend/src/hir/resolution/resolver.rs Co-authored-by: Blaine Bublitz --- crates/noirc_frontend/src/hir/resolution/resolver.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/noirc_frontend/src/hir/resolution/resolver.rs b/crates/noirc_frontend/src/hir/resolution/resolver.rs index a54aca25ba2..47d624e028e 100644 --- a/crates/noirc_frontend/src/hir/resolution/resolver.rs +++ b/crates/noirc_frontend/src/hir/resolution/resolver.rs @@ -700,9 +700,7 @@ impl<'a> Resolver<'a> { self.push_err(ResolverError::DistinctNotAllowed { ident: func.name_ident().clone() }); } - if ((attributes == Some(Attribute::Test { expect_failure: true })) - || (attributes == Some(Attribute::Test { expect_failure: false }))) - && !parameters.is_empty() + if matches!(attributes, Some(Attribute::Test { .. })) && !parameters.is_empty() { self.push_err(ResolverError::TestFunctionHasParameters { span: func.name_ident().span(), From 83a281e8c20511a0980c9bb16ca2ffbb4653e3f7 Mon Sep 17 00:00:00 2001 From: Ethan-000 Date: Wed, 23 Aug 2023 21:35:19 +0100 Subject: [PATCH 14/34] Update crates/noirc_frontend/src/hir/def_map/mod.rs Co-authored-by: Blaine Bublitz --- crates/noirc_frontend/src/hir/def_map/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index 3d6c0078838..f0457f14457 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -135,10 +135,7 @@ impl CrateDefMap { .value_definitions() .filter_map(|id| id.as_function()) .filter(|id| { - interner.function_meta(id).attributes - == Some(Attribute::Test { expect_failure: true }) - || interner.function_meta(id).attributes - == Some(Attribute::Test { expect_failure: true }) + matches!(interner.function_meta(id).attributes, Some(Attribute::Test { .. })) }) .map(|id| { if interner.function_meta(&id).attributes From f550b302204d9ba9b2a6c63c01521ac19679caf3 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Wed, 23 Aug 2023 21:44:50 +0100 Subject: [PATCH 15/34] make testfunc a struct --- crates/lsp/src/lib.rs | 2 +- crates/nargo_cli/src/cli/test_cmd.rs | 4 ++-- crates/noirc_frontend/src/hir/def_map/mod.rs | 25 +++++++++++++++++--- crates/noirc_frontend/src/hir/mod.rs | 15 +++++++----- crates/noirc_frontend/src/node_interner.rs | 2 -- 5 files changed, 34 insertions(+), 14 deletions(-) diff --git a/crates/lsp/src/lib.rs b/crates/lsp/src/lib.rs index 64a60b7964d..7e48f4b4d58 100644 --- a/crates/lsp/src/lib.rs +++ b/crates/lsp/src/lib.rs @@ -200,7 +200,7 @@ fn on_code_lens_request( .get_all_test_functions_in_crate_matching(&crate_id, FunctionNameMatch::Anything); for (func_name, test_function) in tests { - let location = context.function_meta(&test_function.0).name.location; + let location = context.function_meta(&test_function.get_id()).name.location; let file_id = location.file; // Ignore diagnostics for any file that wasn't the file we saved diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index c4d9640a0ba..c2a1abd2508 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -97,8 +97,8 @@ fn run_tests( match run_test( backend, &test_name, - test_function.0, - test_function.1, + test_function.get_id(), + test_function.get_expecte_failure_flag(), &context, show_output, compile_options, diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index f0457f14457..9b5ef3ef795 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -1,7 +1,7 @@ use crate::graph::CrateId; use crate::hir::def_collector::dc_crate::DefCollector; use crate::hir::Context; -use crate::node_interner::{FuncId, NodeInterner, TestFunc}; +use crate::node_interner::{FuncId, NodeInterner}; use crate::parser::{parse_program, ParsedModule}; use crate::token::Attribute; use arena::{Arena, Index}; @@ -141,9 +141,9 @@ impl CrateDefMap { if interner.function_meta(&id).attributes == Some(Attribute::Test { expect_failure: true }) { - (id, true) + TestFunc::new(id, true) } else { - (id, false) + TestFunc::new(id, false) } }) }) @@ -232,3 +232,22 @@ impl std::ops::IndexMut for CrateDefMap { &mut self.modules[local_module_id.0] } } + +pub struct TestFunc { + id: FuncId, + expect_failure: bool, +} + +impl TestFunc { + fn new(id: FuncId, expect_failure: bool) -> Self { + TestFunc { id, expect_failure } + } + + pub fn get_id(&self) -> FuncId { + self.id + } + + pub fn get_expecte_failure_flag(&self) -> bool { + self.expect_failure + } +} diff --git a/crates/noirc_frontend/src/hir/mod.rs b/crates/noirc_frontend/src/hir/mod.rs index 128902f070f..3805efc653c 100644 --- a/crates/noirc_frontend/src/hir/mod.rs +++ b/crates/noirc_frontend/src/hir/mod.rs @@ -6,11 +6,13 @@ pub mod type_check; use crate::graph::{CrateGraph, CrateId}; use crate::hir_def::function::FuncMeta; -use crate::node_interner::{FuncId, NodeInterner, TestFunc}; +use crate::node_interner::{FuncId, NodeInterner}; use def_map::{Contract, CrateDefMap}; use fm::FileManager; use std::collections::HashMap; +use self::def_map::TestFunc; + /// Helper object which groups together several useful context objects used /// during name resolution. Once name resolution is finished, only the /// def_interner is required for type inference and monomorphization. @@ -113,15 +115,16 @@ impl Context { def_map .get_all_test_functions(interner) - .filter_map(|(id, not_fail)| { - let fully_qualified_name = self.fully_qualified_function_name(crate_id, &id); + .filter_map(|test_function| { + let fully_qualified_name = + self.fully_qualified_function_name(crate_id, &test_function.get_id()); match &pattern { - FunctionNameMatch::Anything => Some((fully_qualified_name, (id, not_fail))), + FunctionNameMatch::Anything => Some((fully_qualified_name, test_function)), FunctionNameMatch::Exact(pattern) => (&fully_qualified_name == pattern) - .then_some((fully_qualified_name, (id, not_fail))), + .then_some((fully_qualified_name, test_function)), FunctionNameMatch::Contains(pattern) => fully_qualified_name .contains(pattern) - .then_some((fully_qualified_name, (id, not_fail))), + .then_some((fully_qualified_name, test_function)), } }) .collect() diff --git a/crates/noirc_frontend/src/node_interner.rs b/crates/noirc_frontend/src/node_interner.rs index 155a42cbafc..5a45cfee42b 100644 --- a/crates/noirc_frontend/src/node_interner.rs +++ b/crates/noirc_frontend/src/node_interner.rs @@ -138,8 +138,6 @@ impl FuncId { } } -pub type TestFunc = (FuncId, /* should fail */ bool); - #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] pub struct StructId(pub ModuleId); From a0c37713a2a97d4fa27538f5f99ccc757ac4d026 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Wed, 23 Aug 2023 21:47:15 +0100 Subject: [PATCH 16/34] fix typo --- crates/nargo_cli/src/cli/test_cmd.rs | 2 +- crates/noirc_frontend/src/hir/def_map/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index c2a1abd2508..ff3efcc8c42 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -98,7 +98,7 @@ fn run_tests( backend, &test_name, test_function.get_id(), - test_function.get_expecte_failure_flag(), + test_function.get_expect_failure_flag(), &context, show_output, compile_options, diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index 9b5ef3ef795..852e44256e7 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -247,7 +247,7 @@ impl TestFunc { self.id } - pub fn get_expecte_failure_flag(&self) -> bool { + pub fn get_expect_failure_flag(&self) -> bool { self.expect_failure } } From 34f3af75e512ec0f0738c575f1d2b45f5faa81d8 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Thu, 24 Aug 2023 17:29:28 +0100 Subject: [PATCH 17/34] rename testfunc to testfunction --- crates/noirc_frontend/src/hir/def_map/mod.rs | 12 ++++++------ crates/noirc_frontend/src/hir/mod.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index 852e44256e7..a480a9a80c1 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -129,7 +129,7 @@ impl CrateDefMap { pub fn get_all_test_functions<'a>( &'a self, interner: &'a NodeInterner, - ) -> impl Iterator + 'a { + ) -> impl Iterator + 'a { self.modules.iter().flat_map(|(_, module)| { module .value_definitions() @@ -141,9 +141,9 @@ impl CrateDefMap { if interner.function_meta(&id).attributes == Some(Attribute::Test { expect_failure: true }) { - TestFunc::new(id, true) + TestFunction::new(id, true) } else { - TestFunc::new(id, false) + TestFunction::new(id, false) } }) }) @@ -233,14 +233,14 @@ impl std::ops::IndexMut for CrateDefMap { } } -pub struct TestFunc { +pub struct TestFunction { id: FuncId, expect_failure: bool, } -impl TestFunc { +impl TestFunction { fn new(id: FuncId, expect_failure: bool) -> Self { - TestFunc { id, expect_failure } + TestFunction { id, expect_failure } } pub fn get_id(&self) -> FuncId { diff --git a/crates/noirc_frontend/src/hir/mod.rs b/crates/noirc_frontend/src/hir/mod.rs index 3805efc653c..76bbe59f513 100644 --- a/crates/noirc_frontend/src/hir/mod.rs +++ b/crates/noirc_frontend/src/hir/mod.rs @@ -11,7 +11,7 @@ use def_map::{Contract, CrateDefMap}; use fm::FileManager; use std::collections::HashMap; -use self::def_map::TestFunc; +use self::def_map::TestFunction; /// Helper object which groups together several useful context objects used /// during name resolution. Once name resolution is finished, only the @@ -109,7 +109,7 @@ impl Context { &self, crate_id: &CrateId, pattern: FunctionNameMatch, - ) -> Vec<(String, TestFunc)> { + ) -> Vec<(String, TestFunction)> { let interner = &self.def_interner; let def_map = self.def_map(crate_id).expect("The local crate should be analyzed already"); From 0a339864615d7a0b202297f0985454adbda6ad89 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Thu, 24 Aug 2023 18:05:55 +0100 Subject: [PATCH 18/34] only pass in test function --- crates/nargo_cli/src/cli/test_cmd.rs | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index ff3efcc8c42..9efdb572e6a 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -7,8 +7,7 @@ use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelec use noirc_driver::{compile_no_check, CompileOptions}; use noirc_frontend::{ graph::CrateName, - hir::{Context, FunctionNameMatch}, - node_interner::FuncId, + hir::{def_map::TestFunction, Context, FunctionNameMatch}, }; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; @@ -94,15 +93,7 @@ fn run_tests( .expect("Failed to write to stdout"); writer.flush().expect("Failed to flush writer"); - match run_test( - backend, - &test_name, - test_function.get_id(), - test_function.get_expect_failure_flag(), - &context, - show_output, - compile_options, - ) { + match run_test(backend, &test_name, test_function, &context, show_output, compile_options) { Ok(_) => { writer .set_color(ColorSpec::new().set_fg(Some(Color::Green))) @@ -131,8 +122,7 @@ fn run_tests( fn run_test( backend: &B, test_name: &str, - main: FuncId, - should_fail: bool, + test_function: TestFunction, context: &Context, show_output: bool, config: &CompileOptions, @@ -142,7 +132,7 @@ fn run_test( Err(CliError::Generic(format!("Test '{test_name}' failed to compile"))) }; - let program = compile_no_check(context, config, main); + let program = compile_no_check(context, config, test_function.get_id()); match program { Ok(mut program) => { // Note: We could perform this test using the unoptimized ACIR as generated by `compile_no_check`. @@ -153,7 +143,7 @@ fn run_test( let circuit_execution = execute_circuit(backend, program.circuit, WitnessMap::new(), show_output); - if should_fail { + if test_function.get_expect_failure_flag() { match circuit_execution { Ok(_) => Err(CliError::Generic(format!("Test '{test_name}' should fail"))), Err(_) => Ok(()), @@ -173,7 +163,7 @@ fn run_test( } } Err(err) => { - if should_fail { + if test_function.get_expect_failure_flag() { if !err.diagnostic.message.contains("Failed constraint") { report_error(err) } else { From aa1a71958584a27991a240cf91e35e92744fcb2c Mon Sep 17 00:00:00 2001 From: Ethan-000 Date: Thu, 24 Aug 2023 18:06:18 +0100 Subject: [PATCH 19/34] Update crates/nargo_cli/src/cli/test_cmd.rs Co-authored-by: Blaine Bublitz --- crates/nargo_cli/src/cli/test_cmd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index 9efdb572e6a..8dbf6b61fa5 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -135,7 +135,7 @@ fn run_test( let program = compile_no_check(context, config, test_function.get_id()); match program { Ok(mut program) => { - // Note: We could perform this test using the unoptimized ACIR as generated by `compile_no_check`. + // Note: We don't need to use the optimized ACIR here program.circuit = optimize_circuit(backend, program.circuit).unwrap().0; // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, From d741ef39c32be2b61eb3843d93f84ffcb003a671 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Thu, 24 Aug 2023 18:20:59 +0100 Subject: [PATCH 20/34] use pattern matching --- crates/noirc_frontend/src/hir/def_map/mod.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index a480a9a80c1..aaf15a5280b 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -137,14 +137,12 @@ impl CrateDefMap { .filter(|id| { matches!(interner.function_meta(id).attributes, Some(Attribute::Test { .. })) }) - .map(|id| { - if interner.function_meta(&id).attributes - == Some(Attribute::Test { expect_failure: true }) - { - TestFunction::new(id, true) - } else { - TestFunction::new(id, false) + .map(|id| match interner.function_meta(&id).attributes { + Some(Attribute::Test { expect_failure }) => { + TestFunction::new(id, expect_failure) } + None => unreachable!(), + _ => unreachable!(), }) }) } From 49bd10a9bdbb77cfcd71e4fcbabe9e6340f4f794 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Thu, 24 Aug 2023 18:41:39 +0100 Subject: [PATCH 21/34] use a single filtermap untested --- crates/noirc_frontend/src/hir/def_map/mod.rs | 24 ++++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index aaf15a5280b..1aff1aa305d 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -131,19 +131,19 @@ impl CrateDefMap { interner: &'a NodeInterner, ) -> impl Iterator + 'a { self.modules.iter().flat_map(|(_, module)| { - module - .value_definitions() - .filter_map(|id| id.as_function()) - .filter(|id| { - matches!(interner.function_meta(id).attributes, Some(Attribute::Test { .. })) - }) - .map(|id| match interner.function_meta(&id).attributes { - Some(Attribute::Test { expect_failure }) => { - TestFunction::new(id, expect_failure) + module.value_definitions().filter_map(|id| { + if id.as_function().is_none() { + return None; + } else { + match interner.function_meta(&id.as_function().unwrap()).attributes { + Some(Attribute::Test { expect_failure }) => { + Some(TestFunction::new(id.as_function().unwrap(), expect_failure)) + } + None => None, + _ => None, } - None => unreachable!(), - _ => unreachable!(), - }) + } + }) }) } From 3d96f3fc6c917b5df1abea424cb05efc1617efe2 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Thu, 24 Aug 2023 18:47:31 +0100 Subject: [PATCH 22/34] clippy --- crates/noirc_frontend/src/hir/def_map/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index 1aff1aa305d..d1bfb337dc5 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -133,7 +133,7 @@ impl CrateDefMap { self.modules.iter().flat_map(|(_, module)| { module.value_definitions().filter_map(|id| { if id.as_function().is_none() { - return None; + None } else { match interner.function_meta(&id.as_function().unwrap()).attributes { Some(Attribute::Test { expect_failure }) => { From 77d9c33223976317c87df1fb2ca9cb0acd69a9ea Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Thu, 24 Aug 2023 18:54:17 +0100 Subject: [PATCH 23/34] use if le --- crates/noirc_frontend/src/hir/def_map/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index d1bfb337dc5..dc158462a82 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -132,16 +132,16 @@ impl CrateDefMap { ) -> impl Iterator + 'a { self.modules.iter().flat_map(|(_, module)| { module.value_definitions().filter_map(|id| { - if id.as_function().is_none() { - None - } else { - match interner.function_meta(&id.as_function().unwrap()).attributes { + if let Some(func_id) = id.as_function() { + match interner.function_meta(&func_id).attributes { Some(Attribute::Test { expect_failure }) => { - Some(TestFunction::new(id.as_function().unwrap(), expect_failure)) + Some(TestFunction::new(func_id, expect_failure)) } None => None, _ => None, } + } else { + None } }) }) From 9d55fbfaf9e9a2978f8280a9e6508831cc9353ed Mon Sep 17 00:00:00 2001 From: Ethan-000 Date: Thu, 24 Aug 2023 18:56:48 +0100 Subject: [PATCH 24/34] Update crates/noirc_frontend/src/hir/def_map/mod.rs Co-authored-by: Blaine Bublitz --- crates/noirc_frontend/src/hir/def_map/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index dc158462a82..5520a8654a9 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -137,7 +137,6 @@ impl CrateDefMap { Some(Attribute::Test { expect_failure }) => { Some(TestFunction::new(func_id, expect_failure)) } - None => None, _ => None, } } else { From 8f8106af51205e78122c5a2c0c771e46976938e0 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Thu, 24 Aug 2023 20:38:52 +0100 Subject: [PATCH 25/34] chore: use general scope variant instead of expect_failure (#2427) --- crates/noirc_frontend/src/hir/def_map/mod.rs | 17 +++--- crates/noirc_frontend/src/lexer/lexer.rs | 39 ++++++++++++++ crates/noirc_frontend/src/lexer/token.rs | 57 ++++++++++++++------ 3 files changed, 88 insertions(+), 25 deletions(-) diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index 5520a8654a9..cc17b79ea7a 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -3,7 +3,7 @@ use crate::hir::def_collector::dc_crate::DefCollector; use crate::hir::Context; use crate::node_interner::{FuncId, NodeInterner}; use crate::parser::{parse_program, ParsedModule}; -use crate::token::Attribute; +use crate::token::{Attribute, TestScope}; use arena::{Arena, Index}; use fm::{FileId, FileManager}; use noirc_errors::{FileDiagnostic, Location}; @@ -134,9 +134,7 @@ impl CrateDefMap { module.value_definitions().filter_map(|id| { if let Some(func_id) = id.as_function() { match interner.function_meta(&func_id).attributes { - Some(Attribute::Test { expect_failure }) => { - Some(TestFunction::new(func_id, expect_failure)) - } + Some(Attribute::Test(scope)) => Some(TestFunction::new(func_id, scope)), _ => None, } } else { @@ -232,12 +230,12 @@ impl std::ops::IndexMut for CrateDefMap { pub struct TestFunction { id: FuncId, - expect_failure: bool, + scope: TestScope, } impl TestFunction { - fn new(id: FuncId, expect_failure: bool) -> Self { - TestFunction { id, expect_failure } + fn new(id: FuncId, scope: TestScope) -> Self { + TestFunction { id, scope } } pub fn get_id(&self) -> FuncId { @@ -245,6 +243,9 @@ impl TestFunction { } pub fn get_expect_failure_flag(&self) -> bool { - self.expect_failure + match self.scope { + TestScope::ShouldFail => true, + TestScope::None => false, + } } } diff --git a/crates/noirc_frontend/src/lexer/lexer.rs b/crates/noirc_frontend/src/lexer/lexer.rs index fc72f43674d..eae194d82ba 100644 --- a/crates/noirc_frontend/src/lexer/lexer.rs +++ b/crates/noirc_frontend/src/lexer/lexer.rs @@ -1,3 +1,5 @@ +use crate::token::TestScope; + use super::{ errors::LexerErrorKind, token::{Attribute, IntType, Keyword, SpannedToken, Token, Tokens}, @@ -462,6 +464,43 @@ fn custom_attribute() { assert_eq!(token.token(), &Token::Attribute(Attribute::Custom("custom(hello)".to_string()))); } +#[test] +fn test_attribute() { + let input = r#"#[test]"#; + let mut lexer = Lexer::new(input); + + let token = lexer.next().unwrap().unwrap(); + assert_eq!(token.token(), &Token::Attribute(Attribute::Test(TestScope::None))); +} +#[test] +fn test_attribute_with_valid_scope() { + let input = r#"#[test(should_fail)]"#; + let mut lexer = Lexer::new(input); + + let token = lexer.next().unwrap().unwrap(); + assert_eq!(token.token(), &Token::Attribute(Attribute::Test(TestScope::ShouldFail))); +} + +#[test] +fn test_attribute_with_invalid_scope() { + let input = r#"#[test(invalid_scope)]"#; + let mut lexer = Lexer::new(input); + + let token = lexer.next().unwrap(); + let err = match token { + Ok(_) => panic!("test has an invalid scope, so expected an error"), + Err(err) => err, + }; + + // Check if error is MalformedFuncAttribute and found is "foo" + let sub_string = match err { + LexerErrorKind::MalformedFuncAttribute { found, .. } => found, + _ => panic!("expected malformed func attribute error"), + }; + + assert_eq!(sub_string, "test(invalid_scope)"); +} + #[test] fn test_custom_gate_syntax() { let input = "#[foreign(sha256)]#[foreign(blake2s)]#[builtin(sum)]"; diff --git a/crates/noirc_frontend/src/lexer/token.rs b/crates/noirc_frontend/src/lexer/token.rs index f16f46fa5d1..5a47d4ece73 100644 --- a/crates/noirc_frontend/src/lexer/token.rs +++ b/crates/noirc_frontend/src/lexer/token.rs @@ -316,6 +316,36 @@ impl IntType { } } +/// TestScope is used to specify additional annotations for test functions +#[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)] +pub enum TestScope { + /// If a test has a scope of ShouldFail, then it is expected to fail + ShouldFail, + /// No scope is applied and so the test must pass + None, +} + +impl TestScope { + fn lookup_str(string: &str) -> Option { + match string { + "should_fail" => Some(TestScope::ShouldFail), + _ => None, + } + } + fn as_str(&self) -> &'static str { + match self { + TestScope::ShouldFail => "should_fail", + TestScope::None => "", + } + } +} + +impl fmt::Display for TestScope { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({})", self.as_str()) + } +} + #[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)] // Attributes are special language markers in the target language // An example of one is `#[SHA256]` . Currently only Foreign attributes are supported @@ -325,23 +355,17 @@ pub enum Attribute { Builtin(String), Oracle(String), Deprecated(Option), - Test { expect_failure: bool }, + Test(TestScope), Custom(String), } impl fmt::Display for Attribute { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { + match self { Attribute::Foreign(ref k) => write!(f, "#[foreign({k})]"), Attribute::Builtin(ref k) => write!(f, "#[builtin({k})]"), Attribute::Oracle(ref k) => write!(f, "#[oracle({k})]"), - Attribute::Test { expect_failure } => { - if !expect_failure { - write!(f, "#[test]") - } else { - write!(f, "#[test(should_fail)]") - } - } + Attribute::Test(scope) => write!(f, "#[test{}]", scope), Attribute::Deprecated(None) => write!(f, "#[deprecated]"), Attribute::Deprecated(Some(ref note)) => write!(f, r#"#[deprecated("{note}")]"#), Attribute::Custom(ref k) => write!(f, "#[{k}]"), @@ -397,16 +421,15 @@ impl Attribute { Attribute::Deprecated(name.trim_matches('"').to_string().into()) } - ["test"] => Attribute::Test { expect_failure: false }, + ["test"] => Attribute::Test(TestScope::None), ["test", name] => { - if name != &"should_fail" { - return Err(LexerErrorKind::MalformedFuncAttribute { - span, - found: word.to_owned(), - }); + validate(name)?; + let malformed_scope = + LexerErrorKind::MalformedFuncAttribute { span, found: word.to_owned() }; + match TestScope::lookup_str(name) { + Some(scope) => Attribute::Test(scope), + None => return Err(malformed_scope), } - - Attribute::Test { expect_failure: true } } tokens => { tokens.iter().try_for_each(|token| validate(token))?; From 74ef56fcdbcf872848270b7df2d9812e01d6e86a Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Thu, 24 Aug 2023 21:04:31 +0100 Subject: [PATCH 26/34] fix clippy --- crates/noirc_frontend/src/lexer/lexer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/noirc_frontend/src/lexer/lexer.rs b/crates/noirc_frontend/src/lexer/lexer.rs index eae194d82ba..64a3fd27ab3 100644 --- a/crates/noirc_frontend/src/lexer/lexer.rs +++ b/crates/noirc_frontend/src/lexer/lexer.rs @@ -1,5 +1,3 @@ -use crate::token::TestScope; - use super::{ errors::LexerErrorKind, token::{Attribute, IntType, Keyword, SpannedToken, Token, Tokens}, @@ -466,6 +464,7 @@ fn custom_attribute() { #[test] fn test_attribute() { + use crate::token::TestScope; let input = r#"#[test]"#; let mut lexer = Lexer::new(input); @@ -474,6 +473,7 @@ fn test_attribute() { } #[test] fn test_attribute_with_valid_scope() { + use crate::token::TestScope; let input = r#"#[test(should_fail)]"#; let mut lexer = Lexer::new(input); From a49655d8d78559791e11eb5ef06f2f7c233d7ac6 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Thu, 24 Aug 2023 21:12:05 +0100 Subject: [PATCH 27/34] use cfg test to fix clippy --- crates/noirc_frontend/src/lexer/lexer.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/noirc_frontend/src/lexer/lexer.rs b/crates/noirc_frontend/src/lexer/lexer.rs index 64a3fd27ab3..0b5d5762b24 100644 --- a/crates/noirc_frontend/src/lexer/lexer.rs +++ b/crates/noirc_frontend/src/lexer/lexer.rs @@ -1,3 +1,6 @@ +#[cfg(test)] +use crate::token::TestScope; + use super::{ errors::LexerErrorKind, token::{Attribute, IntType, Keyword, SpannedToken, Token, Tokens}, @@ -464,7 +467,6 @@ fn custom_attribute() { #[test] fn test_attribute() { - use crate::token::TestScope; let input = r#"#[test]"#; let mut lexer = Lexer::new(input); @@ -473,7 +475,6 @@ fn test_attribute() { } #[test] fn test_attribute_with_valid_scope() { - use crate::token::TestScope; let input = r#"#[test(should_fail)]"#; let mut lexer = Lexer::new(input); From cb36da09aee7cdb127022d4e93e3e5e3b2ebf5f4 Mon Sep 17 00:00:00 2001 From: Ethan-000 Date: Thu, 24 Aug 2023 21:21:59 +0100 Subject: [PATCH 28/34] Update crates/nargo_cli/src/cli/test_cmd.rs Co-authored-by: Blaine Bublitz --- crates/nargo_cli/src/cli/test_cmd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index 8dbf6b61fa5..eb61438b812 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -143,7 +143,7 @@ fn run_test( let circuit_execution = execute_circuit(backend, program.circuit, WitnessMap::new(), show_output); - if test_function.get_expect_failure_flag() { + if test_function.should_fail() { match circuit_execution { Ok(_) => Err(CliError::Generic(format!("Test '{test_name}' should fail"))), Err(_) => Ok(()), From d1188a21afe67e6727f4831c4da1a9c23973afc1 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Thu, 24 Aug 2023 21:23:22 +0100 Subject: [PATCH 29/34] rename function to should_fail --- crates/nargo_cli/src/cli/test_cmd.rs | 2 +- crates/noirc_frontend/src/hir/def_map/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index eb61438b812..0cb16c0ad8d 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -163,7 +163,7 @@ fn run_test( } } Err(err) => { - if test_function.get_expect_failure_flag() { + if test_function.should_fail() { if !err.diagnostic.message.contains("Failed constraint") { report_error(err) } else { diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index cc17b79ea7a..cc715766843 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -242,7 +242,7 @@ impl TestFunction { self.id } - pub fn get_expect_failure_flag(&self) -> bool { + pub fn should_fail(&self) -> bool { match self.scope { TestScope::ShouldFail => true, TestScope::None => false, From 6cddf304e1d7c7d88df0f4e192e81afb5a62dff4 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Thu, 24 Aug 2023 20:23:33 +0000 Subject: [PATCH 30/34] fix clippy by putting a cfg on mod tests --- crates/noirc_frontend/src/lexer/lexer.rs | 650 ++++++++++++----------- 1 file changed, 329 insertions(+), 321 deletions(-) diff --git a/crates/noirc_frontend/src/lexer/lexer.rs b/crates/noirc_frontend/src/lexer/lexer.rs index 0b5d5762b24..75dba3cad14 100644 --- a/crates/noirc_frontend/src/lexer/lexer.rs +++ b/crates/noirc_frontend/src/lexer/lexer.rs @@ -1,6 +1,3 @@ -#[cfg(test)] -use crate::token::TestScope; - use super::{ errors::LexerErrorKind, token::{Attribute, IntType, Keyword, SpannedToken, Token, Tokens}, @@ -382,334 +379,344 @@ impl<'a> Iterator for Lexer<'a> { } } -#[test] -fn test_single_double_char() { - let input = "! != + ( ) { } [ ] | , ; : :: < <= > >= & - -> . .. % / * = == << >>"; - - let expected = vec![ - Token::Bang, - Token::NotEqual, - Token::Plus, - Token::LeftParen, - Token::RightParen, - Token::LeftBrace, - Token::RightBrace, - Token::LeftBracket, - Token::RightBracket, - Token::Pipe, - Token::Comma, - Token::Semicolon, - Token::Colon, - Token::DoubleColon, - Token::Less, - Token::LessEqual, - Token::Greater, - Token::GreaterEqual, - Token::Ampersand, - Token::Minus, - Token::Arrow, - Token::Dot, - Token::DoubleDot, - Token::Percent, - Token::Slash, - Token::Star, - Token::Assign, - Token::Equal, - Token::ShiftLeft, - Token::Greater, - Token::Greater, - Token::EOF, - ]; - - let mut lexer = Lexer::new(input); - - for token in expected.into_iter() { - let got = lexer.next_token().unwrap(); - assert_eq!(got, token); +#[cfg(test)] +mod tests { + use super::*; + use crate::token::TestScope; + #[test] + fn test_single_double_char() { + let input = "! != + ( ) { } [ ] | , ; : :: < <= > >= & - -> . .. % / * = == << >>"; + + let expected = vec![ + Token::Bang, + Token::NotEqual, + Token::Plus, + Token::LeftParen, + Token::RightParen, + Token::LeftBrace, + Token::RightBrace, + Token::LeftBracket, + Token::RightBracket, + Token::Pipe, + Token::Comma, + Token::Semicolon, + Token::Colon, + Token::DoubleColon, + Token::Less, + Token::LessEqual, + Token::Greater, + Token::GreaterEqual, + Token::Ampersand, + Token::Minus, + Token::Arrow, + Token::Dot, + Token::DoubleDot, + Token::Percent, + Token::Slash, + Token::Star, + Token::Assign, + Token::Equal, + Token::ShiftLeft, + Token::Greater, + Token::Greater, + Token::EOF, + ]; + + let mut lexer = Lexer::new(input); + + for token in expected.into_iter() { + let got = lexer.next_token().unwrap(); + assert_eq!(got, token); + } } -} -#[test] -fn invalid_attribute() { - let input = "#"; - let mut lexer = Lexer::new(input); + #[test] + fn invalid_attribute() { + let input = "#"; + let mut lexer = Lexer::new(input); - let token = lexer.next().unwrap(); - assert!(token.is_err()); -} + let token = lexer.next().unwrap(); + assert!(token.is_err()); + } -#[test] -fn deprecated_attribute() { - let input = r#"#[deprecated]"#; - let mut lexer = Lexer::new(input); + #[test] + fn deprecated_attribute() { + let input = r#"#[deprecated]"#; + let mut lexer = Lexer::new(input); - let token = lexer.next().unwrap().unwrap(); - assert_eq!(token.token(), &Token::Attribute(Attribute::Deprecated(None))); -} + let token = lexer.next().unwrap().unwrap(); + assert_eq!(token.token(), &Token::Attribute(Attribute::Deprecated(None))); + } -#[test] -fn deprecated_attribute_with_note() { - let input = r#"#[deprecated("hello")]"#; - let mut lexer = Lexer::new(input); + #[test] + fn deprecated_attribute_with_note() { + let input = r#"#[deprecated("hello")]"#; + let mut lexer = Lexer::new(input); - let token = lexer.next().unwrap().unwrap(); - assert_eq!(token.token(), &Token::Attribute(Attribute::Deprecated("hello".to_string().into()))); -} + let token = lexer.next().unwrap().unwrap(); + assert_eq!( + token.token(), + &Token::Attribute(Attribute::Deprecated("hello".to_string().into())) + ); + } -#[test] -fn custom_attribute() { - let input = r#"#[custom(hello)]"#; - let mut lexer = Lexer::new(input); + #[test] + fn custom_attribute() { + let input = r#"#[custom(hello)]"#; + let mut lexer = Lexer::new(input); - let token = lexer.next().unwrap().unwrap(); - assert_eq!(token.token(), &Token::Attribute(Attribute::Custom("custom(hello)".to_string()))); -} + let token = lexer.next().unwrap().unwrap(); + assert_eq!( + token.token(), + &Token::Attribute(Attribute::Custom("custom(hello)".to_string())) + ); + } -#[test] -fn test_attribute() { - let input = r#"#[test]"#; - let mut lexer = Lexer::new(input); + #[test] + fn test_attribute() { + let input = r#"#[test]"#; + let mut lexer = Lexer::new(input); - let token = lexer.next().unwrap().unwrap(); - assert_eq!(token.token(), &Token::Attribute(Attribute::Test(TestScope::None))); -} -#[test] -fn test_attribute_with_valid_scope() { - let input = r#"#[test(should_fail)]"#; - let mut lexer = Lexer::new(input); + let token = lexer.next().unwrap().unwrap(); + assert_eq!(token.token(), &Token::Attribute(Attribute::Test(TestScope::None))); + } + #[test] + fn test_attribute_with_valid_scope() { + let input = r#"#[test(should_fail)]"#; + let mut lexer = Lexer::new(input); - let token = lexer.next().unwrap().unwrap(); - assert_eq!(token.token(), &Token::Attribute(Attribute::Test(TestScope::ShouldFail))); -} + let token = lexer.next().unwrap().unwrap(); + assert_eq!(token.token(), &Token::Attribute(Attribute::Test(TestScope::ShouldFail))); + } -#[test] -fn test_attribute_with_invalid_scope() { - let input = r#"#[test(invalid_scope)]"#; - let mut lexer = Lexer::new(input); + #[test] + fn test_attribute_with_invalid_scope() { + let input = r#"#[test(invalid_scope)]"#; + let mut lexer = Lexer::new(input); - let token = lexer.next().unwrap(); - let err = match token { - Ok(_) => panic!("test has an invalid scope, so expected an error"), - Err(err) => err, - }; + let token = lexer.next().unwrap(); + let err = match token { + Ok(_) => panic!("test has an invalid scope, so expected an error"), + Err(err) => err, + }; - // Check if error is MalformedFuncAttribute and found is "foo" - let sub_string = match err { - LexerErrorKind::MalformedFuncAttribute { found, .. } => found, - _ => panic!("expected malformed func attribute error"), - }; + // Check if error is MalformedFuncAttribute and found is "foo" + let sub_string = match err { + LexerErrorKind::MalformedFuncAttribute { found, .. } => found, + _ => panic!("expected malformed func attribute error"), + }; - assert_eq!(sub_string, "test(invalid_scope)"); -} + assert_eq!(sub_string, "test(invalid_scope)"); + } -#[test] -fn test_custom_gate_syntax() { - let input = "#[foreign(sha256)]#[foreign(blake2s)]#[builtin(sum)]"; + #[test] + fn test_custom_gate_syntax() { + let input = "#[foreign(sha256)]#[foreign(blake2s)]#[builtin(sum)]"; - let expected = vec![ - Token::Attribute(Attribute::Foreign("sha256".to_string())), - Token::Attribute(Attribute::Foreign("blake2s".to_string())), - Token::Attribute(Attribute::Builtin("sum".to_string())), - ]; + let expected = vec![ + Token::Attribute(Attribute::Foreign("sha256".to_string())), + Token::Attribute(Attribute::Foreign("blake2s".to_string())), + Token::Attribute(Attribute::Builtin("sum".to_string())), + ]; - let mut lexer = Lexer::new(input); - for token in expected.into_iter() { - let got = lexer.next_token().unwrap(); - assert_eq!(got, token); + let mut lexer = Lexer::new(input); + for token in expected.into_iter() { + let got = lexer.next_token().unwrap(); + assert_eq!(got, token); + } } -} -#[test] -fn test_int_type() { - let input = "u16 i16 i108 u104.5"; + #[test] + fn test_int_type() { + let input = "u16 i16 i108 u104.5"; - let expected = vec![ - Token::IntType(IntType::Unsigned(16)), - Token::IntType(IntType::Signed(16)), - Token::IntType(IntType::Signed(108)), - Token::IntType(IntType::Unsigned(104)), - Token::Dot, - Token::Int(5_i128.into()), - ]; + let expected = vec![ + Token::IntType(IntType::Unsigned(16)), + Token::IntType(IntType::Signed(16)), + Token::IntType(IntType::Signed(108)), + Token::IntType(IntType::Unsigned(104)), + Token::Dot, + Token::Int(5_i128.into()), + ]; - let mut lexer = Lexer::new(input); - for token in expected.into_iter() { - let got = lexer.next_token().unwrap(); - assert_eq!(got, token); + let mut lexer = Lexer::new(input); + for token in expected.into_iter() { + let got = lexer.next_token().unwrap(); + assert_eq!(got, token); + } } -} -#[test] -fn test_arithmetic_sugar() { - let input = "+= -= *= /= %="; - - let expected = vec![ - Token::Plus, - Token::Assign, - Token::Minus, - Token::Assign, - Token::Star, - Token::Assign, - Token::Slash, - Token::Assign, - Token::Percent, - Token::Assign, - ]; - - let mut lexer = Lexer::new(input); - for token in expected.into_iter() { - let got = lexer.next_token().unwrap(); - assert_eq!(got, token); + #[test] + fn test_arithmetic_sugar() { + let input = "+= -= *= /= %="; + + let expected = vec![ + Token::Plus, + Token::Assign, + Token::Minus, + Token::Assign, + Token::Star, + Token::Assign, + Token::Slash, + Token::Assign, + Token::Percent, + Token::Assign, + ]; + + let mut lexer = Lexer::new(input); + for token in expected.into_iter() { + let got = lexer.next_token().unwrap(); + assert_eq!(got, token); + } } -} -#[test] -fn unterminated_block_comment() { - let input = "/*/"; + #[test] + fn unterminated_block_comment() { + let input = "/*/"; - let mut lexer = Lexer::new(input); - let token = lexer.next().unwrap(); + let mut lexer = Lexer::new(input); + let token = lexer.next().unwrap(); - assert!(token.is_err()); -} + assert!(token.is_err()); + } -#[test] -fn test_comment() { - let input = "// hello + #[test] + fn test_comment() { + let input = "// hello let x = 5 "; - let expected = vec![ - Token::Keyword(Keyword::Let), - Token::Ident("x".to_string()), - Token::Assign, - Token::Int(FieldElement::from(5_i128)), - ]; - - let mut lexer = Lexer::new(input); - for token in expected.into_iter() { - let first_lexer_output = lexer.next_token().unwrap(); - assert_eq!(first_lexer_output, token); + let expected = vec![ + Token::Keyword(Keyword::Let), + Token::Ident("x".to_string()), + Token::Assign, + Token::Int(FieldElement::from(5_i128)), + ]; + + let mut lexer = Lexer::new(input); + for token in expected.into_iter() { + let first_lexer_output = lexer.next_token().unwrap(); + assert_eq!(first_lexer_output, token); + } } -} -#[test] -fn test_block_comment() { - let input = " + #[test] + fn test_block_comment() { + let input = " /* comment */ let x = 5 /* comment */ "; - let expected = vec![ - Token::Keyword(Keyword::Let), - Token::Ident("x".to_string()), - Token::Assign, - Token::Int(FieldElement::from(5_i128)), - ]; - - let mut lexer = Lexer::new(input); - for token in expected.into_iter() { - let first_lexer_output = lexer.next_token().unwrap(); - assert_eq!(first_lexer_output, token); + let expected = vec![ + Token::Keyword(Keyword::Let), + Token::Ident("x".to_string()), + Token::Assign, + Token::Int(FieldElement::from(5_i128)), + ]; + + let mut lexer = Lexer::new(input); + for token in expected.into_iter() { + let first_lexer_output = lexer.next_token().unwrap(); + assert_eq!(first_lexer_output, token); + } } -} -#[test] -fn test_nested_block_comments() { - let input = " + #[test] + fn test_nested_block_comments() { + let input = " /* /* */ /** */ /*! */ */ let x = 5 /* /* */ /** */ /*! */ */ "; - let expected = vec![ - Token::Keyword(Keyword::Let), - Token::Ident("x".to_string()), - Token::Assign, - Token::Int(FieldElement::from(5_i128)), - ]; - - let mut lexer = Lexer::new(input); - for token in expected.into_iter() { - let first_lexer_output = lexer.next_token().unwrap(); - assert_eq!(first_lexer_output, token); + let expected = vec![ + Token::Keyword(Keyword::Let), + Token::Ident("x".to_string()), + Token::Assign, + Token::Int(FieldElement::from(5_i128)), + ]; + + let mut lexer = Lexer::new(input); + for token in expected.into_iter() { + let first_lexer_output = lexer.next_token().unwrap(); + assert_eq!(first_lexer_output, token); + } } -} -#[test] -fn test_eat_string_literal() { - let input = "let _word = \"hello\""; - - let expected = vec![ - Token::Keyword(Keyword::Let), - Token::Ident("_word".to_string()), - Token::Assign, - Token::Str("hello".to_string()), - ]; - let mut lexer = Lexer::new(input); - - for token in expected.into_iter() { - let got = lexer.next_token().unwrap(); - assert_eq!(got, token); + #[test] + fn test_eat_string_literal() { + let input = "let _word = \"hello\""; + + let expected = vec![ + Token::Keyword(Keyword::Let), + Token::Ident("_word".to_string()), + Token::Assign, + Token::Str("hello".to_string()), + ]; + let mut lexer = Lexer::new(input); + + for token in expected.into_iter() { + let got = lexer.next_token().unwrap(); + assert_eq!(got, token); + } } -} -#[test] -fn test_eat_hex_int() { - let input = "0x05"; + #[test] + fn test_eat_hex_int() { + let input = "0x05"; - let expected = vec![Token::Int(5_i128.into())]; - let mut lexer = Lexer::new(input); + let expected = vec![Token::Int(5_i128.into())]; + let mut lexer = Lexer::new(input); - for token in expected.into_iter() { - let got = lexer.next_token().unwrap(); - assert_eq!(got, token); + for token in expected.into_iter() { + let got = lexer.next_token().unwrap(); + assert_eq!(got, token); + } } -} -#[test] -fn test_span() { - let input = "let x = 5"; + #[test] + fn test_span() { + let input = "let x = 5"; - // Let - let start_position = Position::default(); - let let_position = start_position + 2; - let let_token = Token::Keyword(Keyword::Let).into_span(start_position, let_position); + // Let + let start_position = Position::default(); + let let_position = start_position + 2; + let let_token = Token::Keyword(Keyword::Let).into_span(start_position, let_position); - // Skip whitespace - let whitespace_position = let_position + 1; + // Skip whitespace + let whitespace_position = let_position + 1; - // Identifier position - let ident_position = whitespace_position + 1; - let ident_token = Token::Ident("x".to_string()).into_single_span(ident_position); + // Identifier position + let ident_position = whitespace_position + 1; + let ident_token = Token::Ident("x".to_string()).into_single_span(ident_position); - // Skip whitespace - let whitespace_position = ident_position + 1; + // Skip whitespace + let whitespace_position = ident_position + 1; - // Assign position - let assign_position = whitespace_position + 1; - let assign_token = Token::Assign.into_single_span(assign_position); + // Assign position + let assign_position = whitespace_position + 1; + let assign_token = Token::Assign.into_single_span(assign_position); - // Skip whitespace - let whitespace_position = assign_position + 1; + // Skip whitespace + let whitespace_position = assign_position + 1; - // Int position - let int_position = whitespace_position + 1; - let int_token = Token::Int(5_i128.into()).into_single_span(int_position); + // Int position + let int_position = whitespace_position + 1; + let int_token = Token::Int(5_i128.into()).into_single_span(int_position); - let expected = vec![let_token, ident_token, assign_token, int_token]; - let mut lexer = Lexer::new(input); + let expected = vec![let_token, ident_token, assign_token, int_token]; + let mut lexer = Lexer::new(input); - for spanned_token in expected.into_iter() { - let got = lexer.next_token().unwrap(); - assert_eq!(got.to_span(), spanned_token.to_span()); - assert_eq!(got, spanned_token); + for spanned_token in expected.into_iter() { + let got = lexer.next_token().unwrap(); + assert_eq!(got.to_span(), spanned_token.to_span()); + assert_eq!(got, spanned_token); + } } -} -#[test] -fn test_basic_language_syntax() { - let input = " + #[test] + fn test_basic_language_syntax() { + let input = " let five = 5; let ten : Field = 10; let mul = fn(x, y) { @@ -719,60 +726,61 @@ fn test_basic_language_syntax() { assert(ten + five == 15); "; - let expected = vec![ - Token::Keyword(Keyword::Let), - Token::Ident("five".to_string()), - Token::Assign, - Token::Int(5_i128.into()), - Token::Semicolon, - Token::Keyword(Keyword::Let), - Token::Ident("ten".to_string()), - Token::Colon, - Token::Keyword(Keyword::Field), - Token::Assign, - Token::Int(10_i128.into()), - Token::Semicolon, - Token::Keyword(Keyword::Let), - Token::Ident("mul".to_string()), - Token::Assign, - Token::Keyword(Keyword::Fn), - Token::LeftParen, - Token::Ident("x".to_string()), - Token::Comma, - Token::Ident("y".to_string()), - Token::RightParen, - Token::LeftBrace, - Token::Ident("x".to_string()), - Token::Star, - Token::Ident("y".to_string()), - Token::Semicolon, - Token::RightBrace, - Token::Semicolon, - Token::Keyword(Keyword::Constrain), - Token::Ident("mul".to_string()), - Token::LeftParen, - Token::Ident("five".to_string()), - Token::Comma, - Token::Ident("ten".to_string()), - Token::RightParen, - Token::Equal, - Token::Int(50_i128.into()), - Token::Semicolon, - Token::Keyword(Keyword::Assert), - Token::LeftParen, - Token::Ident("ten".to_string()), - Token::Plus, - Token::Ident("five".to_string()), - Token::Equal, - Token::Int(15_i128.into()), - Token::RightParen, - Token::Semicolon, - Token::EOF, - ]; - let mut lexer = Lexer::new(input); - - for token in expected.into_iter() { - let got = lexer.next_token().unwrap(); - assert_eq!(got, token); + let expected = vec![ + Token::Keyword(Keyword::Let), + Token::Ident("five".to_string()), + Token::Assign, + Token::Int(5_i128.into()), + Token::Semicolon, + Token::Keyword(Keyword::Let), + Token::Ident("ten".to_string()), + Token::Colon, + Token::Keyword(Keyword::Field), + Token::Assign, + Token::Int(10_i128.into()), + Token::Semicolon, + Token::Keyword(Keyword::Let), + Token::Ident("mul".to_string()), + Token::Assign, + Token::Keyword(Keyword::Fn), + Token::LeftParen, + Token::Ident("x".to_string()), + Token::Comma, + Token::Ident("y".to_string()), + Token::RightParen, + Token::LeftBrace, + Token::Ident("x".to_string()), + Token::Star, + Token::Ident("y".to_string()), + Token::Semicolon, + Token::RightBrace, + Token::Semicolon, + Token::Keyword(Keyword::Constrain), + Token::Ident("mul".to_string()), + Token::LeftParen, + Token::Ident("five".to_string()), + Token::Comma, + Token::Ident("ten".to_string()), + Token::RightParen, + Token::Equal, + Token::Int(50_i128.into()), + Token::Semicolon, + Token::Keyword(Keyword::Assert), + Token::LeftParen, + Token::Ident("ten".to_string()), + Token::Plus, + Token::Ident("five".to_string()), + Token::Equal, + Token::Int(15_i128.into()), + Token::RightParen, + Token::Semicolon, + Token::EOF, + ]; + let mut lexer = Lexer::new(input); + + for token in expected.into_iter() { + let got = lexer.next_token().unwrap(); + assert_eq!(got, token); + } } } From 53d4bdfc0fabb674d80558d2aa5e6e78ce2559a3 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Thu, 24 Aug 2023 20:25:43 +0000 Subject: [PATCH 31/34] Merge branch 'e/should_panic' of github.com:noir-lang/noir into e/should_panic --- crates/nargo_cli/src/cli/test_cmd.rs | 4 ++-- crates/noirc_frontend/src/hir/def_map/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index 8dbf6b61fa5..0cb16c0ad8d 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -143,7 +143,7 @@ fn run_test( let circuit_execution = execute_circuit(backend, program.circuit, WitnessMap::new(), show_output); - if test_function.get_expect_failure_flag() { + if test_function.should_fail() { match circuit_execution { Ok(_) => Err(CliError::Generic(format!("Test '{test_name}' should fail"))), Err(_) => Ok(()), @@ -163,7 +163,7 @@ fn run_test( } } Err(err) => { - if test_function.get_expect_failure_flag() { + if test_function.should_fail() { if !err.diagnostic.message.contains("Failed constraint") { report_error(err) } else { diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index cc17b79ea7a..cc715766843 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -242,7 +242,7 @@ impl TestFunction { self.id } - pub fn get_expect_failure_flag(&self) -> bool { + pub fn should_fail(&self) -> bool { match self.scope { TestScope::ShouldFail => true, TestScope::None => false, From 8b26cb80d3cc801f93167e9b048999d3381c1ddc Mon Sep 17 00:00:00 2001 From: kevaundray Date: Thu, 24 Aug 2023 20:27:35 +0000 Subject: [PATCH 32/34] add comments --- crates/noirc_frontend/src/hir/def_map/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index cc715766843..f97d7e9bf5d 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -238,10 +238,13 @@ impl TestFunction { TestFunction { id, scope } } + /// Returns the function id of the test function pub fn get_id(&self) -> FuncId { self.id } + /// Returns true if the test function has been specified to fail + /// This is done by annotating the function with `#[test(should_fail)]` pub fn should_fail(&self) -> bool { match self.scope { TestScope::ShouldFail => true, From 239484f7f515b7dc6857c90554b3bef9856f3df4 Mon Sep 17 00:00:00 2001 From: ethan-000 Date: Thu, 24 Aug 2023 21:37:45 +0100 Subject: [PATCH 33/34] write error with color --- crates/nargo_cli/src/cli/test_cmd.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index 0cb16c0ad8d..c5ea3fd5c37 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -131,6 +131,14 @@ fn run_test( noirc_errors::reporter::report_all(&context.file_manager, &[err], config.deny_warnings); Err(CliError::Generic(format!("Test '{test_name}' failed to compile"))) }; + let write_error = |err| { + let writer = StandardStream::stderr(ColorChoice::Always); + let mut writer = writer.lock(); + writer.set_color(ColorSpec::new().set_fg(Some(Color::Red))).ok(); + writeln!(writer, "failed").ok(); + writer.reset().ok(); + Err(err) + }; let program = compile_no_check(context, config, test_function.get_id()); match program { @@ -145,20 +153,15 @@ fn run_test( if test_function.should_fail() { match circuit_execution { - Ok(_) => Err(CliError::Generic(format!("Test '{test_name}' should fail"))), + Ok(_) => { + write_error(CliError::Generic(format!("Test '{test_name}' should fail"))) + } Err(_) => Ok(()), } } else { match circuit_execution { Ok(_) => Ok(()), - Err(error) => { - let writer = StandardStream::stderr(ColorChoice::Always); - let mut writer = writer.lock(); - writer.set_color(ColorSpec::new().set_fg(Some(Color::Red))).ok(); - writeln!(writer, "failed").ok(); - writer.reset().ok(); - Err(error.into()) - } + Err(error) => write_error(error.into()), } } } From 095741bd21aecf55e232eb56dc5a5eeccec3dfa8 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Thu, 24 Aug 2023 20:46:26 +0000 Subject: [PATCH 34/34] add more comments regarding programs that are never satisfiable --- crates/nargo_cli/src/cli/test_cmd.rs | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index c5ea3fd5c37..ddf34933722 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -165,16 +165,27 @@ fn run_test( } } } + // Test function failed to compile + // + // Note: This could be because the compiler was able to deduce + // that a constraint was never satisfiable. + // An example of this is the program `assert(false)` + // In that case, we check if the test function should fail, and if so, we return Ok. Err(err) => { - if test_function.should_fail() { - if !err.diagnostic.message.contains("Failed constraint") { - report_error(err) - } else { - Ok(()) - } - } else { - report_error(err) + // The test has failed compilation, but it should never fail. Report error. + if !test_function.should_fail() { + return report_error(err); } + + // The test has failed compilation, check if it is because the program is never satisfiable. + // If it is never satisfiable, then this is the expected behavior. + let program_is_never_satisfiable = err.diagnostic.message.contains("Failed constraint"); + if program_is_never_satisfiable { + return Ok(()); + } + + // The test has failed compilation, but its a compilation error. Report error + report_error(err) } } }