diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index ae7f0bef150..c44bfa6389c 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -505,7 +505,11 @@ impl PathSegment { /// /// Returns an empty span at the end of `foo` if there's no turbofish. pub fn turbofish_span(&self) -> Span { - Span::from(self.ident.span().end()..self.location.span.end()) + if self.ident.location().file == self.location.file { + Span::from(self.ident.span().end()..self.location.span.end()) + } else { + self.location.span + } } pub fn turbofish_location(&self) -> Location { diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 9847ced3b41..91d72debc0c 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -309,7 +309,7 @@ impl<'a> From<&'a ResolverError> for Diagnostic { let mut diagnostic = Diagnostic::simple_warning( format!("unused variable {name}"), - "unused variable ".to_string(), + "unused variable".to_string(), ident.location(), ); diagnostic.unnecessary = true; diff --git a/compiler/noirc_frontend/src/hir/resolution/import.rs b/compiler/noirc_frontend/src/hir/resolution/import.rs index ceb1dee9cb3..16e25b80465 100644 --- a/compiler/noirc_frontend/src/hir/resolution/import.rs +++ b/compiler/noirc_frontend/src/hir/resolution/import.rs @@ -115,7 +115,8 @@ impl<'a> From<&'a PathResolutionError> for CustomDiagnostic { CustomDiagnostic::simple_warning(error.to_string(), String::new(), ident.location()) } PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, traits } => { - let traits = vecmap(traits, |trait_name| format!("`{}`", trait_name)); + let mut traits = vecmap(traits, |trait_name| format!("`{}`", trait_name)); + traits.sort(); CustomDiagnostic::simple_error( error.to_string(), format!( @@ -126,7 +127,8 @@ impl<'a> From<&'a PathResolutionError> for CustomDiagnostic { ) } PathResolutionError::MultipleTraitsInScope { ident, traits } => { - let traits = vecmap(traits, |trait_name| format!("`{}`", trait_name)); + let mut traits = vecmap(traits, |trait_name| format!("`{}`", trait_name)); + traits.sort(); CustomDiagnostic::simple_error( error.to_string(), format!( diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index b2d0c713cf3..e48e37b748a 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -125,7 +125,11 @@ impl Parser<'_> { None }; - segments.push(PathSegment { ident, generics, location }); + segments.push(PathSegment { + ident, + generics, + location: self.location_since(location), + }); if self.at(Token::DoubleColon) && matches!(self.next_token.token(), Token::Ident(..)) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index d758ad58d31..52f96c69c44 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -16,22 +16,16 @@ mod visibility; // 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 std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use crate::elaborator::{FrontendOptions, UnstableFeature}; use fm::FileId; use iter_extended::vecmap; -use noirc_errors::Location; +use noirc_errors::{CustomDiagnostic, Location, Span}; -use crate::ast::IntegerBitSize; -use crate::hir::comptime::InterpreterError; use crate::hir::def_collector::dc_crate::CompilationError; -use crate::hir::def_collector::errors::{DefCollectorErrorKind, DuplicateType}; use crate::hir::def_map::ModuleData; -use crate::hir::resolution::errors::ResolverError; -use crate::hir::resolution::import::PathResolutionError; -use crate::hir::type_check::TypeCheckError; use crate::hir::Context; use crate::node_interner::{NodeInterner, StmtId}; @@ -63,7 +57,7 @@ pub(crate) fn remove_experimental_warnings(errors: &mut Vec) { pub(crate) fn get_program(src: &str) -> (ParsedModule, Context, Vec) { let allow_parser_errors = false; - get_program_with_maybe_parser_errors(src, allow_parser_errors, FrontendOptions::test_default()) + get_program_with_options(src, allow_parser_errors, FrontendOptions::test_default()) } pub(crate) fn get_program_using_features( @@ -73,13 +67,13 @@ pub(crate) fn get_program_using_features( let allow_parser_errors = false; let mut options = FrontendOptions::test_default(); options.enabled_unstable_features = features; - get_program_with_maybe_parser_errors(src, allow_parser_errors, options) + get_program_with_options(src, allow_parser_errors, options) } /// Compile a program. /// /// The stdlib is not available for these snippets. -pub(crate) fn get_program_with_maybe_parser_errors( +pub(crate) fn get_program_with_options( src: &str, allow_parser_errors: bool, options: FrontendOptions, @@ -151,6 +145,153 @@ fn assert_no_errors(src: &str) { } } +/// Given a source file with annotated errors, like this +/// +/// fn main() -> pub i32 { +/// ^^^ expected i32 because of return type +/// true +/// ~~~~ bool returned here +/// } +/// +/// where: +/// - lines with "^^^" are primary errors +/// - lines with "~~~" are secondary errors +/// +/// this method will check that compiling the program without those error markers +/// will produce errors at those locations and with/ those messages. +fn check_errors(src: &str) { + let allow_parser_errors = false; + check_errors_with_options(src, allow_parser_errors, FrontendOptions::test_default()); +} + +fn check_errors_using_features(src: &str, features: &[UnstableFeature]) { + let allow_parser_errors = false; + let mut options = FrontendOptions::test_default(); + options.enabled_unstable_features = features; + check_errors_with_options(src, allow_parser_errors, options); +} + +fn check_errors_with_options(src: &str, allow_parser_errors: bool, options: FrontendOptions) { + let lines = src.lines().collect::>(); + + // Here we'll hold just the lines that are code + let mut code_lines = Vec::new(); + // Here we'll capture lines that are primary error spans, like: + // + // ^^^ error message + let mut primary_spans_with_errors: Vec<(Span, String)> = Vec::new(); + // Here we'll capture lines that are secondary error spans, like: + // + // ~~~ error message + let mut secondary_spans_with_errors: Vec<(Span, String)> = Vec::new(); + // The byte at the start of this line + let mut byte = 0; + // The length of the last line, needed to go back to the byte at the beginning of the last line + let mut last_line_length = 0; + for line in lines { + if let Some((span, message)) = + get_error_line_span_and_message(line, '^', byte, last_line_length) + { + primary_spans_with_errors.push((span, message)); + continue; + } + + if let Some((span, message)) = + get_error_line_span_and_message(line, '~', byte, last_line_length) + { + secondary_spans_with_errors.push((span, message)); + continue; + } + + code_lines.push(line); + + byte += line.len() + 1; // For '\n' + last_line_length = line.len(); + } + + let mut primary_spans_with_errors: HashMap = + primary_spans_with_errors.into_iter().collect(); + + let mut secondary_spans_with_errors: HashMap = + secondary_spans_with_errors.into_iter().collect(); + + let src = code_lines.join("\n"); + let (_, _, errors) = get_program_with_options(&src, allow_parser_errors, options); + if errors.is_empty() && !primary_spans_with_errors.is_empty() { + panic!("Expected some errors but got none"); + } + + let errors = errors.iter().map(CustomDiagnostic::from).collect::>(); + for error in &errors { + let secondary = error + .secondaries + .first() + .unwrap_or_else(|| panic!("Expected {:?} to have a secondary label", error)); + let span = secondary.location.span; + let message = &error.message; + + let Some(expected_message) = primary_spans_with_errors.remove(&span) else { + if let Some(message) = secondary_spans_with_errors.get(&span) { + panic!("Error at {span:?} with message {message:?} is annotated as secondary but should be primary"); + } else { + panic!("Couldn't find primary error at {span:?} with message {message:?}.\nAll errors: {errors:?}"); + } + }; + + assert_eq!(message, &expected_message, "Primary error at {span:?} has unexpected message"); + + for secondary in &error.secondaries { + let message = &secondary.message; + if message.is_empty() { + continue; + } + + let span = secondary.location.span; + let Some(expected_message) = secondary_spans_with_errors.remove(&span) else { + if let Some(message) = primary_spans_with_errors.get(&span) { + panic!("Error at {span:?} with message {message:?} is annotated as primary but should be secondary"); + } else { + panic!("Couldn't find secondary error at {span:?} with message {message:?}.\nAll errors: {errors:?}"); + }; + }; + + assert_eq!( + message, &expected_message, + "Secondary error at {span:?} has unexpected message" + ); + } + } + + if !primary_spans_with_errors.is_empty() { + panic!("These primary errors didn't happen: {primary_spans_with_errors:?}"); + } + + if !secondary_spans_with_errors.is_empty() { + panic!("These secondary errors didn't happen: {secondary_spans_with_errors:?}"); + } +} + +/// Helper function for `check_errors` that returns the span that +/// `^^^^` or `~~~~` occupy, together with the message that follows it. +fn get_error_line_span_and_message( + line: &str, + char: char, + byte: usize, + last_line_length: usize, +) -> Option<(Span, String)> { + if !line.trim().starts_with(char) { + return None; + } + + let chars = line.chars().collect::>(); + let first_caret = chars.iter().position(|c| *c == char).unwrap(); + let last_caret = chars.iter().rposition(|c| *c == char).unwrap(); + let start = byte - last_line_length; + let span = Span::from((start + first_caret - 1) as u32..(start + last_caret) as u32); + let error = line.trim().trim_start_matches(char).trim().to_string(); + Some((span, error)) +} + #[test] fn check_trait_implemented_for_all_t() { let src = " @@ -213,10 +354,13 @@ fn check_trait_implementation_duplicate_method() { impl Default for Foo { // Duplicate trait methods should not compile fn default(x: Field, y: Field) -> Field { + ^^^^^^^ Duplicate definitions of trait associated function with name default found + ~~~~~~~ First trait associated function found here y + 2 * x } // Duplicate trait methods should not compile fn default(x: Field, y: Field) -> Field { + ~~~~~~~ Second trait associated function found here x + 2 * y } } @@ -224,31 +368,12 @@ fn check_trait_implementation_duplicate_method() { fn main() { let _ = Foo { bar: 1, array: [2, 3] }; // silence Foo never constructed warning }"; - - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for err in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ, - first_def, - second_def, - }) => { - assert_eq!(typ, &DuplicateType::TraitAssociatedFunction); - assert_eq!(first_def, "default"); - assert_eq!(second_def, "default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] fn check_trait_wrong_method_return_type() { + // TODO: improve the error location let src = " trait Default { fn default() -> Self; @@ -259,6 +384,7 @@ fn check_trait_wrong_method_return_type() { impl Default for Foo { fn default() -> Field { + ^^^^^^^ Expected type Foo, found type Field 0 } } @@ -267,29 +393,12 @@ fn check_trait_wrong_method_return_type() { let _ = Foo {}; // silence Foo never constructed warning } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for err in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_location: _, - }) => { - assert_eq!(expected_typ, "Foo"); - assert_eq!(expr_typ, "Field"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] fn check_trait_wrong_method_return_type2() { + // TODO: improve the error location let src = " trait Default { fn default(x: Field, y: Field) -> Self; @@ -302,6 +411,7 @@ fn check_trait_wrong_method_return_type2() { impl Default for Foo { fn default(x: Field, _y: Field) -> Field { + ^^^^^^^ Expected type Foo, found type Field x } } @@ -309,25 +419,7 @@ fn check_trait_wrong_method_return_type2() { fn main() { let _ = Foo { bar: 1, array: [2, 3] }; // silence Foo never constructed warning }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for err in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_location: _, - }) => { - assert_eq!(expected_typ, "Foo"); - assert_eq!(expr_typ, "Field"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -346,6 +438,8 @@ fn check_trait_missing_implementation() { } impl Default for Foo { + ^^^ Method `method2` from trait `Default` is not implemented + ~~~ Please implement method2 here fn default(x: Field, y: Field) -> Self { Self { bar: x, array: [x,y] } } @@ -354,25 +448,7 @@ fn check_trait_missing_implementation() { fn main() { } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for err in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::TraitMissingMethod { - trait_name, - method_name, - trait_impl_location: _, - }) => { - assert_eq!(trait_name, "Default"); - assert_eq!(method_name, "method2"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -383,8 +459,8 @@ fn check_trait_not_in_scope() { array: [Field; 2], } - // Default trait does not exist impl Default for Foo { + ^^^^^^^ Trait Default not found fn default(x: Field, y: Field) -> Self { Self { bar: x, array: [x,y] } } @@ -392,23 +468,8 @@ fn check_trait_not_in_scope() { fn main() { } - "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for err in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::TraitNotFound { - trait_path, - }) => { - assert_eq!(trait_path.as_string(), "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -422,9 +483,9 @@ fn check_trait_wrong_method_name() { array: [Field; 2], } - // wrong trait name method should not compile impl Default for Foo { fn does_not_exist(x: Field, y: Field) -> Self { + ^^^^^^^^^^^^^^ Method with name `does_not_exist` is not part of trait `Default`, therefore it can't be implemented Self { bar: x, array: [x,y] } } } @@ -432,32 +493,12 @@ fn check_trait_wrong_method_name() { fn main() { let _ = Foo { bar: 1, array: [2, 3] }; // silence Foo never constructed warning }"; - let compilation_errors = get_program_errors(src); - assert!(!has_parser_error(&compilation_errors)); - assert!( - compilation_errors.len() == 1, - "Expected 1 compilation error, got: {:?}", - compilation_errors - ); - - for err in compilation_errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::MethodNotInTrait { - trait_name, - impl_method, - }) => { - assert_eq!(trait_name, "Default"); - assert_eq!(impl_method, "does_not_exist"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] fn check_trait_wrong_parameter() { + // TODO: improve the error location let src = " trait Default { fn default(x: Field) -> Self; @@ -469,6 +510,7 @@ fn check_trait_wrong_parameter() { impl Default for Foo { fn default(x: u32) -> Self { + ^ Parameter #1 of method `default` must be of type Field, not u32 Foo {bar: x} } } @@ -476,27 +518,7 @@ fn check_trait_wrong_parameter() { fn main() { } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for err in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { - method_name, - expected_typ, - actual_typ, - .. - }) => { - assert_eq!(method_name, "default"); - assert_eq!(expected_typ, "Field"); - assert_eq!(actual_typ, "u32"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -513,34 +535,15 @@ fn check_trait_wrong_parameter2() { impl Default for Foo { fn default(x: Field, y: Foo) -> Self { + ^ Parameter #2 of method `default` must be of type Field, not Foo Self { bar: x, array: [x, y.bar] } } } fn main() { - }"; - - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - - for err in errors { - match &err { - CompilationError::TypeError(TypeCheckError::TraitMethodParameterTypeMismatch { - method_name, - expected_typ, - actual_typ, - .. - }) => { - assert_eq!(method_name, "default"); - assert_eq!(expected_typ, "Field"); - assert_eq!(actual_typ, "Foo"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; } + "; + check_errors(src); } #[test] @@ -548,30 +551,14 @@ fn check_trait_wrong_parameter_type() { let src = " pub trait Default { fn default(x: Field, y: NotAType) -> Field; + ^^^^^^^^ Could not resolve 'NotAType' in path } fn main(x: Field, y: Field) { assert(y == x); - }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - - // This is a duplicate error in the name resolver & type checker. - // In the elaborator there is no duplicate and only 1 error is issued - assert!(errors.len() <= 2, "Expected 1 or 2 errors, got: {:?}", errors); - - for err in errors { - match &err { - CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Unresolved(ident), - )) => { - assert_eq!(ident, "NotAType"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; } + "; + check_errors(src); } #[test] @@ -588,6 +575,7 @@ fn check_trait_wrong_parameters_count() { impl Default for Foo { fn default(x: Field) -> Self { + ^^^^^^^ `Default::default` expects 2 parameters, but this method has 1 Self { bar: x, array: [x, x] } } } @@ -595,28 +583,7 @@ fn check_trait_wrong_parameters_count() { fn main() { } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for err in errors { - match &err { - CompilationError::TypeError(TypeCheckError::MismatchTraitImplNumParameters { - actual_num_parameters, - expected_num_parameters, - trait_name, - method_name, - .. - }) => { - assert_eq!(actual_num_parameters, &1_usize); - assert_eq!(expected_num_parameters, &2_usize); - assert_eq!(method_name, "default"); - assert_eq!(trait_name, "Default"); - } - _ => { - panic!("No other errors are expected in this test case! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -627,6 +594,7 @@ fn check_trait_impl_for_non_type() { } impl Default for main { + ^^^^ expected type got function fn default(x: Field, y: Field) -> Field { x + y } @@ -634,20 +602,7 @@ fn check_trait_impl_for_non_type() { fn main() {} "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for err in errors { - match &err { - CompilationError::ResolverError(ResolverError::Expected { expected, got, .. }) => { - assert_eq!(*expected, "type"); - assert_eq!(*got, "function"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] @@ -663,8 +618,8 @@ fn check_impl_struct_not_trait() { z: Field, } - // Default is a struct not a trait impl Default for Foo { + ^^^^^^^ Default is not a trait, therefore it can't be implemented fn default(x: Field, y: Field) -> Self { Self { bar: x, array: [x,y] } } @@ -674,27 +629,15 @@ fn check_impl_struct_not_trait() { let _ = Default { x: 1, z: 1 }; // silence Default never constructed warning } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for err in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::NotATrait { - not_a_trait_name, - }) => { - assert_eq!(not_a_trait_name.to_string(), "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; - } + check_errors(src); } #[test] fn check_trait_duplicate_declaration() { let src = " trait Default { + ^^^^^^^ Duplicate definitions of trait definition with name Default found + ~~~~~~~ First trait definition found here fn default(x: Field, y: Field) -> Self; } @@ -709,32 +652,15 @@ fn check_trait_duplicate_declaration() { } } - trait Default { + ~~~~~~~ Second trait definition found here fn default(x: Field) -> Self; } fn main() { - }"; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for err in errors { - match &err { - CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ, - first_def, - second_def, - }) => { - assert_eq!(typ, &DuplicateType::Trait); - assert_eq!(first_def, "Default"); - assert_eq!(second_def, "Default"); - } - _ => { - panic!("No other errors are expected! Found = {:?}", err); - } - }; } + "; + check_errors(src); } #[test] @@ -747,20 +673,17 @@ fn check_trait_duplicate_implementation() { } impl Default for Foo { + ~~~~~~~ Previous impl defined here } impl Default for Foo { + ^^^ Impl for type `Foo` overlaps with existing impl + ~~~ Overlapping impl } fn main() { let _ = Foo { bar: 1 }; // silence Foo never constructed warning } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 1 errors, got: {:?}", errors); - assert!(matches!( - errors[0], - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { .. }) - )); + check_errors(src); } #[test] @@ -775,22 +698,19 @@ fn check_trait_duplicate_implementation_with_alias() { type MyType = MyStruct; impl Default for MyStruct { + ~~~~~~~ Previous impl defined here } impl Default for MyType { + ^^^^^^ Impl for type `MyType` overlaps with existing impl + ~~~~~~ Overlapping impl } fn main() { let _ = MyStruct {}; // silence MyStruct never constructed warning } "; - let errors = get_program_errors(src); - assert!(!has_parser_error(&errors)); - assert!(errors.len() == 1, "Expected 2 errors, got: {:?}", errors); - assert!(matches!( - errors[0], - CompilationError::DefinitionError(DefCollectorErrorKind::OverlappingImpl { .. }) - )); + check_errors(src); } #[test] @@ -808,7 +728,8 @@ fn test_impl_self_within_default_def() { fn ok(self) -> Self { self } - }"; + } + "; assert_no_errors(src); } @@ -935,6 +856,7 @@ fn resolve_empty_function() { "; assert_no_errors(src); } + #[test] fn resolve_basic_function() { let src = r#" @@ -945,24 +867,18 @@ fn resolve_basic_function() { "#; assert_no_errors(src); } + #[test] fn resolve_unused_var() { let src = r#" fn main(x : Field) { let y = x + x; + ^ unused variable y + ~ unused variable assert(x == x); } "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - // It should be regarding the unused variable - match &errors[0] { - CompilationError::ResolverError(ResolverError::UnusedVariable { ident }) => { - assert_eq!(&ident.0.contents, "y"); - } - _ => unreachable!("we should only have an unused var error"), - } + check_errors(src); } #[test] @@ -971,20 +887,11 @@ fn resolve_unresolved_var() { fn main(x : Field) { let y = x + x; assert(y == z); + ^ cannot find `z` in this scope + ~ not found in this scope } "#; - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - // It should be regarding the unresolved var `z` (Maybe change to undeclared and special case) - match &errors[0] { - CompilationError::ResolverError(ResolverError::VariableNotDeclared { - name, - location: _, - }) => { - assert_eq!(name, "z"); - } - _ => unimplemented!("we should only have an unresolved variable"), - } + check_errors(src); } #[test] @@ -992,23 +899,10 @@ fn unresolved_path() { let src = " fn main(x : Field) { let _z = some::path::to::a::func(x); + ^^^^ Could not resolve 'some' in path } "; - let errors = get_program_errors(src); - assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); - for compilation_error in errors { - match compilation_error { - CompilationError::ResolverError(err) => { - match err { - ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { - assert_eq!(name.to_string(), "some"); - } - _ => unimplemented!("we should only have an unresolved function"), - }; - } - _ => unimplemented!(), - } - } + check_errors(src); } #[test] @@ -1027,36 +921,16 @@ fn multiple_resolution_errors() { let src = r#" fn main(x : Field) { let y = foo::bar(x); + ^^^ Could not resolve 'foo' in path let z = y + a; + ^ cannot find `a` in this scope + ~ not found in this scope + ^ unused variable z + ~ unused variable + } "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 3, "Expected 3 errors, got: {:?}", errors); - - // Errors are: - // `a` is undeclared - // `z` is unused - // `foo::bar` does not exist - for compilation_error in errors { - match compilation_error { - CompilationError::ResolverError(err) => { - match err { - ResolverError::UnusedVariable { ident } => { - assert_eq!(&ident.0.contents, "z"); - } - ResolverError::VariableNotDeclared { name, .. } => { - assert_eq!(name, "a"); - } - ResolverError::PathResolutionError(PathResolutionError::Unresolved(name)) => { - assert_eq!(name.to_string(), "foo"); - } - _ => unimplemented!(), - }; - } - _ => unimplemented!(), - } - } + check_errors(src); } #[test] @@ -1138,8 +1012,8 @@ fn resolve_basic_closure() { #[test] fn resolve_simplified_closure() { // based on bug https://github.com/noir-lang/noir/issues/1088 - - let src = r#"fn do_closure(x: Field) -> Field { + let src = r#" + fn do_closure(x: Field) -> Field { let y = x; let ret_capture = || { y @@ -1204,40 +1078,20 @@ fn resolve_fmt_strings() { let src = r#" fn main() { let string = f"this is i: {i}"; + ^ cannot find `i` in this scope + ~ not found in this scope println(string); + ^^^^^^^^^^^^^^^ Unused expression result of type fmtstr<14, ()> let new_val = 10; println(f"random_string{new_val}{new_val}"); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unused expression result of type fmtstr<31, (Field, Field)> } fn println(x : T) -> T { x } "#; - - let errors = get_program_errors(src); - assert!(errors.len() == 3, "Expected 5 errors, got: {:?}", errors); - - for err in errors { - match &err { - CompilationError::ResolverError(ResolverError::VariableNotDeclared { - name, .. - }) => { - assert_eq!(name, "i"); - } - CompilationError::TypeError(TypeCheckError::UnusedResultError { - expr_type: _, - expr_location, - }) => { - let a = src - .get(expr_location.span.start() as usize..expr_location.span.end() as usize) - .unwrap(); - assert!( - a == "println(string)" || a == "println(f\"random_string{new_val}{new_val}\")" - ); - } - _ => unimplemented!(), - }; - } + check_errors(src); } fn monomorphize_program(src: &str) -> Result { @@ -1286,24 +1140,20 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { check_rewrite(src, expected_rewrite); } -// TODO(https://github.com/noir-lang/noir/issues/6780): currently failing -// with a stack overflow #[test] -#[ignore] fn deny_cyclic_globals() { let src = r#" global A: u32 = B; + ^ This global recursively depends on itself + ^ Dependency cycle found + ~ 'A' recursively depends on itself: A -> B -> A global B: u32 = A; + ^ Variable not in scope + ~ Could not find variable fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::ResolverError(ResolverError::DependencyCycle { .. }) - )); + check_errors(src); } #[test] @@ -1311,9 +1161,11 @@ fn deny_cyclic_type_aliases() { let src = r#" type A = B; type B = A; + ^^^^^^^^^^ Dependency cycle found + ~~~~~~~~~~ 'B' recursively depends on itself: B -> A -> B fn main() {} "#; - assert_eq!(get_program_errors(src).len(), 1); + check_errors(src); } #[test] @@ -1323,9 +1175,10 @@ fn ensure_nested_type_aliases_type_check() { type B = u8; fn main() { let _a: A = 0 as u16; + ^^^^^^^^ Expected type A, found type u16 } "#; - assert_eq!(get_program_errors(src).len(), 1); + check_errors(src); } #[test] @@ -1334,7 +1187,7 @@ fn type_aliases_in_entry_point() { type Foo = u8; fn main(_x: Foo) {} "#; - assert_eq!(get_program_errors(src).len(), 0); + assert_no_errors(src); } #[test] @@ -1346,7 +1199,7 @@ fn operators_in_global_used_in_type() { let _array: [Field; COUNT] = [1, 2, 3]; } "#; - assert_eq!(get_program_errors(src).len(), 0); + assert_no_errors(src); } #[test] @@ -1356,14 +1209,18 @@ fn break_and_continue_in_constrained_fn() { for i in 0 .. 10 { if i == 2 { continue; + ^^^^^^^^^ continue is only allowed in unconstrained functions + ~~~~~~~~~ Constrained code must always have a known number of loop iterations } if i == 5 { break; + ^^^^^^ break is only allowed in unconstrained functions + ~~~~~~ Constrained code must always have a known number of loop iterations } } } "#; - assert_eq!(get_program_errors(src).len(), 2); + check_errors(src); } #[test] @@ -1371,10 +1228,12 @@ fn break_and_continue_outside_loop() { let src = r#" unconstrained fn main() { continue; + ^^^^^^^^^ continue is only allowed within loops break; + ^^^^^^ break is only allowed within loops } "#; - assert_eq!(get_program_errors(src).len(), 2); + check_errors(src); } // Regression for #2540 @@ -1390,8 +1249,7 @@ fn for_loop_over_array() { hello(array); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } // Regression for #4545 @@ -1401,51 +1259,47 @@ fn type_aliases_in_main() { type Outer = [u8; N]; fn main(_arg: Outer<1>) {} "#; - assert_eq!(get_program_errors(src).len(), 0); + assert_no_errors(src); } #[test] fn ban_mutable_globals() { - // Mutable globals are only allowed in a comptime context let src = r#" mut global FOO: Field = 0; + ^^^ Only `comptime` globals may be mutable fn main() { let _ = FOO; // silence FOO never used warning } "#; - assert_eq!(get_program_errors(src).len(), 1); + check_errors(src); } #[test] fn deny_inline_attribute_on_unconstrained() { + // TODO: improve the error location let src = r#" #[no_predicates] unconstrained pub fn foo(x: Field, y: Field) { + ^^^ misplaced #[no_predicates] attribute on unconstrained function foo. Only allowed on constrained functions + ~~~ misplaced #[no_predicates] attribute assert(x != y); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::ResolverError(ResolverError::NoPredicatesAttributeOnUnconstrained { .. }) - )); + check_errors(src); } #[test] fn deny_fold_attribute_on_unconstrained() { + // TODO: improve the error location let src = r#" #[fold] unconstrained pub fn foo(x: Field, y: Field) { + ^^^ misplaced #[fold] attribute on unconstrained function foo. Only allowed on constrained functions + ~~~ misplaced #[fold] attribute assert(x != y); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::ResolverError(ResolverError::FoldAttributeOnUnconstrained { .. }) - )); + check_errors(src); } #[test] @@ -1475,8 +1329,7 @@ fn specify_function_types_with_turbofish() { let _ = generic_func::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -1509,8 +1362,7 @@ fn specify_method_types_with_turbofish() { let _ = foo.generic_method::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -1538,14 +1390,10 @@ fn incorrect_turbofish_count_function_call() { fn main() { let _ = generic_func::(); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected 2 generics from this function, but 3 were provided } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), - )); + check_errors(src); } #[test] @@ -1576,14 +1424,10 @@ fn incorrect_turbofish_count_method_call() { fn main() { let foo: Foo = Foo { inner: 1 }; let _ = foo.generic_method::(); + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected 1 generic from this function, but 2 were provided } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::IncorrectTurbofishGenericCount { .. }), - )); + check_errors(src); } #[test] @@ -1594,15 +1438,12 @@ fn struct_numeric_generic_in_function() { } pub fn bar() { + ^ N has a type of Foo. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`. + ~ Unsupported numeric generic type let _ = Foo { inner: 1 }; // silence Foo never constructed warning } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); + check_errors(src); } #[test] @@ -1613,19 +1454,18 @@ fn struct_numeric_generic_in_struct() { } pub struct Bar { } + ^ N has a type of Foo. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`. + ~ Unsupported numeric generic type "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType(_)), - )); + check_errors(src); } #[test] fn bool_numeric_generic() { let src = r#" pub fn read() -> Field { + ^ N has a type of bool. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`. + ~ Unsupported numeric generic type if N { 0 } else { @@ -1633,12 +1473,7 @@ fn bool_numeric_generic() { } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); + check_errors(src); } #[test] @@ -1647,49 +1482,30 @@ fn numeric_generic_binary_operation_type_mismatch() { pub fn foo() -> bool { let mut check: bool = true; check = N; + ^ Cannot assign an expression of type Field to a value of type bool check } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }), - )); + check_errors(src); } #[test] fn bool_generic_as_loop_bound() { - let src = r#" - pub fn read() { // error here - let mut fields = [0; N]; // error here - for i in 0..N { // error here + // TODO: improve the error location of the last error (should be just on N) + let src = r#" + pub fn read() { + ^ N has a type of bool. The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`. + ~ Unsupported numeric generic type + let mut fields = [0; N]; + ^ Expected kind numeric u32, found kind numeric bool + for i in 0..N { + ^^^^ Expected type Field, found type bool fields[i] = i + 1; } assert(fields[0] == 1); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - assert!(matches!( - errors[0], - CompilationError::ResolverError(ResolverError::UnsupportedNumericGenericType { .. }), - )); - - assert!(matches!( - errors[1], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, expr_typ, .. - }) = &errors[2] - else { - panic!("Got an error other than a type mismatch"); - }; - - assert_eq!(expected_typ, "Field"); - assert_eq!(expr_typ, "bool"); + check_errors(src); } #[test] @@ -1702,98 +1518,73 @@ fn numeric_generic_in_function_signature() { #[test] fn numeric_generic_as_struct_field_type_fails() { + // TODO: improve error message, in Rust it says "expected type, found const parameter `N`" + // which might be more understandable let src = r#" pub struct Foo { a: Field, b: N, + ^ Expected kind normal, found kind numeric u32 } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn normal_generic_as_array_length() { + // TODO: improve error location, should be just on N let src = r#" pub struct Foo { a: Field, b: [Field; N], + ^^^^^^^^^^ Expected kind numeric u32, found kind normal } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn numeric_generic_as_param_type() { + // TODO: improve the error message, see what Rust does let src = r#" pub fn foo(x: I) -> I { + ^ Expected kind normal, found kind numeric u32 + ^ Expected kind normal, found kind numeric u32 let _q: I = 5; + ^ Expected kind normal, found kind numeric u32 x } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - - // Error from the parameter type - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // Error from the let statement annotated type - assert!(matches!( - errors[1], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // Error from the return type - assert!(matches!( - errors[2], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn numeric_generic_as_unused_param_type() { + // TODO: improve the error message let src = r#" pub fn foo(_x: I) { } + ^ Expected kind normal, found kind numeric u32 "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn numeric_generic_as_unused_trait_fn_param_type() { + // TODO: improve the error message let src = r#" trait Foo { + ^^^ unused trait Foo + ~~~ unused trait fn foo(_x: I) { } + ^ Expected kind normal, found kind numeric u32 } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // Foo is unused - assert!( - matches!(errors[1], CompilationError::ResolverError(ResolverError::UnusedItem { .. }),) - ); + check_errors(src); } #[test] fn numeric_generic_as_return_type() { + // TODO: improve the error message let src = r#" // std::mem::zeroed() without stdlib trait Zeroed { @@ -1801,27 +1592,20 @@ fn numeric_generic_as_return_type() { } fn foo(x: T) -> I where T: Zeroed { + ^ Expected kind normal, found kind numeric Field + ^^^ unused function foo + ~~~ unused function x.zeroed() } fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - // Error from the return type - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // foo is unused - assert!( - matches!(errors[1], CompilationError::ResolverError(ResolverError::UnusedItem { .. }),) - ); + check_errors(src); } #[test] fn numeric_generic_used_in_nested_type_fails() { + // TODO: improve the error message let src = r#" pub struct Foo { a: Field, @@ -1829,33 +1613,26 @@ fn numeric_generic_used_in_nested_type_fails() { } pub struct Bar { inner: N + ^ Expected kind normal, found kind numeric u32 } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] fn normal_generic_used_in_nested_array_length_fail() { + // TODO: improve the error message let src = r#" pub struct Foo { a: Field, b: Bar, + ^ Expected kind numeric u32, found kind normal } pub struct Bar { inner: [Field; N] } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] @@ -1970,6 +1747,7 @@ fn numeric_generic_used_in_turbofish() { // allow u16 to be used as an array size #[test] fn numeric_generic_u16_array_size() { + // TODO: improve the error location (and maybe the message) let src = r#" fn len(_arr: [Field; N]) -> u32 { N @@ -1977,19 +1755,12 @@ fn numeric_generic_u16_array_size() { pub fn foo() -> u32 { let fields: [Field; N] = [0; N]; + ^^^^^^^^^^ Expected kind numeric u32, found kind numeric u16 + ^ Expected kind numeric u32, found kind numeric u16 len(fields) } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[1], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] @@ -2003,8 +1774,7 @@ fn numeric_generic_field_larger_than_u32() { let _ = foo::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -2027,8 +1797,7 @@ fn numeric_generic_field_arithmetic_larger_than_u32() { let _ = size(foo::()); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -2036,14 +1805,11 @@ fn cast_256_to_u8_size_checks() { let src = r#" fn main() { assert(256 as u8 == 0); + ^^^^^^^^^ Casting value of type Field to a smaller type (u8) + ~~~~~~~~~ casting untyped value (256) to a type with a maximum size (255) that's smaller than it } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::DownsizingCast { .. }), - )); + check_errors(src); } // TODO(https://github.com/noir-lang/noir/issues/6247): @@ -2055,8 +1821,7 @@ fn cast_negative_one_to_u8_size_checks() { assert((-1) as u8 != 0); } "#; - let errors = get_program_errors(src); - assert!(errors.is_empty()); + assert_no_errors(src); } #[test] @@ -2091,48 +1856,32 @@ fn normal_generic_used_when_numeric_expected_in_where_clause() { } pub fn read() -> T where T: Deserialize { + ^ Expected kind numeric u32, found kind normal T::deserialize([0, 1]) } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); + // TODO: improve the error location for the array (should be on N) let src = r#" trait Deserialize { fn deserialize(fields: [Field; N]) -> Self; } pub fn read() -> T where T: Deserialize { + ^ Expected kind numeric u32, found kind normal let mut fields: [Field; N] = [0; N]; + ^ Expected kind numeric u32, found kind normal + ^^^^^^^^^^ Expected kind numeric u32, found kind normal for i in 0..N { + ^ cannot find `N` in this scope + ~ not found in this scope fields[i] = i as Field + 1; } T::deserialize(fields) } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 4); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[1], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - assert!(matches!( - errors[2], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); - // N - assert!(matches!( - errors[3], - CompilationError::ResolverError(ResolverError::VariableNotDeclared { .. }), - )); + check_errors(src); } #[test] @@ -2146,6 +1895,7 @@ fn numeric_generics_type_kind_mismatch() { fn bar() -> u16 { foo::() + ^ Expected kind numeric u32, found kind numeric u16 } global M: u16 = 3; @@ -2154,12 +1904,7 @@ fn numeric_generics_type_kind_mismatch() { let _ = bar::(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }), - )); + check_errors(src); } #[test] @@ -2181,6 +1926,7 @@ fn numeric_generics_value_kind_mismatch_u32_u64() { pub fn push(&mut self, elem: T) { assert(self.len < MaxLen, "push out of bounds"); + ^^^^^^^^^^^^^^^^^ Integers must have the same bit width LHS is 64, RHS is 32 self.storage[self.len] = elem; self.len += 1; } @@ -2190,20 +1936,12 @@ fn numeric_generics_value_kind_mismatch_u32_u64() { let _ = BoundedVec { storage: [1], len: 1 }; // silence never constructed warning } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::IntegerBitWidth { - bit_width_x: IntegerBitSize::SixtyFour, - bit_width_y: IntegerBitSize::ThirtyTwo, - .. - }), - )); + check_errors(src); } #[test] fn quote_code_fragments() { + // TODO: have the error also point to `contact!` as a secondary // This test ensures we can quote (and unquote/splice) code fragments // which by themselves are not valid code. They only need to be valid // by the time they are unquoted into the macro's call site. @@ -2211,6 +1949,7 @@ fn quote_code_fragments() { fn main() { comptime { concat!(quote { assert( }, quote { false); }); + ^^^^^ Assertion failed } } @@ -2218,11 +1957,7 @@ fn quote_code_fragments() { quote { $a $b } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use InterpreterError::FailingConstraint; - assert!(matches!(&errors[0], CompilationError::InterpreterError(FailingConstraint { .. }))); + check_errors(src); } #[test] @@ -2234,6 +1969,7 @@ fn impl_stricter_than_trait_no_trait_method_constraints() { // We want to make sure we trigger the error when override a trait method // which itself has no trait constraints. fn serialize(self) -> [Field; N]; + ~~~~~~~~~ definition of `serialize` from trait } trait ToField { @@ -2255,6 +1991,8 @@ fn impl_stricter_than_trait_no_trait_method_constraints() { impl Serialize<2> for MyType { fn serialize(self) -> [Field; 2] where T: ToField { + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `T: ToField` [ self.a.to_field(), self.b.to_field() ] } } @@ -2269,13 +2007,7 @@ fn impl_stricter_than_trait_no_trait_method_constraints() { let _ = MyType { a: 1, b: 1 }; // silence MyType never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0], - CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { .. }) - )); + check_errors(src); } #[test] @@ -2288,26 +2020,18 @@ fn impl_stricter_than_trait_different_generics() { fn foo_good() where T: Default; fn foo_bad() where T: Default; + ~~~~~~~ definition of `foo_bad` from trait } impl Foo for () { fn foo_good() where A: Default {} fn foo_bad() where B: Default {} + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `B: Default` } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - .. - }) = &errors[0] - { - assert!(matches!(constraint_typ.to_string().as_str(), "B")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0]); - } + check_errors(src); } #[test] @@ -2329,14 +2053,17 @@ fn impl_stricter_than_trait_different_object_generics() { fn bar_good() where Option: MyTrait, OtherOption>: OtherTrait; fn bar_bad() where Option: MyTrait, OtherOption>: OtherTrait; + ~~~~~~~ definition of `bar_bad` from trait fn array_good() where [T; 8]: MyTrait; fn array_bad() where [T; 8]: MyTrait; + ~~~~~~~~~ definition of `array_bad` from trait fn tuple_good() where (Option, Option): MyTrait; fn tuple_bad() where (Option, Option): MyTrait; + ~~~~~~~~~ definition of `tuple_bad` from trait } impl Bar for () { @@ -2349,58 +2076,27 @@ fn impl_stricter_than_trait_different_object_generics() { where OtherOption>: OtherTrait, Option: MyTrait { } + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `Option: MyTrait` fn array_good() where [A; 8]: MyTrait { } fn array_bad() where [B; 8]: MyTrait { } + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `[B; 8]: MyTrait` fn tuple_good() where (Option, Option): MyTrait { } fn tuple_bad() where (Option, Option): MyTrait { } + ^^^^^^^ impl has stricter requirements than trait + ~~~~~~~ impl has extra requirement `(Option, Option): MyTrait` } fn main() { let _ = OtherOption { inner: Option { inner: 1 } }; // silence unused warnings } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 3); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0] - { - assert!(matches!(constraint_typ.to_string().as_str(), "Option")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0]); - } - - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[1] - { - assert!(matches!(constraint_typ.to_string().as_str(), "[B; 8]")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0]); - } - - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[2] - { - assert!(matches!(constraint_typ.to_string().as_str(), "(Option, Option)")); - assert!(matches!(constraint_name.as_str(), "MyTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0]); - } + check_errors(src); } #[test] @@ -2416,32 +2112,22 @@ fn impl_stricter_than_trait_different_trait() { trait Bar { fn bar() where Option: Default; + ~~~ definition of `bar` from trait } impl Bar for () { // Trait constraint differs due to the trait even though the constraint // types are the same. fn bar() where Option: OtherDefault {} + ^^^^^^^^^^^^ impl has stricter requirements than trait + ~~~~~~~~~~~~ impl has extra requirement `Option: OtherDefault` } fn main() { let _ = Option { inner: 1 }; // silence Option never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0] - { - assert!(matches!(constraint_typ.to_string().as_str(), "Option")); - assert!(matches!(constraint_name.as_str(), "OtherDefault")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0]); - } + check_errors(src); } #[test] @@ -2451,6 +2137,7 @@ fn trait_impl_where_clause_stricter_pass() { fn good_foo() where H: OtherTrait; fn bad_foo() where H: OtherTrait; + ~~~~~~~ definition of `bad_foo` from trait } trait OtherTrait {} @@ -2463,26 +2150,15 @@ fn trait_impl_where_clause_stricter_pass() { fn good_foo() where B: OtherTrait { } fn bad_foo() where A: OtherTrait { } + ^^^^^^^^^^ impl has stricter requirements than trait + ~~~~~~~~~~ impl has extra requirement `A: OtherTrait` } fn main() { let _ = Option { inner: 1 }; // silence Option never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - .. - }) = &errors[0] - { - assert!(matches!(constraint_typ.to_string().as_str(), "A")); - assert!(matches!(constraint_name.as_str(), "OtherTrait")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0]); - } + check_errors(src); } #[test] @@ -2490,31 +2166,19 @@ fn impl_stricter_than_trait_different_trait_generics() { let src = r#" trait Foo { fn foo() where T: T2; + ~~~ definition of `foo` from trait } impl Foo for () { // Should be A: T2 fn foo() where A: T2 {} + ^^ impl has stricter requirements than trait + ~~ impl has extra requirement `A: T2` } trait T2 {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - if let CompilationError::DefinitionError(DefCollectorErrorKind::ImplIsStricterThanTrait { - constraint_typ, - constraint_name, - constraint_generics, - .. - }) = &errors[0] - { - assert!(matches!(constraint_typ.to_string().as_str(), "A")); - assert!(matches!(constraint_name.as_str(), "T2")); - assert!(matches!(constraint_generics.ordered[0].to_string().as_str(), "B")); - } else { - panic!("Expected DefCollectorErrorKind::ImplIsStricterThanTrait but got {:?}", errors[0]); - } + check_errors(src); } #[test] @@ -2551,6 +2215,8 @@ fn impl_not_found_for_inner_impl() { impl MyType { fn do_thing_with_serialization_with_extra_steps(self) -> Field { process_array(serialize_thing(self)) + ^^^^^^^^^^^^^^^ No matching impl found for `T: ToField` + ~~~~~~~~~~~~~~~ No impl for `T: ToField` } } @@ -2558,13 +2224,7 @@ fn impl_not_found_for_inner_impl() { let _ = MyType { a: 1, b: 1 }; // silence MyType never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0], - CompilationError::TypeError(TypeCheckError::NoMatchingImplFound { .. }) - )); + check_errors(src); } #[test] @@ -2572,16 +2232,12 @@ fn cannot_call_unconstrained_function_outside_of_unsafe() { let src = r#" fn main() { foo(); + ^^^^^ Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block } unconstrained fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0] else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0]); - }; + check_errors(src); } #[test] @@ -2589,26 +2245,19 @@ fn cannot_call_unconstrained_first_class_function_outside_of_unsafe() { let src = r#" fn main() { let func = foo; - // Warning should trigger here func(); + ^^^^^^ Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block inner(func); } fn inner(x: unconstrained fn() -> ()) { - // Warning should trigger here x(); + ^^^ Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block } unconstrained fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - for error in &errors { - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = error else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0]); - }; - } + check_errors(src); } #[test] @@ -2642,15 +2291,11 @@ fn missing_unsafe_block_when_needing_type_annotations() { impl BigNumTrait for BigNum { fn __is_zero(self) -> bool { self.__is_zero_impl() + ^^^^^^^^^^^^^^^^^^^ Call to unconstrained function is unsafe and must be in an unconstrained function or unsafe block } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Unsafe { .. }) = &errors[0] else { - panic!("Expected an 'unsafe' error, got {:?}", errors[0]); - }; + check_errors(src); } #[test] @@ -2659,6 +2304,7 @@ fn cannot_pass_unconstrained_function_to_regular_function() { fn main() { let func = foo; expect_regular(func); + ^^^^ Converting an unconstrained fn to a non-unconstrained fn is unsafe } unconstrained fn foo() {} @@ -2666,12 +2312,7 @@ fn cannot_pass_unconstrained_function_to_regular_function() { fn expect_regular(_func: fn() -> ()) { } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0] else { - panic!("Expected an UnsafeFn error, got {:?}", errors[0]); - }; + check_errors(src); } #[test] @@ -2679,24 +2320,13 @@ fn cannot_assign_unconstrained_and_regular_fn_to_variable() { let src = r#" fn main() { let _func = if true { foo } else { bar }; + ^^^ Expected type fn() -> (), found type unconstrained fn() -> () } fn foo() {} unconstrained fn bar() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0] else { - panic!("Expected a context error, got {:?}", errors[0]); - }; - - if let TypeCheckError::TypeMismatch { expected_typ, expr_typ, .. } = err.as_ref() { - assert_eq!(expected_typ, "fn() -> ()"); - assert_eq!(expr_typ, "unconstrained fn() -> ()"); - } else { - panic!("Expected a type mismatch error, got {:?}", errors[0]); - }; + check_errors(src); } #[test] @@ -2720,18 +2350,14 @@ fn cannot_pass_unconstrained_function_to_constrained_function() { fn main() { let func = foo; expect_regular(func); + ^^^^ Converting an unconstrained fn to a non-unconstrained fn is unsafe } unconstrained fn foo() {} fn expect_regular(_func: fn() -> ()) {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::UnsafeFn { .. }) = &errors[0] else { - panic!("Expected an UnsafeFn error, got {:?}", errors[0]); - }; + check_errors(src); } #[test] @@ -2768,24 +2394,11 @@ fn trait_impl_generics_count_mismatch() { trait Foo {} impl Foo<()> for Field {} + ^^^ Foo expects 0 generics but 1 was given - fn main() {}"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - item, - expected, - found, - .. - }) = &errors[0] - else { - panic!("Expected a generic count mismatch error, got {:?}", errors[0]); - }; - - assert_eq!(item, "Foo"); - assert_eq!(*expected, 0); - assert_eq!(*found, 1); + fn main() {} + "#; + check_errors(src); } #[test] @@ -2800,31 +2413,19 @@ fn bit_not_on_untyped_integer() { #[test] fn duplicate_struct_field() { + // TODO: the primary error location should be on the second field let src = r#" pub struct Foo { x: i32, + ^ Duplicate definitions of struct field with name x found + ~ First struct field found here x: i32, + ~ Second struct field found here } fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::Duplicate { - typ: _, - first_def, - second_def, - }) = &errors[0] - else { - panic!("Expected a 'duplicate' error, got {:?}", errors[0]); - }; - - assert_eq!(first_def.to_string(), "x"); - assert_eq!(second_def.to_string(), "x"); - - assert_eq!(first_def.span().start(), 30); - assert_eq!(second_def.span().start(), 46); + check_errors(src); } #[test] @@ -2862,23 +2463,12 @@ fn incorrect_generic_count_on_struct_impl() { let src = r#" struct Foo {} impl Foo {} + ^^^ Foo expects 0 generics but 1 was given fn main() { let _ = Foo {}; // silence Foo never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - found, expected, .. - }) = errors[0] - else { - panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0]); - }; - - assert_eq!(found, 1); - assert_eq!(expected, 0); + check_errors(src); } #[test] @@ -2886,23 +2476,12 @@ fn incorrect_generic_count_on_type_alias() { let src = r#" pub struct Foo {} pub type Bar = Foo; + ^^^ Foo expects 0 generics but 1 was given fn main() { let _ = Foo {}; // silence Foo never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - found, expected, .. - }) = errors[0] - else { - panic!("Expected an incorrect generic count mismatch error, got {:?}", errors[0]); - }; - - assert_eq!(found, 1); - assert_eq!(expected, 0); + check_errors(src); } #[test] @@ -2959,14 +2538,18 @@ fn uses_self_type_in_trait_where_clause() { } pub trait Foo where Self: Trait { + ~~~~~ required by this bound in `Foo fn foo(self) -> bool { self.trait_func() + ^^^^^^^^^^^^^^^^^ No method named 'trait_func' found for type 'Bar' } } struct Bar {} impl Foo for Bar { + ^^^ The trait bound `_: Trait` is not satisfied + ~~~ The trait `Trait` is not implemented for `_ } @@ -2974,22 +2557,7 @@ fn uses_self_type_in_trait_where_clause() { let _ = Bar {}; // silence Bar never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - let CompilationError::ResolverError(ResolverError::TraitNotImplemented { .. }) = &errors[0] - else { - panic!("Expected a trait not implemented error, got {:?}", errors[0]); - }; - - let CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { method_name, .. }) = - &errors[1] - else { - panic!("Expected an unresolved method call error, got {:?}", errors[1]); - }; - - assert_eq!(method_name, "trait_func"); + check_errors(src); } #[test] @@ -3017,13 +2585,10 @@ fn error_on_cast_over_type_variable() { fn main() { let x = "a"; let _: Field = foo(|x| x as Field, x); + ^ Expected type Field, found type str<1> } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!(errors[0], CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }))); + check_errors(src); } #[test] @@ -3106,15 +2671,9 @@ fn impl_missing_associated_type() { } impl Foo for () {} + ^^^ `Foo` is missing the associated type `Assoc` "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - &errors[0], - CompilationError::TypeError(TypeCheckError::MissingNamedTypeArg { .. }) - )); + check_errors(src); } #[test] @@ -3134,22 +2693,12 @@ fn as_trait_path_syntax_resolves_outside_impl() { // AsTraitPath syntax is a bit silly when associated types // are explicitly specified let _: i64 = 1 as >::Assoc; + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected type i64, found type i32 let _ = Bar {}; // silence Bar never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use CompilationError::TypeError; - use TypeCheckError::TypeMismatch; - let TypeError(TypeMismatch { expected_typ, expr_typ, .. }) = errors[0].clone() else { - panic!("Expected TypeMismatch error, found {:?}", errors[0]); - }; - - assert_eq!(expected_typ, "i64".to_string()); - assert_eq!(expr_typ, "i32".to_string()); + check_errors(src); } #[test] @@ -3167,70 +2716,69 @@ fn as_trait_path_syntax_no_impl() { fn main() { let _: i64 = 1 as >::Assoc; + ^^^ No matching impl found for `Bar: Foo` + ~~~ No impl for `Bar: Foo` let _ = Bar {}; // silence Bar never constructed warning } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use CompilationError::TypeError; - assert!(matches!(&errors[0], TypeError(TypeCheckError::NoMatchingImplFound { .. }))); + check_errors(src); } #[test] -fn dont_infer_globals_to_u32_from_type_use() { +fn do_not_infer_globals_to_u32_from_type_use() { + // TODO: improve the error location (maybe it should be on the global name) let src = r#" global ARRAY_LEN = 3; + ^ Globals must have a specified type + ~ Inferred type is `Field` global STR_LEN: _ = 2; + ^ Globals must have a specified type + ~ Inferred type is `Field` global FMT_STR_LEN = 2; + ^ Globals must have a specified type + ~ Inferred type is `Field` fn main() { let _a: [u32; ARRAY_LEN] = [1, 2, 3]; + ^^^^^^^^^^^^^^^^ Expected kind numeric u32, found kind numeric Field let _b: str = "hi"; + ^^^^^^^^^^^^ Expected kind numeric u32, found kind numeric Field let _c: fmtstr = f"hi"; + ^^^^^^^^^^^^^^^^^^^^^^ Expected kind numeric u32, found kind numeric Field } "#; - - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 6); - for error in errors.drain(0..3) { - assert!(matches!( - error, - CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) - )); - } - for error in errors { - assert!(matches!( - error, - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) - )); - } + check_errors(src); } #[test] -fn dont_infer_partial_global_types() { +fn do_not_infer_partial_global_types() { let src = r#" pub global ARRAY: [Field; _] = [0; 3]; + ^^^^^^ Globals must have a specified type + ~~~~~~ Inferred type is `[Field; 3]` pub global NESTED_ARRAY: [[Field; _]; 3] = [[]; 3]; + ^^^^^^^ Globals must have a specified type + ~~~~~~~ Inferred type is `[[Field; 0]; 3]` pub global STR: str<_> = "hi"; + ^^^^ Globals must have a specified type + ~~~~ Inferred type is `str<2>` + pub global NESTED_STR: [str<_>] = &["hi"]; + ^^^^^^^ Globals must have a specified type + ~~~~~~~ Inferred type is `[str<2>]` pub global FORMATTED_VALUE: str<5> = "there"; pub global FMT_STR: fmtstr<_, _> = f"hi {FORMATTED_VALUE}"; - pub global TUPLE_WITH_MULTIPLE: ([str<_>], [[Field; _]; 3]) = (&["hi"], [[]; 3]); + ^^^^^^^^^^^^^^^^^^^^^^^ Globals must have a specified type + ~~~~~~~~~~~~~~~~~~~~~~~ Inferred type is `fmtstr<20, (str<5>)>` + pub global TUPLE_WITH_MULTIPLE: ([str<_>], [[Field; _]; 3]) = + (&["hi"], [[]; 3]); + ^^^^^^^^^^^^^^^^^^ Globals must have a specified type + ~~~~~~~~~~~~~~~~~~ Inferred type is `([str<2>], [[Field; 0]; 3])` fn main() { } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 6); - for error in errors { - assert!(matches!( - error, - CompilationError::ResolverError(ResolverError::UnspecifiedGlobalType { .. }) - )); - } + check_errors(src); } #[test] @@ -3246,9 +2794,7 @@ fn u32_globals_as_sizes_in_types() { let _c: fmtstr = f"hi"; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -3260,6 +2806,8 @@ fn struct_array_len() { impl Array { pub fn len(self) -> u32 { + ^^^^ unused variable self + ~~~~ unused variable N as u32 } } @@ -3271,17 +2819,11 @@ fn struct_array_len() { assert(ys.len() == 2); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::ResolverError(ResolverError::UnusedVariable { .. }) - )); + check_errors(src); } // TODO(https://github.com/noir-lang/noir/issues/6245): -// support u16 as an array size +// support u8 as an array size #[test] fn non_u32_as_array_length() { let src = r#" @@ -3289,15 +2831,10 @@ fn non_u32_as_array_length() { fn main() { let _a: [u32; ARRAY_LEN] = [1, 2, 3]; + ^^^^^^^^^^^^^^^^ Expected kind numeric u32, found kind numeric u8 } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::TypeKindMismatch { .. }) - )); + check_errors(src); } #[test] @@ -3309,9 +2846,7 @@ fn use_non_u32_generic_in_struct() { let _: S<3> = S {}; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -3334,10 +2869,7 @@ fn use_numeric_generic_in_trait_method() { let _ = Bar{}.foo(bytes); } "#; - - let errors = get_program_errors(src); - println!("{errors:?}"); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -3363,26 +2895,17 @@ fn trait_unconstrained_methods_typechecked_correctly() { assert_eq(2.foo(), 2.identity() as Field); } "#; - - let errors = get_program_errors(src); - println!("{errors:?}"); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] fn error_if_attribute_not_in_scope() { let src = r#" #[not_in_scope] + ^^^^^^^^^^^^^^^ Attribute function `not_in_scope` is not in scope fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0], - CompilationError::ResolverError(ResolverError::AttributeFunctionNotInScope { .. }) - )); + check_errors(src); } #[test] @@ -3395,9 +2918,7 @@ fn arithmetic_generics_rounding_pass() { fn round(_x: [Field; N / M * M]) {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -3407,14 +2928,12 @@ fn arithmetic_generics_rounding_fail() { // Do not simplify N/M*M to just N // This should be 3/2*2 = 2, not 3 round::<3, 2>([1, 2, 3]); + ^^^^^^^^^ Expected type [Field; 2], found type [Field; 3] } fn round(_x: [Field; N / M * M]) {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!(errors[0], CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }))); + check_errors(src); } #[test] @@ -3432,12 +2951,10 @@ fn arithmetic_generics_rounding_fail_on_struct() { // Do not simplify N/M*M to just N // This should be 3/2*2 = 2, not 3 let _: W<3> = foo(w_3, w_2); + ^^^^^^^^^^^^^ Expected type W<3>, found type W<2> } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!(errors[0], CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }))); + check_errors(src); } #[test] @@ -3450,42 +2967,58 @@ fn unconditional_recursion_fail() { let srcs = vec![ r#" fn main() { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing main() } "#, r#" fn main() -> pub bool { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing if main() { true } else { false } } "#, r#" fn main() -> pub bool { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing if true { main() } else { main() } } "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing main() + main() } "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing 1 + main() } "#, r#" fn main() -> pub bool { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing let _ = main(); true } "#, r#" fn main(a: u64, b: u64) -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing main(a + b, main(a, b)) } "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing foo(1, main()) } fn foo(a: u64, b: u64) -> u64 { @@ -3494,12 +3027,16 @@ fn unconditional_recursion_fail() { "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing let (a, b) = (main(), main()); a + b } "#, r#" fn main() -> pub u64 { + ^^^^ function `main` cannot return without recursing + ~~~~ function cannot return without recursing let mut sum = 0; for i in 0 .. main() { sum += i; @@ -3510,19 +3047,7 @@ fn unconditional_recursion_fail() { ]; for src in srcs { - let errors = get_program_errors(src); - assert!( - !errors.is_empty(), - "expected 'unconditional recursion' error, got nothing; src = {src}" - ); - - for error in errors { - let CompilationError::ResolverError(ResolverError::UnconditionalRecursion { .. }) = - error - else { - panic!("Expected an 'unconditional recursion' error, got {:?}; src = {src}", error); - }; - } + check_errors(src); } } @@ -3722,117 +3247,89 @@ fn errors_with_better_message_when_trying_to_invoke_struct_field_that_is_a_funct impl Foo { fn call(self) -> bool { self.wrapped(1) + ^^^^^^^^^^^^^^^ Cannot invoke function field 'wrapped' on type 'Foo' as a method + ~~~~~~~~~~~~~~~ to call the function stored in 'wrapped', surround the field access with parentheses: '(', ')' } } fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::CannotInvokeStructFieldFunctionType { - method_name, - .. - }) = &errors[0] - else { - panic!("Expected a 'CannotInvokeStructFieldFunctionType' error, got {:?}", errors[0]); - }; - - assert_eq!(method_name, "wrapped"); -} - -fn test_disallows_attribute_on_impl_method( - attr: &str, - check_error: impl FnOnce(&CompilationError), -) { - let src = format!( - " - pub struct Foo {{ }} - - impl Foo {{ - #[{attr}] - fn foo() {{ }} - }} - - fn main() {{ }} - " - ); - let errors = get_program_errors(&src); - assert_eq!(errors.len(), 1); - check_error(&errors[0]); -} - -fn test_disallows_attribute_on_trait_impl_method( - attr: &str, - check_error: impl FnOnce(&CompilationError), -) { - let src = format!( - " - pub trait Trait {{ - fn foo() {{ }} - }} - - pub struct Foo {{ }} - - impl Trait for Foo {{ - #[{attr}] - fn foo() {{ }} - }} - - fn main() {{ }} - " - ); - let errors = get_program_errors(&src); - assert_eq!(errors.len(), 1); - check_error(&errors[0]); + check_errors(src); } #[test] fn disallows_test_attribute_on_impl_method() { - test_disallows_attribute_on_impl_method("test", |error| { - assert!(matches!( - error, - CompilationError::DefinitionError( - DefCollectorErrorKind::TestOnAssociatedFunction { .. } - ) - )); - }); + // TODO: improve the error location + let src = " + pub struct Foo { } + + impl Foo { + #[test] + fn foo() { } + ^^^ The `#[test]` attribute is disallowed on `impl` methods + } + + fn main() { } + "; + check_errors(src); } #[test] fn disallows_test_attribute_on_trait_impl_method() { - test_disallows_attribute_on_trait_impl_method("test", |error| { - assert!(matches!( - error, - CompilationError::DefinitionError( - DefCollectorErrorKind::TestOnAssociatedFunction { .. } - ) - )); - }); + let src = " + pub trait Trait { + fn foo() { } + } + + pub struct Foo { } + + impl Trait for Foo { + #[test] + fn foo() { } + ^^^ The `#[test]` attribute is disallowed on `impl` methods + } + + fn main() { } + "; + check_errors(src); } #[test] fn disallows_export_attribute_on_impl_method() { - test_disallows_attribute_on_impl_method("export", |error| { - assert!(matches!( - error, - CompilationError::DefinitionError( - DefCollectorErrorKind::ExportOnAssociatedFunction { .. } - ) - )); - }); + // TODO: improve the error location + let src = " + pub struct Foo { } + + impl Foo { + #[export] + fn foo() { } + ^^^ The `#[export]` attribute is disallowed on `impl` methods + } + + fn main() { } + "; + check_errors(src); } #[test] fn disallows_export_attribute_on_trait_impl_method() { - test_disallows_attribute_on_trait_impl_method("export", |error| { - assert!(matches!( - error, - CompilationError::DefinitionError( - DefCollectorErrorKind::ExportOnAssociatedFunction { .. } - ) - )); - }); + // TODO: improve the error location + let src = " + pub trait Trait { + fn foo() { } + } + + pub struct Foo { } + + impl Trait for Foo { + #[export] + fn foo() { } + ^^^ The `#[export]` attribute is disallowed on `impl` methods + } + + fn main() { } + "; + check_errors(src); } #[test] @@ -3851,38 +3348,27 @@ fn disallows_underscore_on_right_hand_side() { fn main() { let _ = 1; let _x = _; + ^ in expressions, `_` can only be used on the left-hand side of an assignment + ~ `_` not allowed here } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::VariableNotDeclared { name, .. }) = - &errors[0] - else { - panic!("Expected a VariableNotDeclared error, got {:?}", errors[0]); - }; - - assert_eq!(name, "_"); + check_errors(src); } #[test] fn errors_on_cyclic_globals() { let src = r#" pub comptime global A: u32 = B; + ^ This global recursively depends on itself + ^ Dependency cycle found + ~ 'A' recursively depends on itself: A -> B -> A pub comptime global B: u32 = A; + ^ Variable not in scope + ~ Could not find variable fn main() { } "#; - let errors = get_program_errors(src); - - assert!(errors.iter().any(|error| matches!( - error, - CompilationError::InterpreterError(InterpreterError::GlobalsDependencyCycle { .. }) - ))); - assert!(errors.iter().any(|error| matches!( - error, - CompilationError::ResolverError(ResolverError::DependencyCycle { .. }) - ))); + check_errors(src); } #[test] @@ -3891,18 +3377,14 @@ fn warns_on_unneeded_unsafe() { fn main() { // Safety: test unsafe { + ^^^^^^ Unnecessary `unsafe` block foo() } } fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0], - CompilationError::TypeError(TypeCheckError::UnnecessaryUnsafeBlock { .. }) - )); + check_errors(src); } #[test] @@ -3913,6 +3395,8 @@ fn warns_on_nested_unsafe() { unsafe { // Safety: test unsafe { + ^^^^^^ Unnecessary `unsafe` block + ~~~~~~ Because it's nested inside another `unsafe` block foo() } } @@ -3920,14 +3404,7 @@ fn warns_on_nested_unsafe() { unconstrained fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - let CompilationError::TypeError(TypeCheckError::NestedUnsafeBlock { location }) = &errors[0] - else { - panic!("Expected NestedUnsafeBlock"); - }; - - assert_eq!(&src[location.span.start() as usize..location.span.end() as usize], "unsafe"); + check_errors(src); } #[test] @@ -4226,14 +3703,11 @@ fn errors_on_empty_loop_no_break() { unconstrained fn foo() { loop {} + ^^^^ `loop` must have at least one `break` in it + ~~~~ Infinite loops are disallowed } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0], - CompilationError::ResolverError(ResolverError::LoopWithoutBreak { .. }) - )); + check_errors(src); } #[test] @@ -4249,6 +3723,8 @@ fn errors_on_loop_without_break() { unconstrained fn foo() { let mut x = 1; loop { + ^^^^ `loop` must have at least one `break` in it + ~~~~ Infinite loops are disallowed x += 1; bar(x); } @@ -4256,12 +3732,7 @@ fn errors_on_loop_without_break() { fn bar(_: Field) {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0], - CompilationError::ResolverError(ResolverError::LoopWithoutBreak { .. }) - )); + check_errors(src); } #[test] @@ -4277,6 +3748,8 @@ fn errors_on_loop_without_break_with_nested_loop() { unconstrained fn foo() { let mut x = 1; loop { + ^^^^ `loop` must have at least one `break` in it + ~~~~ Infinite loops are disallowed x += 1; bar(x); loop { @@ -4288,12 +3761,7 @@ fn errors_on_loop_without_break_with_nested_loop() { fn bar(_: Field) {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - &errors[0], - CompilationError::ResolverError(ResolverError::LoopWithoutBreak { .. }) - )); + check_errors(src); } #[test] @@ -4318,16 +3786,11 @@ fn errors_on_if_without_else_type_mismatch() { fn main() { if true { 1 + ^ Expected type Field, found type () } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::Context { err, .. }) = &errors[0] else { - panic!("Expected a Context error"); - }; - assert!(matches!(**err, TypeCheckError::TypeMismatch { .. })); + check_errors(src); } #[test] @@ -4342,15 +3805,11 @@ fn errors_if_for_body_type_is_not_unit() { fn main() { for _ in 0..1 { 1 + ^ Expected type (), found type Field } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) = &errors[0] else { - panic!("Expected a TypeMismatch error"); - }; + check_errors(src); } #[test] @@ -4361,15 +3820,11 @@ fn errors_if_loop_body_type_is_not_unit() { if false { break; } 1 + ^ Expected type (), found type Field } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) = &errors[0] else { - panic!("Expected a TypeMismatch error"); - }; + check_errors(src); } #[test] @@ -4378,36 +3833,30 @@ fn errors_if_while_body_type_is_not_unit() { unconstrained fn main() { while 1 == 1 { 1 + ^ Expected type (), found type Field } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }) = &errors[0] else { - panic!("Expected a TypeMismatch error"); - }; + check_errors(src); } #[test] fn check_impl_duplicate_method_without_self() { + // TODO: the primary error location should be n the second `foo` let src = " pub struct Foo {} impl Foo { fn foo() {} + ^^^ duplicate definitions of foo found + ~~~ first definition found here fn foo() {} + ~~~ second definition found here } fn main() {} "; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0], - CompilationError::ResolverError(ResolverError::DuplicateDefinition { .. }) - )); + check_errors(src); } #[test] diff --git a/compiler/noirc_frontend/src/tests/arithmetic_generics.rs b/compiler/noirc_frontend/src/tests/arithmetic_generics.rs index 83de9c077ab..bcbdbdd6211 100644 --- a/compiler/noirc_frontend/src/tests/arithmetic_generics.rs +++ b/compiler/noirc_frontend/src/tests/arithmetic_generics.rs @@ -2,11 +2,10 @@ use acvm::{AcirField, FieldElement}; -use super::get_program_errors; use crate::hir::type_check::TypeCheckError; use crate::hir_def::types::{BinaryTypeOperator, Type}; use crate::monomorphization::errors::MonomorphizationError; -use crate::tests::get_monomorphization_error; +use crate::tests::{assert_no_errors, get_monomorphization_error}; #[test] fn arithmetic_generics_canonicalization_deduplication_regression() { @@ -23,8 +22,7 @@ fn arithmetic_generics_canonicalization_deduplication_regression() { }; } "#; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); } #[test] @@ -52,9 +50,7 @@ fn checked_casts_do_not_prevent_canonicalization() { } } "#; - let errors = get_program_errors(source); - println!("{:?}", errors); - assert_eq!(errors.len(), 0); + assert_no_errors(source); } #[test] @@ -76,9 +72,7 @@ fn arithmetic_generics_checked_cast_zeros() { bar(w) } "#; - - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); let monomorphization_error = get_monomorphization_error(source); assert!(monomorphization_error.is_some()); @@ -123,9 +117,7 @@ fn arithmetic_generics_checked_cast_indirect_zeros() { let _ = bar(w); } "#; - - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); let monomorphization_error = get_monomorphization_error(source); assert!(monomorphization_error.is_some()); @@ -166,8 +158,7 @@ fn global_numeric_generic_larger_than_u32() { let _ = foo::(); } "#; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); } #[test] @@ -196,6 +187,5 @@ fn global_arithmetic_generic_larger_than_u32() { let _ = foo::().size(); } "#; - let errors = get_program_errors(source); - assert_eq!(errors.len(), 0); + assert_no_errors(source); } diff --git a/compiler/noirc_frontend/src/tests/bound_checks.rs b/compiler/noirc_frontend/src/tests/bound_checks.rs index 3a9efd716de..6a084e68c7e 100644 --- a/compiler/noirc_frontend/src/tests/bound_checks.rs +++ b/compiler/noirc_frontend/src/tests/bound_checks.rs @@ -1,24 +1,14 @@ -use crate::hir::def_collector::dc_crate::CompilationError; - -use super::get_program_errors; +use crate::tests::check_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] { - assert_eq!( - error.to_string(), - "The value `256` cannot fit into `u8` which has range `0..=255`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0]); - } + ^^^ The value `256` cannot fit into `u8` which has range `0..=255` + } + "#; + check_errors(src); } #[test] @@ -26,18 +16,10 @@ 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] { - 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]); - } + ^^ The value `-1` cannot fit into `u8` which has range `0..=255` + } + "#; + check_errors(src); } #[test] @@ -45,18 +27,10 @@ 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] { - assert_eq!( - error.to_string(), - "The value `128` cannot fit into `i8` which has range `-128..=127`" - ); - } else { - panic!("Expected OverflowingAssignment error, got {:?}", errors[0]); - } + ^^^ The value `128` cannot fit into `i8` which has range `-128..=127` + } + "#; + check_errors(src); } #[test] @@ -64,16 +38,8 @@ 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] { - 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]); - } + ^^^^ The value `-129` cannot fit into `i8` which has range `-128..=127` + } + "#; + check_errors(src); } diff --git a/compiler/noirc_frontend/src/tests/enums.rs b/compiler/noirc_frontend/src/tests/enums.rs index 5a2480d67ae..78f0442bc9f 100644 --- a/compiler/noirc_frontend/src/tests/enums.rs +++ b/compiler/noirc_frontend/src/tests/enums.rs @@ -1,64 +1,55 @@ use crate::{ - hir::{ - def_collector::{dc_crate::CompilationError, errors::DefCollectorErrorKind}, - resolution::errors::ResolverError, - type_check::TypeCheckError, - }, + hir::def_collector::dc_crate::CompilationError, parser::ParserErrorReason, - tests::{get_program_errors, get_program_using_features}, + tests::{assert_no_errors, get_program_using_features}, }; -use CompilationError::*; -use DefCollectorErrorKind::Duplicate; -use ResolverError::*; -use TypeCheckError::{ArityMisMatch, TypeMismatch}; + +use super::{check_errors, check_errors_using_features}; #[test] fn error_with_duplicate_enum_variant() { + // TODO: the primary error should be on the second `Bar` let src = r#" - enum Foo { - Bar(i32), - Bar(u8), - } - - fn main() {} + pub enum Foo { + Bar(i32), + ^^^ Duplicate definitions of enum variant with name Bar found + ~~~ First enum variant found here + Bar(u8), + ~~~ Second enum variant found here + } + + fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - assert!(matches!(&errors[0], DefinitionError(Duplicate { .. }))); - assert!(matches!(&errors[1], ResolverError(UnusedItem { .. }))); + check_errors(src); } #[test] fn errors_on_unspecified_unstable_enum() { // Enums are experimental - this will need to be updated when they are stabilized let src = r#" - enum Foo { Bar } + enum Foo { Bar } + ^^^ This requires the unstable feature 'enums' which is not enabled + ~~~ Pass -Zenums to nargo to enable this feature at your own risk. - fn main() { - let _x = Foo::Bar; - } + fn main() { + let _x = Foo::Bar; + } "#; - let no_features = &[]; - let errors = get_program_using_features(src, no_features).2; - assert_eq!(errors.len(), 1); - - let CompilationError::ParseError(error) = &errors[0] else { - panic!("Expected a ParseError experimental feature error"); - }; - - assert!(matches!(error.reason(), Some(ParserErrorReason::ExperimentalFeature(_)))); + check_errors_using_features(src, no_features); } #[test] fn errors_on_unspecified_unstable_match() { + // TODO: update this test. Right now it's hard to test because the span happens in the entire + // `match` node but ideally it would be nice if it only happened in the `match` keyword. // Enums are experimental - this will need to be updated when they are stabilized let src = r#" - fn main() { - match 3 { - _ => (), - } + fn main() { + match 3 { + _ => (), } + } "#; let no_features = &[]; @@ -75,83 +66,73 @@ fn errors_on_unspecified_unstable_match() { #[test] fn errors_on_repeated_match_variables_in_pattern() { let src = r#" - fn main() { - match (1, 2) { - (_x, _x) => (), - } - } + fn main() { + match (1, 2) { + (_x, _x) => (), + ^^ Variable `_x` was already defined in the same match pattern + ~~ `_x` redefined here + ~~ `_x` was previously defined here + } + } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!(&errors[0], ResolverError(VariableAlreadyDefinedInPattern { .. }))); + check_errors(src); } #[test] fn duplicate_field_in_match_struct_pattern() { let src = r#" - fn main() { - let foo = Foo { x: 10, y: 20 }; - match foo { - Foo { x: _, x: _, y: _ } => {} - } - } - - struct Foo { - x: i32, - y: Field, - } + fn main() { + let foo = Foo { x: 10, y: 20 }; + match foo { + Foo { x: _, x: _, y: _ } => {} + ^ duplicate field x + } + } + + struct Foo { + x: i32, + y: Field, + } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!(&errors[0], ResolverError(DuplicateField { .. }))); + check_errors(src); } #[test] fn missing_field_in_match_struct_pattern() { let src = r#" - fn main() { - let foo = Foo { x: 10, y: 20 }; - match foo { - Foo { x: _ } => {} - } - } - - struct Foo { - x: i32, - y: Field, - } + fn main() { + let foo = Foo { x: 10, y: 20 }; + match foo { + Foo { x: _ } => {} + ^^^ missing field y in struct Foo + } + } + + struct Foo { + x: i32, + y: Field, + } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!(&errors[0], ResolverError(MissingFields { .. }))); + check_errors(src); } #[test] fn no_such_field_in_match_struct_pattern() { let src = r#" - fn main() { - let foo = Foo { x: 10, y: 20 }; - match foo { - Foo { x: _, y: _, z: _ } => {} - } - } - - struct Foo { - x: i32, - y: Field, - } + fn main() { + let foo = Foo { x: 10, y: 20 }; + match foo { + Foo { x: _, y: _, z: _ } => {} + ^ no such field z defined in struct Foo + } + } + + struct Foo { + x: i32, + y: Field, + } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!(&errors[0], ResolverError(NoSuchField { .. }))); + check_errors(src); } #[test] @@ -160,6 +141,7 @@ fn match_integer_type_mismatch_in_pattern() { fn main() { match 2 { Foo::One(_) => (), + ^^^^^^^^ Expected type Field, found type Foo } } @@ -167,10 +149,7 @@ fn match_integer_type_mismatch_in_pattern() { One(i32), } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!(&errors[0], TypeError(TypeMismatch { .. }))); + check_errors(src); } #[test] @@ -184,8 +163,7 @@ fn match_shadow_global() { fn foo() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -194,15 +172,13 @@ fn match_no_shadow_global() { fn main() { match 2 { crate::foo => (), + ^^^^^^^^^^ Expected a struct, enum, or literal pattern, but found a function } } fn foo() {} "#; - let errors = dbg!(get_program_errors(src)); - assert_eq!(errors.len(), 1); - - assert!(matches!(&errors[0], ResolverError(UnexpectedItemInPattern { .. }))); + check_errors(src); } #[test] @@ -210,8 +186,10 @@ fn constructor_arg_arity_mismatch_in_pattern() { let src = r#" fn main() { match Foo::One(1) { - Foo::One(_, _) => (), // too many - Foo::Two(_) => (), // too few + Foo::One(_, _) => (), + ^^^^^^^^ Expected 1 argument, but found 2 + Foo::Two(_) => (), + ^^^^^^^^ Expected 2 arguments, but found 1 } } @@ -220,9 +198,5 @@ fn constructor_arg_arity_mismatch_in_pattern() { Two(i32, i32), } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - assert!(matches!(&errors[0], TypeError(ArityMisMatch { .. }))); - assert!(matches!(&errors[1], TypeError(ArityMisMatch { .. }))); + check_errors(src); } diff --git a/compiler/noirc_frontend/src/tests/imports.rs b/compiler/noirc_frontend/src/tests/imports.rs index dafc5b098de..ba2dad285d6 100644 --- a/compiler/noirc_frontend/src/tests/imports.rs +++ b/compiler/noirc_frontend/src/tests/imports.rs @@ -1,9 +1,6 @@ -use crate::hir::{ - def_collector::{dc_crate::CompilationError, errors::DefCollectorErrorKind}, - resolution::{errors::ResolverError, import::PathResolutionError}, -}; +use crate::tests::check_errors; -use super::{assert_no_errors, get_program_errors}; +use super::assert_no_errors; #[test] fn use_super() { @@ -23,19 +20,11 @@ fn use_super() { #[test] fn no_super() { - let src = "use super::some_func;"; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( - PathResolutionError::NoSuper(location), - )) = &errors[0] - else { - panic!("Expected a 'no super' error, got {:?}", errors[0]); - }; - - assert_eq!(location.span.start(), 4); - assert_eq!(location.span.end(), 9); + let src = " + use super::some_func; + ^^^^^ There is no super module + "; + check_errors(src); } #[test] @@ -69,18 +58,11 @@ fn warns_on_use_of_private_exported_item() { fn main() { foo::baz(); + ^^^ baz is private and not visible from the current module + ~~~ baz is private } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - &errors[0], - CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(..), - )) - )); + check_errors(src); } #[test] @@ -110,22 +92,15 @@ fn warns_on_re_export_of_item_with_less_visibility() { } pub use bar::baz; + ^^^ cannot re-export baz because it has less visibility than this use statement + ~~~ consider marking baz as pub } fn main() { foo::baz(); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - &errors[0], - CompilationError::DefinitionError( - DefCollectorErrorKind::CannotReexportItemWithLessVisibility { .. } - ) - )); + check_errors(src); } #[test] @@ -136,21 +111,10 @@ fn errors_if_using_alias_in_import() { } use foo::bar::baz; + ^^^ bar is a type alias, not a module fn main() { } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( - PathResolutionError::NotAModule { ident, kind }, - )) = &errors[0] - else { - panic!("Expected a 'not a module' error, got {:?}", errors[0]); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(*kind, "type alias"); + check_errors(src); } diff --git a/compiler/noirc_frontend/src/tests/metaprogramming.rs b/compiler/noirc_frontend/src/tests/metaprogramming.rs index 1ea23b4301c..a19ef17d835 100644 --- a/compiler/noirc_frontend/src/tests/metaprogramming.rs +++ b/compiler/noirc_frontend/src/tests/metaprogramming.rs @@ -3,15 +3,13 @@ use noirc_errors::Located; use crate::{ ast::Ident, hir::{ - comptime::{ComptimeError, InterpreterError}, + comptime::ComptimeError, def_collector::{ dc_crate::CompilationError, errors::{DefCollectorErrorKind, DuplicateType}, }, - resolution::errors::ResolverError, - type_check::TypeCheckError, }, - parser::ParserErrorReason, + tests::check_errors, }; use super::{assert_no_errors, get_program_errors}; @@ -23,39 +21,30 @@ fn comptime_let() { comptime let my_var = 2; assert_eq(my_var, 2); }"#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] fn comptime_code_rejects_dynamic_variable() { - let src = r#"fn main(x: Field) { + let src = r#" + fn main(x: Field) { comptime let my_var = (x - x) + 2; + ^ Non-comptime variable `x` referenced in comptime code + ~ Non-comptime variables can't be used in comptime code assert_eq(my_var, 2); - }"#; - let errors = get_program_errors(src); - - assert_eq!(errors.len(), 1); - match &errors[0] { - CompilationError::InterpreterError(InterpreterError::NonComptimeVarReferenced { - name, - .. - }) => { - assert_eq!(name, "x"); - } - _ => panic!("expected an InterpreterError"), } + "#; + check_errors(src); } #[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], - CompilationError::ResolverError(ResolverError::ComptimeTypeInRuntimeCode { .. }) - )); + let source = " + pub fn foo(_f: FunctionDefinition) {} + ^^^^^^^^^^^^^^^^^^ Comptime-only type `FunctionDefinition` cannot be used in runtime code + ~~~~~~~~~~~~~~~~~~ Comptime-only type used here + "; + check_errors(source); } #[test] @@ -64,6 +53,7 @@ fn macro_result_type_mismatch() { fn main() { comptime { let x = unquote!(quote { "test" }); + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected type Field, found type str<4> let _: Field = x; } } @@ -72,10 +62,7 @@ fn macro_result_type_mismatch() { q } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!(errors[0], CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }))); + check_errors(src); } #[test] @@ -127,6 +114,8 @@ fn allows_references_to_structs_generated_by_macros() { #[test] fn errors_if_macros_inject_functions_with_name_collisions() { + // This can't be tested using `check_errors` right now because the two secondary + // errors land on the same span. let src = r#" comptime fn make_colliding_functions(_s: StructDefinition) -> Quoted { quote { @@ -198,6 +187,8 @@ fn does_not_fail_to_parse_macro_on_parser_warning() { quote { pub fn bar() { unsafe { + ^^^^^^ Unsafe block must have a safety comment above it + ~~~~~~ The comment must start with the "Safety: " word foo(); } } @@ -208,12 +199,5 @@ fn does_not_fail_to_parse_macro_on_parser_warning() { bar() } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ParseError(parser_error) = &errors[0] else { - panic!("Expected a ParseError, got {:?}", errors[0]); - }; - - assert!(matches!(parser_error.reason(), Some(ParserErrorReason::MissingSafetyComment))); + check_errors(src); } diff --git a/compiler/noirc_frontend/src/tests/references.rs b/compiler/noirc_frontend/src/tests/references.rs index 236b22b9e90..a11cd0eea53 100644 --- a/compiler/noirc_frontend/src/tests/references.rs +++ b/compiler/noirc_frontend/src/tests/references.rs @@ -1,9 +1,4 @@ -use crate::hir::{ - def_collector::dc_crate::CompilationError, resolution::errors::ResolverError, - type_check::TypeCheckError, -}; - -use super::get_program_errors; +use crate::tests::check_errors; #[test] fn cannot_mutate_immutable_variable() { @@ -11,21 +6,12 @@ fn cannot_mutate_immutable_variable() { fn main() { let array = [1]; mutate(&mut array); + ^^^^^ Cannot mutate immutable variable `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] - else { - panic!("Expected a CannotMutateImmutableVariable error"); - }; - - assert_eq!(name, "array"); + check_errors(src); } #[test] @@ -38,23 +24,14 @@ fn cannot_mutate_immutable_variable_on_member_access() { fn main() { let foo = Foo { x: 0 }; mutate(&mut foo.x); + ^^^^^ Cannot mutate immutable variable `foo` } 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] - else { - panic!("Expected a CannotMutateImmutableVariable error"); - }; - - assert_eq!(name, "foo"); + check_errors(src); } #[test] @@ -62,23 +39,15 @@ fn does_not_crash_when_passing_mutable_undefined_variable() { let src = r#" fn main() { mutate(&mut undefined); + ^^^^^^^^^ cannot find `undefined` in this scope + ~~~~~~~~~ not found in this scope } 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] - else { - panic!("Expected a VariableNotDeclared error"); - }; - - assert_eq!(name, "undefined"); + check_errors(src); } #[test] @@ -90,6 +59,7 @@ fn constrained_reference_to_unconstrained() { // Safety: test context unsafe { mut_ref_input(x_ref, y); + ^^^^^ Cannot pass a mutable reference from a constrained runtime to an unconstrained runtime } } @@ -100,13 +70,5 @@ fn constrained_reference_to_unconstrained() { *x = y; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::ConstrainedReferenceToUnconstrained { .. }) = - &errors[0] - else { - panic!("Expected an error about passing a constrained reference to unconstrained"); - }; + check_errors(src); } diff --git a/compiler/noirc_frontend/src/tests/traits.rs b/compiler/noirc_frontend/src/tests/traits.rs index e5b19e695d4..5ba63bc6a29 100644 --- a/compiler/noirc_frontend/src/tests/traits.rs +++ b/compiler/noirc_frontend/src/tests/traits.rs @@ -1,11 +1,6 @@ use crate::elaborator::FrontendOptions; -use crate::hir::def_collector::dc_crate::CompilationError; -use crate::hir::def_collector::errors::DefCollectorErrorKind; -use crate::hir::resolution::errors::ResolverError; -use crate::hir::resolution::import::PathResolutionError; -use crate::hir::type_check::TypeCheckError; -use crate::tests::{get_program_errors, get_program_with_maybe_parser_errors}; +use crate::tests::{check_errors, get_program_with_options}; use super::assert_no_errors; @@ -107,63 +102,49 @@ fn trait_inheritance_with_generics_4() { #[test] fn trait_inheritance_dependency_cycle() { + // TODO: maybe the error location should be just on Foo let src = r#" trait Foo: Bar {} + ^^^^^^^^^^^^^^^^^ Dependency cycle found + ~~~~~~~~~~~~~~~~~ 'Foo' recursively depends on itself: Foo -> Bar -> Foo trait Bar: Foo {} fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - assert!(matches!( - errors[0], - CompilationError::ResolverError(ResolverError::DependencyCycle { .. }) - )); + check_errors(src); } #[test] fn trait_inheritance_missing_parent_implementation() { + // TODO: the secondary errors are missing a closing backtick let src = r#" pub trait Foo {} pub trait Bar: Foo {} + ~~~ required by this bound in `Bar pub struct Struct {} impl Bar for Struct {} + ^^^^^^ The trait bound `Struct: Foo` is not satisfied + ~~~~~~ The trait `Foo` is not implemented for `Struct fn main() { let _ = Struct {}; // silence Struct never constructed warning } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::TraitNotImplemented { - impl_trait, - missing_trait: the_trait, - type_missing_trait: typ, - .. - }) = &errors[0] - else { - panic!("Expected a TraitNotImplemented error, got {:?}", &errors[0]); - }; - - assert_eq!(the_trait, "Foo"); - assert_eq!(typ, "Struct"); - assert_eq!(impl_trait, "Bar"); + check_errors(src); } #[test] fn errors_on_unknown_type_in_trait_where_clause() { let src = r#" pub trait Foo where T: Unknown {} + ^^^^^^^ Could not resolve 'Unknown' in path fn main() { } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); + check_errors(src); } #[test] @@ -222,6 +203,7 @@ fn does_not_error_if_impl_trait_constraint_is_satisfied_for_type_variable() { "#; assert_no_errors(src); } + #[test] fn errors_if_impl_trait_constraint_is_not_satisfied() { let src = r#" @@ -232,6 +214,7 @@ fn errors_if_impl_trait_constraint_is_not_satisfied() { pub trait Foo where T: Greeter, + ~~~~~~~ required by this bound in `Foo { fn greet(object: U) where @@ -246,25 +229,12 @@ fn errors_if_impl_trait_constraint_is_not_satisfied() { pub struct Bar; impl Foo for Bar {} + ^^^ The trait bound `SomeGreeter: Greeter` is not satisfied + ~~~ The trait `Greeter` is not implemented for `SomeGreeter fn main() {} "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::TraitNotImplemented { - impl_trait, - missing_trait: the_trait, - type_missing_trait: typ, - .. - }) = &errors[0] - else { - panic!("Expected a TraitNotImplemented error, got {:?}", &errors[0]); - }; - - assert_eq!(the_trait, "Greeter"); - assert_eq!(typ, "SomeGreeter"); - assert_eq!(impl_trait, "Foo"); + check_errors(src); } #[test] @@ -443,6 +413,7 @@ fn trait_alias_polymorphic_where_clause() { fn qux(x: T) -> bool where T: Qux { x.foo().bar().baz() + ^^^^^^^^^^^^^^^^^^^ No method named 'baz' found for type 'U' } impl Foo for Field { @@ -465,35 +436,14 @@ fn trait_alias_polymorphic_where_clause() { fn main() { assert(0.foo().bar().baz() == qux(0)); + ^^^ No matching impl found for `T: Baz` + ~~~ No impl for `T: Baz` } "#; // TODO(https://github.com/noir-lang/noir/issues/6467) // assert_no_errors(src); - let errors = get_program_errors(src); - assert_eq!(errors.len(), 2); - - match &errors[0] { - CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { - method_name, .. - }) => { - assert_eq!(method_name, "baz"); - } - other => { - panic!("expected UnresolvedMethodCall, but found {:?}", other); - } - } - - match &errors[1] { - CompilationError::TypeError(TypeCheckError::NoMatchingImplFound(err)) => { - assert_eq!(err.constraints.len(), 2); - assert_eq!(err.constraints[0].1, "Baz"); - assert_eq!(err.constraints[1].1, "Qux<_>"); - } - other => { - panic!("expected NoMatchingImplFound, but found {:?}", other); - } - } + check_errors(src); } // TODO(https://github.com/noir-lang/noir/issues/6467): currently failing, so @@ -519,10 +469,12 @@ fn trait_alias_with_where_clause_has_equivalent_errors() { pub fn qux(x: T, _: U) -> bool where U: Qux { x.baz() + ^^^^^^^ No method named 'baz' found for type 'T' } fn main() {} "#; + check_errors(src); let alias_src = r#" trait Bar { @@ -537,37 +489,12 @@ fn trait_alias_with_where_clause_has_equivalent_errors() { pub fn qux(x: T, _: U) -> bool where U: Qux { x.baz() + ^^^^^^^ No method named 'baz' found for type 'T' } fn main() {} "#; - - let errors = get_program_errors(src); - let alias_errors = get_program_errors(alias_src); - - assert_eq!(errors.len(), 1); - assert_eq!(alias_errors.len(), 1); - - match (&errors[0], &alias_errors[0]) { - ( - CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { - method_name, - object_type, - .. - }), - CompilationError::TypeError(TypeCheckError::UnresolvedMethodCall { - method_name: alias_method_name, - object_type: alias_object_type, - .. - }), - ) => { - assert_eq!(method_name, alias_method_name); - assert_eq!(object_type, alias_object_type); - } - other => { - panic!("expected UnresolvedMethodCall, but found {:?}", other); - } - } + check_errors(alias_src); } #[test] @@ -654,7 +581,7 @@ fn does_not_crash_on_as_trait_path_with_empty_path() { let allow_parser_errors = true; let options = FrontendOptions::test_default(); - let (_, _, errors) = get_program_with_maybe_parser_errors(src, allow_parser_errors, options); + let (_, _, errors) = get_program_with_options(src, allow_parser_errors, options); assert!(!errors.is_empty()); } @@ -663,6 +590,7 @@ fn warns_if_trait_is_not_in_scope_for_function_call_and_there_is_only_one_trait_ let src = r#" fn main() { let _ = Bar::foo(); + ^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } pub struct Bar { @@ -680,17 +608,7 @@ fn warns_if_trait_is_not_in_scope_for_function_call_and_there_is_only_one_trait_ } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0] - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -792,6 +710,8 @@ fn errors_if_trait_is_not_in_scope_for_function_call_and_there_are_multiple_cand let src = r#" fn main() { let _ = Bar::foo(); + ^^^ Could not resolve 'foo' in path + ~~~ The following traits which provide `foo` are implemented but not in scope: `private_mod::Foo2`, `private_mod::Foo` } pub struct Bar { @@ -819,18 +739,7 @@ fn errors_if_trait_is_not_in_scope_for_function_call_and_there_are_multiple_cand } } "#; - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, mut traits }, - )) = errors.remove(0) - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - traits.sort(); - assert_eq!(traits, vec!["private_mod::Foo", "private_mod::Foo2"]); + check_errors(src); } #[test] @@ -841,6 +750,8 @@ fn errors_if_multiple_trait_methods_are_in_scope_for_function_call() { fn main() { let _ = Bar::foo(); + ^^^ Multiple applicable items in scope + ~~~ All these trait which provide `foo` are implemented and in scope: `private_mod::Foo2`, `private_mod::Foo` } pub struct Bar { @@ -868,18 +779,7 @@ fn errors_if_multiple_trait_methods_are_in_scope_for_function_call() { } } "#; - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::MultipleTraitsInScope { ident, mut traits }, - )) = errors.remove(0) - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - traits.sort(); - assert_eq!(traits, vec!["private_mod::Foo", "private_mod::Foo2"]); + check_errors(src); } #[test] @@ -888,6 +788,7 @@ fn warns_if_trait_is_not_in_scope_for_method_call_and_there_is_only_one_trait_me fn main() { let bar = Bar { x: 42 }; let _ = bar.foo(); + ^^^^^^^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } pub struct Bar { @@ -906,17 +807,7 @@ fn warns_if_trait_is_not_in_scope_for_method_call_and_there_is_only_one_trait_me } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0] - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -954,6 +845,8 @@ fn errors_if_trait_is_not_in_scope_for_method_call_and_there_are_multiple_candid fn main() { let bar = Bar { x: 42 }; let _ = bar.foo(); + ^^^^^^^^^ Could not resolve 'foo' in path + ~~~~~~~~~ The following traits which provide `foo` are implemented but not in scope: `private_mod::Foo2`, `private_mod::Foo` } pub struct Bar { @@ -982,18 +875,7 @@ fn errors_if_trait_is_not_in_scope_for_method_call_and_there_are_multiple_candid } } "#; - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::UnresolvedWithPossibleTraitsToImport { ident, mut traits }, - )) = errors.remove(0) - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - traits.sort(); - assert_eq!(traits, vec!["private_mod::Foo", "private_mod::Foo2"]); + check_errors(src); } #[test] @@ -1005,6 +887,8 @@ fn errors_if_multiple_trait_methods_are_in_scope_for_method_call() { fn main() { let bar = Bar { x : 42 }; let _ = bar.foo(); + ^^^^^^^^^ Multiple applicable items in scope + ~~~~~~~~~ All these trait which provide `foo` are implemented and in scope: `private_mod::Foo2`, `private_mod::Foo` } pub struct Bar { @@ -1033,18 +917,7 @@ fn errors_if_multiple_trait_methods_are_in_scope_for_method_call() { } } "#; - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::MultipleTraitsInScope { ident, mut traits }, - )) = errors.remove(0) - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - traits.sort(); - assert_eq!(traits, vec!["private_mod::Foo", "private_mod::Foo2"]); + check_errors(src); } #[test] @@ -1084,28 +957,17 @@ fn type_checks_trait_default_method_and_errors() { let src = r#" pub trait Foo { fn foo(self) -> i32 { + ^^^ expected type i32, found type bool + ~~~ expected i32 because of return type let _ = self; true + ~~~~ bool returned here } } fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { - expected, - actual, - .. - }) = &errors[0] - else { - panic!("Expected a type mismatch error, got {:?}", errors[0]); - }; - - assert_eq!(expected.to_string(), "i32"); - assert_eq!(actual.to_string(), "bool"); + check_errors(src); } #[test] @@ -1147,6 +1009,7 @@ fn warns_if_trait_is_not_in_scope_for_primitive_function_call_and_there_is_only_ let src = r#" fn main() { let _ = Field::foo(); + ^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } mod private_mod { @@ -1161,17 +1024,7 @@ fn warns_if_trait_is_not_in_scope_for_primitive_function_call_and_there_is_only_ } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0] - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -1180,6 +1033,7 @@ fn warns_if_trait_is_not_in_scope_for_primitive_method_call_and_there_is_only_on fn main() { let x: Field = 1; let _ = x.foo(); + ^^^^^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } mod private_mod { @@ -1194,17 +1048,7 @@ fn warns_if_trait_is_not_in_scope_for_primitive_method_call_and_there_is_only_on } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0] - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -1213,6 +1057,7 @@ fn warns_if_trait_is_not_in_scope_for_generic_function_call_and_there_is_only_on fn main() { let x: i32 = 1; let _ = x.foo(); + ^^^^^^^ trait `private_mod::Foo` which provides `foo` is implemented but not in scope, please import it } mod private_mod { @@ -1227,17 +1072,7 @@ fn warns_if_trait_is_not_in_scope_for_generic_function_call_and_there_is_only_on } } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TraitMethodNotInScope { ident, trait_name }, - )) = &errors[0] - else { - panic!("Expected a 'trait method not in scope' error"); - }; - assert_eq!(ident.to_string(), "foo"); - assert_eq!(trait_name, "private_mod::Foo"); + check_errors(src); } #[test] @@ -1248,23 +1083,19 @@ fn error_on_duplicate_impl_with_associated_type() { } impl Foo for i32 { + ~~~ Previous impl defined here type Bar = u32; } impl Foo for i32 { + ^^^ Impl for type `i32` overlaps with existing impl + ~~~ Overlapping impl type Bar = u8; } fn main() {} "#; - - // Expect "Impl for type `i32` overlaps with existing impl" - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use CompilationError::DefinitionError; - use DefCollectorErrorKind::*; - assert!(matches!(&errors[0], DefinitionError(OverlappingImpl { .. }))); + check_errors(src); } #[test] @@ -1275,23 +1106,19 @@ fn error_on_duplicate_impl_with_associated_constant() { } impl Foo for i32 { + ~~~ Previous impl defined here let Bar = 5; } impl Foo for i32 { + ^^^ Impl for type `i32` overlaps with existing impl + ~~~ Overlapping impl let Bar = 6; } fn main() {} "#; - - // Expect "Impl for type `i32` overlaps with existing impl" - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - use CompilationError::DefinitionError; - use DefCollectorErrorKind::*; - assert!(matches!(&errors[0], DefinitionError(OverlappingImpl { .. }))); + check_errors(src); } // See https://github.com/noir-lang/noir/issues/6530 @@ -1335,8 +1162,7 @@ fn regression_6530() { let _: Field = foo.into(); } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] @@ -1383,9 +1209,8 @@ fn calls_trait_method_using_struct_name_when_multiple_impls_exist_and_errors_tur } fn main() { let _ = U60Repr::::from2([1, 2, 3]); + ^^^^^^^^^ Expected type Field, found type [Field; 3] } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!(errors[0], CompilationError::TypeError(TypeCheckError::TypeMismatch { .. }))); + check_errors(src); } diff --git a/compiler/noirc_frontend/src/tests/turbofish.rs b/compiler/noirc_frontend/src/tests/turbofish.rs index bda590533cf..d1bf9002ab7 100644 --- a/compiler/noirc_frontend/src/tests/turbofish.rs +++ b/compiler/noirc_frontend/src/tests/turbofish.rs @@ -1,10 +1,6 @@ -use crate::hir::{ - def_collector::dc_crate::CompilationError, - resolution::{errors::ResolverError, import::PathResolutionError}, - type_check::TypeCheckError, -}; +use crate::tests::check_errors; -use super::{assert_no_errors, get_program_errors}; +use super::assert_no_errors; #[test] fn turbofish_numeric_generic_nested_call() { @@ -66,15 +62,10 @@ fn turbofish_in_constructor_generics_mismatch() { fn main() { let _ = Foo:: { x: 1 }; + ^^^^^^^^^^^^ struct Foo expects 1 generic but 2 were given } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::TypeError(TypeCheckError::GenericCountMismatch { .. }), - )); + check_errors(src); } #[test] @@ -87,21 +78,10 @@ fn turbofish_in_constructor() { fn main() { let x: Field = 0; let _ = Foo:: { x: x }; + ^ Expected type i32, found type Field } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, expr_typ, .. - }) = &errors[0] - else { - panic!("Expected a type mismatch error, got {:?}", errors[0]); - }; - - assert_eq!(expected_typ, "i32"); - assert_eq!(expr_typ, "Field"); + check_errors(src); } #[test] @@ -122,6 +102,7 @@ fn turbofish_in_struct_pattern() { #[test] fn turbofish_in_struct_pattern_errors_if_type_mismatch() { + // TODO: maybe the error should be on the expression let src = r#" struct Foo { x: T @@ -130,17 +111,11 @@ fn turbofish_in_struct_pattern_errors_if_type_mismatch() { fn main() { let value: Field = 0; let Foo:: { x } = Foo { x: value }; + ^^^^^^^^^^^^^^^^ Cannot assign an expression of type Foo to a value of type Foo let _ = x; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatchWithSource { .. }) = &errors[0] - else { - panic!("Expected a type mismatch error, got {:?}", errors[0]); - }; + check_errors(src); } #[test] @@ -153,26 +128,11 @@ fn turbofish_in_struct_pattern_generic_count_mismatch() { fn main() { let value = 0; let Foo:: { x } = Foo { x: value }; + ^^^^^^^^^^^^ struct Foo expects 1 generic but 2 were given let _ = x; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { - item, - expected, - found, - .. - }) = &errors[0] - else { - panic!("Expected a generic count mismatch error, got {:?}", errors[0]); - }; - - assert_eq!(item, "struct Foo"); - assert_eq!(*expected, 1); - assert_eq!(*found, 2); + check_errors(src); } #[test] @@ -202,19 +162,10 @@ fn errors_if_turbofish_after_module() { fn main() { moo::::foo(); + ^^^^^^^ turbofish (`::<_>`) not allowed on module `moo` } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::TurbofishNotAllowedOnItem { item, .. }, - )) = &errors[0] - else { - panic!("Expected a turbofish not allowed on item error, got {:?}", errors[0]); - }; - assert_eq!(item, "module `moo`"); + check_errors(src); } #[test] @@ -252,22 +203,10 @@ fn turbofish_in_type_before_call_errors() { fn main() { let _ = Foo::::new(true); + ^^^^ Expected type i32, found type bool } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_location: _, - }) = &errors[0] - else { - panic!("Expected a type mismatch error, got {:?}", errors[0]); - }; - - assert_eq!(expected_typ, "i32"); - assert_eq!(expr_typ, "bool"); + check_errors(src); } #[test] @@ -312,22 +251,10 @@ fn use_generic_type_alias_with_turbofish_in_method_call_errors() { fn main() { let _ = Bar::::new(true); + ^^^^ Expected type i32, found type bool } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_location: _, - }) = &errors[0] - else { - panic!("Expected a type mismatch error, got {:?}", errors[0]); - }; - - assert_eq!(expected_typ, "i32"); - assert_eq!(expr_typ, "bool"); + check_errors(src); } #[test] @@ -371,22 +298,10 @@ fn use_generic_type_alias_with_partial_generics_with_turbofish_in_method_call_er fn main() { let _ = Bar::::new(1, 1); + ^ Expected type bool, found type Field } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_location: _, - }) = &errors[0] - else { - panic!("Expected a type mismatch error, got {:?}", errors[0]); - }; - - assert_eq!(expected_typ, "bool"); - assert_eq!(expr_typ, "Field"); + check_errors(src); } #[test] @@ -407,22 +322,10 @@ fn use_generic_type_alias_with_partial_generics_with_turbofish_in_method_call_er fn main() { let _ = Bar::::new(true, true); + ^^^^ Expected type i32, found type bool } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_location: _, - }) = &errors[0] - else { - panic!("Expected a type mismatch error, got {:?}", errors[0]); - }; - - assert_eq!(expected_typ, "i32"); - assert_eq!(expr_typ, "bool"); + check_errors(src); } #[test] @@ -440,20 +343,8 @@ fn trait_function_with_turbofish_on_trait_gives_error() { fn main() { let _: i32 = Foo::::foo(1); + ^ Expected type bool, found type Field } "#; - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::TypeError(TypeCheckError::TypeMismatch { - expected_typ, - expr_typ, - expr_location: _, - }) = &errors[0] - else { - panic!("Expected a type mismatch error, got {:?}", errors[0]); - }; - - assert_eq!(expected_typ, "bool"); - assert_eq!(expr_typ, "Field"); + check_errors(src); } diff --git a/compiler/noirc_frontend/src/tests/unused_items.rs b/compiler/noirc_frontend/src/tests/unused_items.rs index 0840eb18a38..a3c50114244 100644 --- a/compiler/noirc_frontend/src/tests/unused_items.rs +++ b/compiler/noirc_frontend/src/tests/unused_items.rs @@ -1,9 +1,4 @@ -use crate::{ - hir::{def_collector::dc_crate::CompilationError, resolution::errors::ResolverError}, - tests::assert_no_errors, -}; - -use super::get_program_errors; +use crate::tests::{assert_no_errors, check_errors}; #[test] fn errors_on_unused_private_import() { @@ -17,6 +12,8 @@ fn errors_on_unused_private_import() { } use foo::bar; + ^^^ unused import bar + ~~~ unused import use foo::baz; use foo::Foo; @@ -27,17 +24,7 @@ fn errors_on_unused_private_import() { baz(); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0] - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(item.item_type(), "import"); + check_errors(src); } #[test] @@ -52,6 +39,8 @@ fn errors_on_unused_pub_crate_import() { } pub(crate) use foo::bar; + ^^^ unused import bar + ~~~ unused import use foo::baz; use foo::Foo; @@ -62,17 +51,7 @@ fn errors_on_unused_pub_crate_import() { baz(); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0] - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "bar"); - assert_eq!(item.item_type(), "import"); + check_errors(src); } #[test] @@ -88,51 +67,37 @@ fn errors_on_unused_function() { fn foo() { + ^^^ unused function foo + ~~~ unused function bar(); } fn bar() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0] - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "foo"); - assert_eq!(item.item_type(), "function"); + check_errors(src); } #[test] fn errors_on_unused_struct() { let src = r#" struct Foo {} + ^^^ struct `Foo` is never constructed + ~~~ struct is never constructed struct Bar {} fn main() { let _ = Bar {}; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0] - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "Foo"); - assert_eq!(item.item_type(), "struct"); + check_errors(src); } #[test] fn errors_on_unused_trait() { let src = r#" trait Foo {} + ^^^ unused trait Foo + ~~~ unused trait trait Bar {} pub struct Baz { @@ -143,17 +108,7 @@ fn errors_on_unused_trait() { fn main() { } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0] - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "Foo"); - assert_eq!(item.item_type(), "trait"); + check_errors(src); } #[test] @@ -171,44 +126,28 @@ fn silences_unused_variable_warning() { fn errors_on_unused_type_alias() { let src = r#" type Foo = Field; + ^^^ unused type alias Foo + ~~~ unused type alias type Bar = Field; pub fn bar(_: Bar) {} fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0] - else { - panic!("Expected an unused item error"); - }; - - assert_eq!(ident.to_string(), "Foo"); - assert_eq!(item.item_type(), "type alias"); + check_errors(src); } #[test] fn warns_on_unused_global() { let src = r#" global foo: u32 = 1; + ^^^ unused global foo + ~~~ unused global global bar: Field = 1; fn main() { let _ = bar; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::UnusedItem { ident, item }) = &errors[0] - else { - panic!("Expected an unused item warning"); - }; - - assert_eq!(ident.to_string(), "foo"); - assert_eq!(item.item_type(), "global"); + check_errors(src); } #[test] @@ -262,9 +201,7 @@ fn no_warning_on_inner_struct_when_parent_is_used() { assert_eq(foos[0].a, 10); } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 0); + assert_no_errors(src); } #[test] diff --git a/compiler/noirc_frontend/src/tests/visibility.rs b/compiler/noirc_frontend/src/tests/visibility.rs index ce41141b8d8..ee53dcbc84f 100644 --- a/compiler/noirc_frontend/src/tests/visibility.rs +++ b/compiler/noirc_frontend/src/tests/visibility.rs @@ -1,11 +1,4 @@ -use crate::{ - hir::{ - comptime::ComptimeError, - def_collector::{dc_crate::CompilationError, errors::DefCollectorErrorKind}, - resolution::{errors::ResolverError, import::PathResolutionError}, - }, - tests::{assert_no_errors, get_program_errors}, -}; +use crate::tests::{assert_no_errors, check_errors}; #[test] fn errors_once_on_unused_import_that_is_not_accessible() { @@ -15,39 +8,13 @@ fn errors_once_on_unused_import_that_is_not_accessible() { struct Foo {} } use moo::Foo; + ^^^ Foo is private and not visible from the current module + ~~~ Foo is private fn main() { let _ = Foo {}; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - assert!(matches!( - errors[0], - CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError( - PathResolutionError::Private { .. } - )) - )); -} - -fn assert_type_is_more_private_than_item_error(src: &str, private_typ: &str, public_item: &str) { - let errors = get_program_errors(src); - - assert!(!errors.is_empty(), "expected visibility error, got nothing"); - for error in &errors { - let CompilationError::ResolverError(ResolverError::TypeIsMorePrivateThenItem { - typ, - item, - .. - }) = error - else { - panic!("Expected a type vs item visibility error, got {}", error); - }; - - assert_eq!(typ, private_typ); - assert_eq!(item, public_item); - } - assert_eq!(errors.len(), 1, "only expected one error"); + check_errors(src); } #[test] @@ -55,12 +22,13 @@ fn errors_if_type_alias_aliases_more_private_type() { let src = r#" struct Foo {} pub type Bar = Foo; + ^^^ Type `Foo` is more private than item `Bar` pub fn no_unused_warnings() { let _: Bar = Foo {}; } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Foo", "Bar"); + check_errors(src); } #[test] @@ -69,13 +37,14 @@ fn errors_if_type_alias_aliases_more_private_type_in_generic() { pub struct Generic { value: T } struct Foo {} pub type Bar = Generic; + ^^^^^^^^^^^^ Type `Foo` is more private than item `Bar` pub fn no_unused_warnings() { let _ = Foo {}; let _: Bar = Generic { value: Foo {} }; } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Foo", "Bar"); + check_errors(src); } #[test] @@ -85,6 +54,7 @@ fn errors_if_pub_type_alias_leaks_private_type_in_generic() { struct Bar {} pub struct Foo { pub value: T } pub type FooBar = Foo; + ^^^^^^^^ Type `Bar` is more private than item `FooBar` pub fn no_unused_warnings() { let _: FooBar = Foo { value: Bar {} }; @@ -92,7 +62,7 @@ fn errors_if_pub_type_alias_leaks_private_type_in_generic() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "FooBar"); + check_errors(src); } #[test] @@ -102,6 +72,7 @@ fn errors_if_pub_struct_field_leaks_private_type_in_generic() { struct Bar {} pub struct Foo { pub value: T } pub struct FooBar { pub value: Foo } + ^^^^^ Type `Bar` is more private than item `FooBar::value` pub fn no_unused_warnings() { let _ = FooBar { value: Foo { value: Bar {} } }; @@ -109,7 +80,7 @@ fn errors_if_pub_struct_field_leaks_private_type_in_generic() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "FooBar::value"); + check_errors(src); } #[test] @@ -119,12 +90,13 @@ fn errors_if_pub_function_leaks_private_type_in_return() { struct Bar {} pub fn bar() -> Bar { + ^^^ Type `Bar` is more private than item `bar` Bar {} } } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "bar"); + check_errors(src); } #[test] @@ -133,6 +105,7 @@ fn errors_if_pub_function_leaks_private_type_in_arg() { pub mod moo { struct Bar {} pub fn bar(_bar: Bar) {} + ^^^ Type `Bar` is more private than item `bar` pub fn no_unused_warnings() { let _ = Bar {}; @@ -140,7 +113,7 @@ fn errors_if_pub_function_leaks_private_type_in_arg() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "bar"); + check_errors(src); } #[test] @@ -173,6 +146,7 @@ fn errors_if_pub_function_on_pub_struct_returns_private() { impl Foo { pub fn bar() -> Bar { + ^^^ Type `Bar` is more private than item `bar` Bar {} } } @@ -183,7 +157,7 @@ fn errors_if_pub_function_on_pub_struct_returns_private() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "bar"); + check_errors(src); } #[test] @@ -219,6 +193,7 @@ fn errors_if_pub_trait_returns_private_struct() { pub trait Foo { fn foo() -> Bar; + ^^^ Type `Bar` is more private than item `foo` } pub fn no_unused_warnings() { @@ -227,7 +202,7 @@ fn errors_if_pub_trait_returns_private_struct() { } fn main() {} "#; - assert_type_is_more_private_than_item_error(src, "Bar", "foo"); + check_errors(src); } #[test] @@ -263,20 +238,11 @@ fn errors_if_trying_to_access_public_function_inside_private_module() { } fn main() { foo::bar::baz() + ^^^ bar is private and not visible from the current module + ~~~ bar is private } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0] - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "bar"); + check_errors(src); } #[test] @@ -294,22 +260,13 @@ fn warns_if_calling_private_struct_method() { pub fn method(foo: moo::Foo) { foo.bar() + ^^^ bar is private and not visible from the current module + ~~~ bar is private } fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0] - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "bar"); + check_errors(src); } #[test] @@ -386,22 +343,13 @@ fn error_when_accessing_private_struct_field() { fn foo(foo: moo::Foo) -> Field { foo.x + ^ x is private and not visible from the current module + ~ x is private } fn main() {} "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0] - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "x"); + check_errors(src); } #[test] @@ -455,20 +403,11 @@ fn error_when_using_private_struct_field_in_constructor() { fn main() { let _ = moo::Foo { x: 1 }; + ^ x is private and not visible from the current module + ~ x is private } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0] - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "x"); + check_errors(src); } #[test] @@ -482,24 +421,15 @@ fn error_when_using_private_struct_field_in_struct_pattern() { fn foo(foo: moo::Foo) -> Field { let moo::Foo { x } = foo; + ^ x is private and not visible from the current module + ~ x is private x } fn main() { } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0] - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "x"); + check_errors(src); } #[test] @@ -564,21 +494,12 @@ fn errors_if_accessing_private_struct_member_inside_comptime_context() { comptime { let foo = Foo::new(5); let _ = foo.inner; + ^^^^^ inner is private and not visible from the current module + ~~~~~ inner is private }; } "#; - - let errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = &errors[0] - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "inner"); + check_errors(src); } #[test] @@ -593,6 +514,7 @@ fn errors_if_accessing_private_struct_member_inside_function_generated_at_compti use foo::Foo; #[generate_inner_accessor] + ~~~~~~~~~~~~~~~~~~~~~~~~~~ While running this function attribute struct Bar { bar_inner: Foo, } @@ -601,6 +523,8 @@ fn errors_if_accessing_private_struct_member_inside_function_generated_at_compti quote { fn bar_get_foo_inner(x: Bar) -> Field { x.bar_inner.foo_inner + ^^^^^^^^^ foo_inner is private and not visible from the current module + ~~~~~~~~~ foo_inner is private } } } @@ -609,22 +533,5 @@ fn errors_if_accessing_private_struct_member_inside_function_generated_at_compti let _ = bar_get_foo_inner(x); } "#; - - let mut errors = get_program_errors(src); - assert_eq!(errors.len(), 1); - - let CompilationError::ComptimeError(ComptimeError::ErrorRunningAttribute { error, .. }) = - errors.remove(0) - else { - panic!("Expected a ComptimeError, got {:?}", errors[0]); - }; - - let CompilationError::ResolverError(ResolverError::PathResolutionError( - PathResolutionError::Private(ident), - )) = *error - else { - panic!("Expected a private error"); - }; - - assert_eq!(ident.to_string(), "foo_inner"); + check_errors(src); }