also support using Types as namespace when nested in module.

Signed-off-by: KtorZ <matthias.benkort@gmail.com>
This commit is contained in:
KtorZ 2025-03-16 00:26:44 +01:00
parent 2adc1fab66
commit b8f42dd555
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
8 changed files with 96 additions and 8 deletions

View File

@ -1656,7 +1656,7 @@ impl TypedPattern {
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum Namespace { pub enum Namespace {
Module(String), Module(String),
Type(String), Type(Option<String>, String),
} }
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]

View File

@ -1217,9 +1217,15 @@ impl<'comments> Formatter<'comments> {
} }
let name = match module { let name = match module {
Some(Namespace::Module(m)) | Some(Namespace::Type(m)) => { Some(Namespace::Module(m)) | Some(Namespace::Type(None, m)) => {
m.to_doc().append(".").append(name) m.to_doc().append(".").append(name)
} }
Some(Namespace::Type(Some(m), c)) => m
.to_doc()
.append(".")
.append(c.as_str())
.append(".")
.append(name),
None => name.to_doc(), None => name.to_doc(),
}; };

View File

@ -9,6 +9,27 @@ pub fn parser(
pattern: Recursive<'_, Token, UntypedPattern, ParseError>, pattern: Recursive<'_, Token, UntypedPattern, ParseError>,
) -> impl Parser<Token, UntypedPattern, Error = ParseError> + '_ { ) -> impl Parser<Token, UntypedPattern, Error = ParseError> + '_ {
choice(( choice((
select! { Token::Name { name } => name }
.then(just(Token::Dot).ignore_then(select! {Token::UpName { name } => name}))
.then(
just(Token::Dot).ignore_then(
select! {Token::UpName { name } => name}.then(args(pattern.clone())),
),
)
.map_with_span(
|((module, namespace), (name, (arguments, spread_location, is_record))), span| {
UntypedPattern::Constructor {
is_record,
location: span,
name,
arguments,
module: Some(Namespace::Type(Some(module), namespace)),
constructor: (),
spread_location,
tipo: (),
}
},
),
select! { Token::UpName { name } => name } select! { Token::UpName { name } => name }
.then( .then(
just(Token::Dot).ignore_then( just(Token::Dot).ignore_then(
@ -22,7 +43,7 @@ pub fn parser(
location: span, location: span,
name, name,
arguments, arguments,
module: Some(Namespace::Type(namespace)), module: Some(Namespace::Type(None, namespace)),
constructor: (), constructor: (),
spread_location, spread_location,
tipo: (), tipo: (),

View File

@ -27,9 +27,9 @@ pub use var::parser as var;
pub fn parser() -> impl Parser<Token, UntypedPattern, Error = ParseError> { pub fn parser() -> impl Parser<Token, UntypedPattern, Error = ParseError> {
recursive(|pattern| { recursive(|pattern| {
choice(( choice((
var(pattern.clone()),
pair(pattern.clone()), pair(pattern.clone()),
constructor(pattern.clone()), constructor(pattern.clone()),
var(pattern.clone()),
discard(), discard(),
int(), int(),
bytearray(), bytearray(),

View File

@ -9,6 +9,7 @@ Constructor {
arguments: [], arguments: [],
module: Some( module: Some(
Type( Type(
None,
"Foo", "Foo",
), ),
), ),

View File

@ -2328,6 +2328,31 @@ fn use_type_as_namespace_for_patterns() {
assert!(matches!(result, Ok(..)), "{result:#?}"); assert!(matches!(result, Ok(..)), "{result:#?}");
} }
#[test]
fn use_nested_type_as_namespace_for_patterns() {
let dependency = r#"
pub type Foo {
A
B
}
"#;
let source_code = r#"
use foo.{Foo}
fn bar(x: Foo) {
when x is {
foo.Foo.A -> True
foo.Foo.B -> False
}
}
"#;
let result = check_with_deps(parse(source_code), vec![(parse_as(dependency, "foo"))]);
assert!(matches!(result, Ok(..)), "{result:#?}");
}
#[test] #[test]
fn use_opaque_type_as_namespace_for_patterns_fails() { fn use_opaque_type_as_namespace_for_patterns_fails() {
let dependency = r#" let dependency = r#"

View File

@ -529,7 +529,42 @@ impl<'a> Environment<'a> {
constructors: self.local_constructor_names(), constructors: self.local_constructor_names(),
}), }),
Some(Namespace::Type(t)) => { Some(Namespace::Type(Some(module_name), t)) => {
let module_location = location.map(|start, _| (start, start + module_name.len()));
// Lookup the module using the declared name (which may have been rebind with
// 'as'), to obtain its _full unambiguous name_.
let (_, module) =
self.imported_modules
.get(module_name)
.ok_or_else(|| Error::UnknownModule {
location: module_location,
name: module_name.to_string(),
known_modules: self
.importable_modules
.keys()
.map(|t| t.to_string())
.collect(),
})?;
let type_location = Span::create(module_location.end + 1, t.len());
let parent_type = module.types.get(t).ok_or_else(|| Error::UnknownType {
location: type_location,
name: t.to_string(),
types: self.known_type_names(),
})?;
self.unused_modules.remove(&parent_type.module);
self.get_fully_qualified_value_constructor(
(parent_type.module.as_str(), module_location),
(t, type_location),
(name, location.map(|_, end| (type_location.end + 1, end))),
)
}
Some(Namespace::Type(None, t)) => {
let type_location = location.map(|start, _| (start, start + t.len())); let type_location = location.map(|start, _| (start, start + t.len()));
let parent_type = self.module_types.get(t).ok_or_else(|| Error::UnknownType { let parent_type = self.module_types.get(t).ok_or_else(|| Error::UnknownType {
@ -558,7 +593,7 @@ impl<'a> Environment<'a> {
.keys() .keys()
.map(|t| t.to_string()) .map(|t| t.to_string())
.collect(), .collect(),
location, location: Span::create(location.start, m.len()),
})?; })?;
self.unused_modules.remove(m); self.unused_modules.remove(m);

View File

@ -573,7 +573,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
// NOTE: // NOTE:
// Type namespaces are completely erased during type-check. // Type namespaces are completely erased during type-check.
module: match module { module: match module {
None | Some(Namespace::Type(_)) => None, None | Some(Namespace::Type(..)) => None,
Some(Namespace::Module(m)) => Some(m), Some(Namespace::Module(m)) => Some(m),
}, },
name, name,
@ -609,7 +609,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
// NOTE: // NOTE:
// Type namespaces are completely erased during type-check. // Type namespaces are completely erased during type-check.
module: match module { module: match module {
None | Some(Namespace::Type(_)) => None, None | Some(Namespace::Type(..)) => None,
Some(Namespace::Module(m)) => Some(m), Some(Namespace::Module(m)) => Some(m),
}, },
name, name,