diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index dc54fd624e4..e92b437da1d 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1,12 +1,15 @@ #![cfg(test)] -#[cfg(test)] +mod bound_checks; +mod imports; mod name_shadowing; +mod references; +mod turbofish; +mod unused_items; // XXX: These tests repeat a lot of code // what we should do is have test cases which are passed to a test harness // A test harness will allow for more expressive and readable tests -use core::panic; use std::collections::BTreeMap; use fm::FileId; @@ -2342,174 +2345,6 @@ fn impl_not_found_for_inner_impl() { )); } -// Regression for #5388 -#[test] -fn comptime_let() { - let src = r#"fn main() { - comptime let my_var = 2; - assert_eq(my_var, 2); - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); -} - -#[test] -fn overflowing_u8() { - let src = r#" - fn main() { - let _: u8 = 256; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `2⁸` cannot fit into `u8` which has range `0..=255`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } -} - -#[test] -fn underflowing_u8() { - let src = r#" - fn main() { - let _: u8 = -1; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `-1` cannot fit into `u8` which has range `0..=255`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } -} - -#[test] -fn overflowing_i8() { - let src = r#" - fn main() { - let _: i8 = 128; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `2⁷` cannot fit into `i8` which has range `-128..=127`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } -} - -#[test] -fn underflowing_i8() { - let src = r#" - fn main() { - let _: i8 = -129; - }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - if let CompilationError::TypeError(error) = &errors[0].0 { - assert_eq!( - error.to_string(), - "The value `-129` cannot fit into `i8` which has range `-128..=127`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); - } -} - -#[test] -fn turbofish_numeric_generic_nested_call() { - // Check for turbofish numeric generics used with function calls - let src = r#" - fn foo() -> [u8; N] { - [0; N] - } - - fn bar() -> [u8; N] { - foo::() - } - - global M: u32 = 3; - - fn main() { - let _ = bar::(); - } - "#; - assert_no_errors(src); - - // Check for turbofish numeric generics used with method calls - let src = r#" - struct Foo { - a: T - } - - impl Foo { - fn static_method() -> [u8; N] { - [0; N] - } - - fn impl_method(self) -> [T; N] { - [self.a; N] - } - } - - fn bar() -> [u8; N] { - let _ = Foo::static_method::(); - let x: Foo = Foo { a: 0 }; - x.impl_method::() - } - - global M: u32 = 3; - - fn main() { - let _ = bar::(); - } - "#; - assert_no_errors(src); -} - -#[test] -fn use_super() { - let src = r#" - fn some_func() {} - - mod foo { - use super::some_func; - - pub fn bar() { - some_func(); - } - } - "#; - assert_no_errors(src); -} - -#[test] -fn use_super_in_path() { - let src = r#" - fn some_func() {} - - mod foo { - pub fn func() { - super::some_func(); - } - } - "#; - assert_no_errors(src); -} - #[test] fn no_super() { let src = "use super::some_func;"; @@ -2801,150 +2636,6 @@ fn trait_constraint_on_tuple_type() { assert_no_errors(src); } -#[test] -fn turbofish_in_constructor_generics_mismatch() { - let src = r#" - struct Foo { - x: T - } - - fn main() { - let _ = Foo:: { x: 1 }; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::GenericCountMismatch { .. }), - )); -} - -#[test] -fn turbofish_in_constructor() { - let src = r#" - struct Foo { - x: T - } - - fn main() { - let x: Field = 0; - let _ = Foo:: { x: x }; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, expr_typ, .. - }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(expected_typ, "i32"); - assert_eq!(expr_typ, "Field"); -} - -#[test] -fn turbofish_in_middle_of_variable_unsupported_yet() { - let src = r#" - struct Foo { - x: T - } - - impl Foo { - fn new(x: T) -> Self { - Foo { x } - } - } - - fn main() { - let _ = Foo::::new(1); - } - "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::UnsupportedTurbofishUsage { .. }), - )); -} - -#[test] -fn turbofish_in_struct_pattern() { - let src = r#" - struct Foo { - x: T - } - - fn main() { - let value: Field = 0; - let Foo:: { x } = Foo { x: value }; - let _ = x; - } - "#; - assert_no_errors(src); -} - -#[test] -fn turbofish_in_struct_pattern_errors_if_type_mismatch() { - let src = r#" - struct Foo { - x: T - } - - fn main() { - let value: Field = 0; - let Foo:: { x } = Foo { x: value }; - let _ = x; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }) = &errors[0].0 - else { - panic!("Expected a type mismatch error, got {:?}", errors[0].0); - }; -} - -#[test] -fn turbofish_in_struct_pattern_generic_count_mismatch() { - let src = r#" - struct Foo { - x: T - } - - fn main() { - let value = 0; - let Foo:: { x } = Foo { x: value }; - let _ = x; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - item, - expected, - found, - .. - }) = &errors[0].0 - else { - panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); - }; - - assert_eq!(item, "struct Foo"); - assert_eq!(*expected, 1); - assert_eq!(*found, 2); -} - #[test] fn incorrect_generic_count_on_struct_impl() { let src = r#" @@ -3255,306 +2946,6 @@ fn as_trait_path_syntax_no_impl() { assert!(matches!(&errors[0].0, TypeError(TypeCheckError::NoMatchingImplFound { .. }))); } -#[test] -fn errors_on_unused_private_import() { - let src = r#" - mod foo { - pub fn bar() {} - pub fn baz() {} - - pub trait Foo { - } - } - - use foo::bar; - use foo::baz; - use foo::Foo; - - impl Foo for Field { - } - - fn main() { - baz(); - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = - &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(*item_type, "import"); -} - -#[test] -fn errors_on_unused_pub_crate_import() { - let src = r#" - mod foo { - pub fn bar() {} - pub fn baz() {} - - pub trait Foo { - } - } - - pub(crate) use foo::bar; - use foo::baz; - use foo::Foo; - - impl Foo for Field { - } - - fn main() { - baz(); - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = - &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(*item_type, "import"); -} - -#[test] -fn warns_on_use_of_private_exported_item() { - let src = r#" - mod foo { - mod bar { - pub fn baz() {} - } - - use bar::baz; - - pub fn qux() { - baz(); - } - } - - fn main() { - foo::baz(); - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); // An existing bug causes this error to be duplicated - - assert!(matches!( - &errors[0].0, - CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(..), - )) - )); -} - -#[test] -fn can_use_pub_use_item() { - let src = r#" - mod foo { - mod bar { - pub fn baz() {} - } - - pub use bar::baz; - } - - fn main() { - foo::baz(); - } - "#; - assert_no_errors(src); -} - -#[test] -fn warns_on_re_export_of_item_with_less_visibility() { - let src = r#" - mod foo { - mod bar { - pub(crate) fn baz() {} - } - - pub use bar::baz; - } - - fn main() { - foo::baz(); - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - &errors[0].0, - CompilationError::DefinitionError( - DefCollectorErrorKind::CannotReexportItemWithLessVisibility { .. } - ) - )); -} - -#[test] -fn unquoted_integer_as_integer_token() { - let src = r#" - trait Serialize { - fn serialize() {} - } - - #[attr] - pub fn foobar() {} - - comptime fn attr(_f: FunctionDefinition) -> Quoted { - let serialized_len = 1; - // We are testing that when we unquote $serialized_len, it's unquoted - // as the token `1` and not as something else that later won't be parsed correctly - // in the context of a generic argument. - quote { - impl Serialize<$serialized_len> for Field { - fn serialize() { } - } - } - } - - fn main() {} - "#; - - assert_no_errors(src); -} - -#[test] -fn errors_on_unused_function() { - let src = r#" - contract some_contract { - // This function is unused, but it's a contract entrypoint - // so it should not produce a warning - fn foo() -> pub Field { - 1 - } - } - - - fn foo() { - bar(); - } - - fn bar() {} - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = - &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "foo"); - assert_eq!(*item_type, "function"); -} - -#[test] -fn errors_on_unused_struct() { - let src = r#" - struct Foo {} - struct Bar {} - - fn main() { - let _ = Bar {}; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = - &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "Foo"); - assert_eq!(*item_type, "struct"); -} - -#[test] -fn errors_on_unused_trait() { - let src = r#" - trait Foo {} - trait Bar {} - - pub struct Baz { - } - - impl Bar for Baz {} - - fn main() { - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = - &errors[0].0 - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "Foo"); - assert_eq!(*item_type, "trait"); -} - -#[test] -fn constrained_reference_to_unconstrained() { - let src = r#" - fn main(mut x: u32, y: pub u32) { - let x_ref = &mut x; - if x == 5 { - unsafe { - mut_ref_input(x_ref, y); - } - } - - assert(x == 10); - } - - unconstrained fn mut_ref_input(x: &mut u32, y: u32) { - *x = y; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = - &errors[0].0 - else { - panic!("Expected an error about passing a constrained reference to unconstrained"); - }; -} - -#[test] -fn comptime_type_in_runtime_code() { - let source = "pub fn foo(_f: FunctionDefinition) {}"; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::ResolverError(ResolverError::ComptimeTypeInRuntimeCode { .. }) - )); -} - #[test] fn arithmetic_generics_canonicalization_deduplication_regression() { let source = r#" @@ -3574,82 +2965,6 @@ fn arithmetic_generics_canonicalization_deduplication_regression() { assert_eq!(errors.len(), 0); } -#[test] -fn cannot_mutate_immutable_variable() { - let src = r#" - fn main() { - let array = [1]; - mutate(&mut array); - } - - fn mutate(_: &mut [Field; 1]) {} - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = - &errors[0].0 - else { - panic!("Expected a CannotMutateImmutableVariable error"); - }; - - assert_eq!(name, "array"); -} - -#[test] -fn cannot_mutate_immutable_variable_on_member_access() { - let src = r#" - struct Foo { - x: Field - } - - fn main() { - let foo = Foo { x: 0 }; - mutate(&mut foo.x); - } - - fn mutate(foo: &mut Field) { - *foo = 1; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = - &errors[0].0 - else { - panic!("Expected a CannotMutateImmutableVariable error"); - }; - - assert_eq!(name, "foo"); -} - -#[test] -fn does_not_crash_when_passing_mutable_undefined_variable() { - let src = r#" - fn main() { - mutate(&mut undefined); - } - - fn mutate(foo: &mut Field) { - *foo = 1; - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, .. }) = - &errors[0].0 - else { - panic!("Expected a VariableNotDeclared error"); - }; - - assert_eq!(name, "undefined"); -} - #[test] fn infer_globals_to_u32_from_type_use() { let src = r#" @@ -3725,26 +3040,3 @@ fn use_numeric_generic_in_trait_method() { println!("{errors:?}"); assert_eq!(errors.len(), 0); } - -#[test] -fn macro_result_type_mismatch() { - let src = r#" - fn main() { - comptime { - let x = unquote!(quote { "test" }); - let _: Field = x; - } - } - - comptime fn unquote(q: Quoted) -> Quoted { - q - } - "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0].0, - CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) - )); -} diff --git a/compiler/noirc_frontend/src/tests/bound_checks.rs b/compiler/noirc_frontend/src/tests/bound_checks.rs new file mode 100644 index 00000000000..271f9d7a1a7 --- /dev/null +++ b/compiler/noirc_frontend/src/tests/bound_checks.rs @@ -0,0 +1,79 @@ +use crate::hir::def_collector::dc_crate::CompilationError; + +use super::get_program_errors; + +#[test] +fn overflowing_u8() { + let src = r#" + fn main() { + let _: u8 = 256; + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(error) = &errors[0].0 { + assert_eq!( + error.to_string(), + "The value `2⁸` cannot fit into `u8` which has range `0..=255`" + ); + } else { + panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); + } +} + +#[test] +fn underflowing_u8() { + let src = r#" + fn main() { + let _: u8 = -1; + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(error) = &errors[0].0 { + assert_eq!( + error.to_string(), + "The value `-1` cannot fit into `u8` which has range `0..=255`" + ); + } else { + panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); + } +} + +#[test] +fn overflowing_i8() { + let src = r#" + fn main() { + let _: i8 = 128; + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(error) = &errors[0].0 { + assert_eq!( + error.to_string(), + "The value `2⁷` cannot fit into `i8` which has range `-128..=127`" + ); + } else { + panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); + } +} + +#[test] +fn underflowing_i8() { + let src = r#" + fn main() { + let _: i8 = -129; + }"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + if let CompilationError::TypeError(error) = &errors[0].0 { + assert_eq!( + error.to_string(), + "The value `-129` cannot fit into `i8` which has range `-128..=127`" + ); + } else { + panic!("Expected OverflowingAssignment error, got {:?}", errors[0].0); + } +} diff --git a/compiler/noirc_frontend/src/tests/imports.rs b/compiler/noirc_frontend/src/tests/imports.rs new file mode 100644 index 00000000000..dfdc60e15e4 --- /dev/null +++ b/compiler/noirc_frontend/src/tests/imports.rs @@ -0,0 +1,112 @@ +use crate::hir::{ + def_collector::{dc_crate::CompilationError, errors::DefCollectorErrorKind}, + resolution::{errors::ResolverError, import::PathResolutionError}, +}; + +use super::{assert_no_errors, get_program_errors}; + +#[test] +fn use_super() { + let src = r#" + fn some_func() {} + + mod foo { + use super::some_func; + + pub fn bar() { + some_func(); + } + } + "#; + assert_no_errors(src); +} + +#[test] +fn use_super_in_path() { + let src = r#" + fn some_func() {} + + mod foo { + pub fn func() { + super::some_func(); + } + } + "#; + assert_no_errors(src); +} + +#[test] +fn warns_on_use_of_private_exported_item() { + let src = r#" + mod foo { + mod bar { + pub fn baz() {} + } + + use bar::baz; + + pub fn qux() { + baz(); + } + } + + fn main() { + foo::baz(); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 2); // An existing bug causes this error to be duplicated + + assert!(matches!( + &errors[0].0, + CompilationError::ResolverError(ResolverError::PathResolutionError( + PathResolutionError::Private(..), + )) + )); +} + +#[test] +fn can_use_pub_use_item() { + let src = r#" + mod foo { + mod bar { + pub fn baz() {} + } + + pub use bar::baz; + } + + fn main() { + foo::baz(); + } + "#; + assert_no_errors(src); +} + +#[test] +fn warns_on_re_export_of_item_with_less_visibility() { + let src = r#" + mod foo { + mod bar { + pub(crate) fn baz() {} + } + + pub use bar::baz; + } + + fn main() { + foo::baz(); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + assert!(matches!( + &errors[0].0, + CompilationError::DefinitionError( + DefCollectorErrorKind::CannotReexportItemWithLessVisibility { .. } + ) + )); +} diff --git a/compiler/noirc_frontend/src/tests/metaprogramming.rs b/compiler/noirc_frontend/src/tests/metaprogramming.rs new file mode 100644 index 00000000000..d980cba5cfd --- /dev/null +++ b/compiler/noirc_frontend/src/tests/metaprogramming.rs @@ -0,0 +1,65 @@ +use crate::hir::def_collector::dc_crate::CompilationError; + +use super::get_program_errors; + +#[test] +fn comptime_type_in_runtime_code() { + let source = "pub fn foo(_f: FunctionDefinition) {}"; + let errors = get_program_errors(source); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::ResolverError(ResolverError::ComptimeTypeInRuntimeCode { .. }) + )); +} + +#[test] +fn macro_result_type_mismatch() { + let src = r#" + fn main() { + comptime { + let x = unquote!(quote { "test" }); + let _: Field = x; + } + } + + comptime fn unquote(q: Quoted) -> Quoted { + q + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) + )); +} + +#[test] +fn unquoted_integer_as_integer_token() { + let src = r#" + trait Serialize { + fn serialize() {} + } + + #[attr] + pub fn foobar() {} + + comptime fn attr(_f: FunctionDefinition) -> Quoted { + let serialized_len = 1; + // We are testing that when we unquote $serialized_len, it's unquoted + // as the token `1` and not as something else that later won't be parsed correctly + // in the context of a generic argument. + quote { + impl Serialize<$serialized_len> for Field { + fn serialize() { } + } + } + } + + fn main() {} + "#; + + assert_no_errors(src); +} diff --git a/compiler/noirc_frontend/src/tests/references.rs b/compiler/noirc_frontend/src/tests/references.rs new file mode 100644 index 00000000000..ce72240d146 --- /dev/null +++ b/compiler/noirc_frontend/src/tests/references.rs @@ -0,0 +1,111 @@ +use crate::hir::{ + def_collector::dc_crate::CompilationError, resolution::errors::ResolverError, + type_check::TypeCheckError, +}; + +use super::get_program_errors; + +#[test] +fn cannot_mutate_immutable_variable() { + let src = r#" + fn main() { + let array = [1]; + mutate(&mut array); + } + + fn mutate(_: &mut [Field; 1]) {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = + &errors[0].0 + else { + panic!("Expected a CannotMutateImmutableVariable error"); + }; + + assert_eq!(name, "array"); +} + +#[test] +fn cannot_mutate_immutable_variable_on_member_access() { + let src = r#" + struct Foo { + x: Field + } + + fn main() { + let foo = Foo { x: 0 }; + mutate(&mut foo.x); + } + + fn mutate(foo: &mut Field) { + *foo = 1; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::CannotMutateImmutableVariable { name, .. }) = + &errors[0].0 + else { + panic!("Expected a CannotMutateImmutableVariable error"); + }; + + assert_eq!(name, "foo"); +} + +#[test] +fn does_not_crash_when_passing_mutable_undefined_variable() { + let src = r#" + fn main() { + mutate(&mut undefined); + } + + fn mutate(foo: &mut Field) { + *foo = 1; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, .. }) = + &errors[0].0 + else { + panic!("Expected a VariableNotDeclared error"); + }; + + assert_eq!(name, "undefined"); +} + +#[test] +fn constrained_reference_to_unconstrained() { + let src = r#" + fn main(mut x: u32, y: pub u32) { + let x_ref = &mut x; + if x == 5 { + unsafe { + mut_ref_input(x_ref, y); + } + } + + assert(x == 10); + } + + unconstrained fn mut_ref_input(x: &mut u32, y: u32) { + *x = y; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = + &errors[0].0 + else { + panic!("Expected an error about passing a constrained reference to unconstrained"); + }; +} diff --git a/compiler/noirc_frontend/src/tests/turbofish.rs b/compiler/noirc_frontend/src/tests/turbofish.rs new file mode 100644 index 00000000000..43d536fd196 --- /dev/null +++ b/compiler/noirc_frontend/src/tests/turbofish.rs @@ -0,0 +1,198 @@ +use crate::hir::{def_collector::dc_crate::CompilationError, type_check::TypeCheckError}; + +use super::{assert_no_errors, get_program_errors}; + +#[test] +fn turbofish_numeric_generic_nested_call() { + // Check for turbofish numeric generics used with function calls + let src = r#" + fn foo() -> [u8; N] { + [0; N] + } + + fn bar() -> [u8; N] { + foo::() + } + + global M: u32 = 3; + + fn main() { + let _ = bar::(); + } + "#; + assert_no_errors(src); + + // Check for turbofish numeric generics used with method calls + let src = r#" + struct Foo { + a: T + } + + impl Foo { + fn static_method() -> [u8; N] { + [0; N] + } + + fn impl_method(self) -> [T; N] { + [self.a; N] + } + } + + fn bar() -> [u8; N] { + let _ = Foo::static_method::(); + let x: Foo = Foo { a: 0 }; + x.impl_method::() + } + + global M: u32 = 3; + + fn main() { + let _ = bar::(); + } + "#; + assert_no_errors(src); +} + +#[test] +fn turbofish_in_constructor_generics_mismatch() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let _ = Foo:: { x: 1 }; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::GenericCountMismatch { .. }), + )); +} + +#[test] +fn turbofish_in_constructor() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let x: Field = 0; + let _ = Foo:: { x: x }; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::TypeMismatch { + expected_typ, expr_typ, .. + }) = &errors[0].0 + else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(expected_typ, "i32"); + assert_eq!(expr_typ, "Field"); +} + +#[test] +fn turbofish_in_middle_of_variable_unsupported_yet() { + let src = r#" + struct Foo { + x: T + } + + impl Foo { + fn new(x: T) -> Self { + Foo { x } + } + } + + fn main() { + let _ = Foo::::new(1); + } + "#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + assert!(matches!( + errors[0].0, + CompilationError::TypeError(TypeCheckError::UnsupportedTurbofishUsage { .. }), + )); +} + +#[test] +fn turbofish_in_struct_pattern() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let value: Field = 0; + let Foo:: { x } = Foo { x: value }; + let _ = x; + } + "#; + assert_no_errors(src); +} + +#[test] +fn turbofish_in_struct_pattern_errors_if_type_mismatch() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let value: Field = 0; + let Foo:: { x } = Foo { x: value }; + let _ = x; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }) = &errors[0].0 + else { + panic!("Expected a type mismatch error, got {:?}", errors[0].0); + }; +} + +#[test] +fn turbofish_in_struct_pattern_generic_count_mismatch() { + let src = r#" + struct Foo { + x: T + } + + fn main() { + let value = 0; + let Foo:: { x } = Foo { x: value }; + let _ = x; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { + item, + expected, + found, + .. + }) = &errors[0].0 + else { + panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(item, "struct Foo"); + assert_eq!(*expected, 1); + assert_eq!(*found, 2); +} diff --git a/compiler/noirc_frontend/src/tests/unused_items.rs b/compiler/noirc_frontend/src/tests/unused_items.rs new file mode 100644 index 00000000000..b49414d8b03 --- /dev/null +++ b/compiler/noirc_frontend/src/tests/unused_items.rs @@ -0,0 +1,159 @@ +use crate::hir::{def_collector::dc_crate::CompilationError, resolution::errors::ResolverError}; + +use super::get_program_errors; + +#[test] +fn errors_on_unused_private_import() { + let src = r#" + mod foo { + pub fn bar() {} + pub fn baz() {} + + pub trait Foo { + } + } + + use foo::bar; + use foo::baz; + use foo::Foo; + + impl Foo for Field { + } + + fn main() { + baz(); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "bar"); + assert_eq!(*item_type, "import"); +} + +#[test] +fn errors_on_unused_pub_crate_import() { + let src = r#" + mod foo { + pub fn bar() {} + pub fn baz() {} + + pub trait Foo { + } + } + + pub(crate) use foo::bar; + use foo::baz; + use foo::Foo; + + impl Foo for Field { + } + + fn main() { + baz(); + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "bar"); + assert_eq!(*item_type, "import"); +} + +#[test] +fn errors_on_unused_function() { + let src = r#" + contract some_contract { + // This function is unused, but it's a contract entrypoint + // so it should not produce a warning + fn foo() -> pub Field { + 1 + } + } + + + fn foo() { + bar(); + } + + fn bar() {} + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "foo"); + assert_eq!(*item_type, "function"); +} + +#[test] +fn errors_on_unused_struct() { + let src = r#" + struct Foo {} + struct Bar {} + + fn main() { + let _ = Bar {}; + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "Foo"); + assert_eq!(*item_type, "struct"); +} + +#[test] +fn errors_on_unused_trait() { + let src = r#" + trait Foo {} + trait Bar {} + + pub struct Baz { + } + + impl Bar for Baz {} + + fn main() { + } + "#; + + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item_type }) = + &errors[0].0 + else { + panic!("Expected an unused item error"); + }; + + assert_eq!(ident.to_string(), "Foo"); + assert_eq!(*item_type, "trait"); +} diff --git a/cspell.json b/cspell.json index 3ace51689fb..dbc5fb5a43e 100644 --- a/cspell.json +++ b/cspell.json @@ -213,6 +213,7 @@ "udiv", "umap", "underconstrained", + "underflowing", "uninstantiated", "unnormalized", "unoptimized", @@ -226,8 +227,7 @@ "wasmer", "Weierstraß", "zkhash", - "zshell", - "Linea" + "zshell" ], "ignorePaths": [ "./**/node_modules/**",