diff --git a/crates/aiken-lang/src/tests/check.rs b/crates/aiken-lang/src/tests/check.rs index 77017615..05753ef3 100644 --- a/crates/aiken-lang/src/tests/check.rs +++ b/crates/aiken-lang/src/tests/check.rs @@ -2724,6 +2724,64 @@ fn use_non_imported_module_as_namespace() { ); } +#[test] +fn invalid_type_field_access_chain() { + let dependency = r#" + pub type Foo { + I(Int) + B(Bool) + } + "#; + + let source_code = r#" + use foo.{Foo} + + test my_test() { + trace Foo.I.Int(42) + Void + } + "#; + + let result = check_with_deps(parse(source_code), vec![(parse_as(dependency, "foo"))]); + + assert!( + matches!( + &result, + Err((warnings, Error::InvalidFieldAccess { .. })) if warnings.is_empty(), + ), + "{result:#?}" + ); +} + +#[test] +fn invalid_type_field_access_chain_2() { + let dependency = r#" + pub type Foo { + I(Int) + B(Bool) + } + "#; + + let source_code = r#" + use foo.{Foo} + + test my_test() { + trace Foo.i(42) + Void + } + "#; + + let result = check_with_deps(parse(source_code), vec![(parse_as(dependency, "foo"))]); + + assert!( + matches!( + &result, + Err((warnings, Error::UnknownTypeConstructor { .. })) if warnings.is_empty(), + ), + "{result:#?}" + ); +} + #[test] fn forbid_importing_or_using_opaque_constructors() { let dependency = r#" diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index dd7aea8d..2a438f4a 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -1079,7 +1079,7 @@ The best thing to do from here is to remove it."#))] available_purposes: Vec, }, - #[error("I could not find an appropriate handler in the validator definition\n")] + #[error("I could not find an appropriate handler in the validator definition.\n")] #[diagnostic(code("unknown::handler"))] #[diagnostic(help( "When referring to a validator handler via record access, you must refer to one of the declared handlers{}{}", @@ -1095,7 +1095,7 @@ The best thing to do from here is to remove it."#))] available_handlers: Vec, }, - #[error("I caught an extraneous fallback handler in an already exhaustive validator\n")] + #[error("I caught an extraneous fallback handler in an already exhaustive validator.\n")] #[diagnostic(code("extraneous::fallback"))] #[diagnostic(help( "Validator handlers must be exhaustive and either cover all purposes, or provide a fallback handler. Here, you have successfully covered all script purposes with your handler, but left an extraneous fallback branch. I cannot let that happen, but removing it for you would probably be deemed rude. So please, remove the fallback." @@ -1104,6 +1104,16 @@ The best thing to do from here is to remove it."#))] #[label("redundant fallback handler")] fallback: Span, }, + + #[error("I was stopped by a suspicious field access chain.\n")] + #[diagnostic(code("invalid::field_access"))] + #[diagnostic(help( + "It seems like you've got things mixed up a little here? You can only access fields exported by modules or, by types within those modules. Double-check the culprit field access chain, there's likely something wrong about it." + ))] + InvalidFieldAccess { + #[label("invalid field access")] + location: Span, + }, } impl ExtraData for Error { @@ -1166,7 +1176,8 @@ impl ExtraData for Error { | Error::UnknownValidatorHandler { .. } | Error::UnexpectedValidatorFallback { .. } | Error::IncorrectBenchmarkArity { .. } - | Error::MustInferFirst { .. } => None, + | Error::MustInferFirst { .. } + | Error::InvalidFieldAccess { .. } => None, Error::UnknownType { name, .. } | Error::UnknownTypeConstructor { name, .. } diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index c6a289ec..080b3195 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -1095,6 +1095,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> { location: module_location, } = type_container.as_ref() { + if TypeConstructor::might_be(module_name) { + return Err(Error::InvalidFieldAccess { + location: access_location, + }); + } + // Lookup the module using the declared name (which may have been rebind with // 'as'), to obtain its _full unambiguous name_. let (_, module) = self