Handle importing validator handler into test module.

This commit is contained in:
KtorZ 2024-08-30 22:43:10 +02:00
parent 5dfa3e7cca
commit 7aefa85de1
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
2 changed files with 134 additions and 28 deletions

View File

@ -844,7 +844,7 @@ Perhaps, try the following:
} }
))] ))]
UnknownModuleValue { UnknownModuleValue {
#[label("unknown import")] #[label("not exported by {module_name}?")]
location: Span, location: Span,
name: String, name: String,
module_name: String, module_name: String,
@ -941,16 +941,17 @@ The best thing to do from here is to remove it."#))]
}, },
#[error( #[error(
"I discovered an attempt to import a validator module: '{}'\n", "I discovered an attempt to import a validator module in a library: '{}'\n",
name.if_supports_color(Stdout, |s| s.purple()) name.if_supports_color(Stdout, |s| s.purple())
)] )]
#[diagnostic(code("illegal::import"))] #[diagnostic(code("illegal::import"))]
#[diagnostic(help( #[diagnostic(help(
"If you are trying to share code defined in a validator then move it to a library module under {}", "If you are trying to share code defined in a validator then move it to a library module under {}.\nIf, however, you are trying to import a validator for testing, make sure that your test module doesn't export any definition using the {} keyword.",
"lib/".if_supports_color(Stdout, |s| s.purple())) "lib/".if_supports_color(Stdout, |s| s.purple()),
)] "pub".if_supports_color(Stdout, |s| s.cyan())
))]
ValidatorImported { ValidatorImported {
#[label("validator")] #[label("imported validator")]
location: Span, location: Span,
name: String, name: String,
}, },
@ -1074,7 +1075,8 @@ The best thing to do from here is to remove it."#))]
#[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(code("unknown::handler"))]
#[diagnostic(help( #[diagnostic(help(
"When referring to a validator handler via record access, you must refer to one of the declared handlers:\n{}", "When referring to a validator handler via record access, you must refer to one of the declared handlers{}{}",
if available_handlers.is_empty() { "." } else { ":\n" },
available_handlers available_handlers
.iter() .iter()
.map(|p| format!("-> {}", p.if_supports_color(Stdout, |s| s.green()))) .map(|p| format!("-> {}", p.if_supports_color(Stdout, |s| s.green())))

View File

@ -21,7 +21,7 @@ use crate::{
expr::{FnStyle, TypedExpr, UntypedExpr}, expr::{FnStyle, TypedExpr, UntypedExpr},
format, format,
parser::token::Base, parser::token::Base,
tipo::{fields::FieldMap, DefaultFunction, PatternConstructor, TypeVar}, tipo::{fields::FieldMap, DefaultFunction, ModuleKind, PatternConstructor, TypeVar},
IdGenerator, IdGenerator,
}; };
use std::{ use std::{
@ -920,23 +920,24 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
}) })
} }
fn infer_field_access( fn infer_validator_handler_access(
&mut self, &mut self,
container: UntypedExpr, container: &UntypedExpr,
label: String, label: &str,
access_location: Span, access_location: Span,
) -> Result<TypedExpr, Error> { ) -> Option<Result<TypedExpr, Error>> {
if let UntypedExpr::Var { ref name, location } = container { match container {
UntypedExpr::Var { name, location } => {
if let Some((_, available_handlers)) = self if let Some((_, available_handlers)) = self
.environment .environment
.module_validators .module_validators
.get(name.as_str()) .get(name.as_str())
.cloned() .cloned()
{ {
return self return Some(
.infer_var( self.infer_var(
TypedValidator::handler_name(name.as_str(), label.as_str()), TypedValidator::handler_name(name.as_str(), label),
location, *location,
) )
.map_err(|err| match err { .map_err(|err| match err {
Error::UnknownVariable { .. } => Error::UnknownValidatorHandler { Error::UnknownVariable { .. } => Error::UnknownValidatorHandler {
@ -944,8 +945,111 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
available_handlers, available_handlers,
}, },
_ => err, _ => err,
}),
);
}
}
UntypedExpr::FieldAccess {
label: name,
container,
location,
} => {
if let UntypedExpr::Var {
name: ref module,
location: module_location,
} = container.as_ref()
{
match self.environment.imported_modules.get(module) {
Some((_, info)) if info.kind == ModuleKind::Validator => {
let has_validator = info
.values
.keys()
.any(|k| k.split(".").next() == Some(name));
let value_constructors = info
.values
.keys()
.map(|k| k.split(".").next().unwrap_or(k).to_string())
.collect::<Vec<_>>();
return Some(
self.infer_module_access(
module,
TypedValidator::handler_name(name, label),
location,
access_location,
)
.and_then(|access| {
let export_values = self
.environment
.module_values
.iter()
.any(|(_, constructor)| constructor.public);
let export_functions = self
.environment
.module_functions
.iter()
.any(|(_, function)| function.public);
let export_validators =
!self.environment.module_validators.is_empty();
if export_values || export_functions || export_validators {
return Err(Error::ValidatorImported {
location: location
.map(|_start, end| (module_location.end, end)),
name: name.to_string(),
}); });
} }
Ok(access)
})
.map_err(|err| match err {
Error::UnknownModuleValue { .. } => {
if has_validator {
Error::UnknownValidatorHandler {
location: access_location
.map(|_start, end| (location.end, end)),
available_handlers: Vec::new(),
}
} else {
Error::UnknownModuleValue {
location: location
.map(|_start, end| (module_location.end, end)),
name: name.to_string(),
module_name: module.to_string(),
value_constructors,
}
}
}
_ => err,
}),
);
}
_ => (),
}
}
}
_ => (),
}
None
}
fn infer_field_access(
&mut self,
container: UntypedExpr,
label: String,
access_location: Span,
) -> Result<TypedExpr, Error> {
// NOTE: Before we actually resolve the field access, we try to short-circuit the access if
// we detect a validator handler access. This can happen in two cases:
//
// - Either it is a direct access from a validator in the same module.
// - Or it is an attempt to pull a handler from an imported validator module.
if let Some(shortcircuit) =
self.infer_validator_handler_access(&container, &label, access_location)
{
return shortcircuit;
} }
// Attempt to infer the container as a record access. If that fails, we may be shadowing the name // Attempt to infer the container as a record access. If that fails, we may be shadowing the name