Skip to main content

lisette_semantics/checker/registration/
mod.rs

1mod builtins;
2mod convert;
3mod methods;
4mod types;
5
6use rustc_hash::FxHashMap as HashMap;
7
8use stdlib::get_go_stdlib_typedef;
9use syntax::ast::{
10    Annotation, Attribute, AttributeArg, EnumVariant, Expression, FunctionDefinition, Generic,
11    Span, StructKind, Visibility as SyntacticVisibility,
12};
13use syntax::program::{Definition, File, Visibility};
14use syntax::types::Type;
15
16use super::Checker;
17
18pub(super) fn extract_go_name(attributes: &[Attribute]) -> Option<String> {
19    attributes
20        .iter()
21        .filter(|a| a.name == "go")
22        .flat_map(|a| a.args.iter())
23        .find_map(|arg| {
24            if let AttributeArg::String(name) = arg {
25                Some(name.clone())
26            } else {
27                None
28            }
29        })
30}
31
32pub(super) fn extract_attribute_flags(attributes: &[Attribute], name: &str) -> Vec<String> {
33    attributes
34        .iter()
35        .filter(|a| a.name == name)
36        .flat_map(|a| {
37            a.args.iter().filter_map(|arg| {
38                if let AttributeArg::Flag(name) = arg {
39                    Some(name.clone())
40                } else {
41                    None
42                }
43            })
44        })
45        .collect()
46}
47
48impl Checker<'_, '_> {
49    fn definition_exists(&self, qualified_name: &str) -> bool {
50        self.store
51            .get_module(&self.cursor.module_id)
52            .expect("current module must exist in store")
53            .definitions
54            .contains_key(qualified_name)
55    }
56
57    fn type_definition_exists(&self, qualified_name: &str) -> bool {
58        self.store
59            .get_module(&self.cursor.module_id)
60            .expect("current module must exist in store")
61            .definitions
62            .get(qualified_name)
63            .is_some_and(|d| {
64                matches!(
65                    d,
66                    Definition::Struct { .. }
67                        | Definition::Enum { .. }
68                        | Definition::ValueEnum { .. }
69                        | Definition::Interface { .. }
70                        | Definition::TypeAlias { .. }
71                )
72            })
73    }
74
75    pub fn register_module(&mut self, id: &str) {
76        self.cursor.module_id = id.to_string();
77
78        let type_name_entries = {
79            let module = self
80                .store
81                .get_module(id)
82                .expect("module must exist for declaration");
83            let mut entries = Vec::new();
84            for file in module.files.values() {
85                entries.extend(self.collect_type_name_entries(&file.items, &Visibility::Private));
86            }
87            for file in module.all_typedefs() {
88                entries.extend(self.collect_type_name_entries(&file.items, &Visibility::Private));
89            }
90            entries
91        };
92        let module = self
93            .store
94            .get_module_mut(id)
95            .expect("module must exist for declaration");
96        for (qualified_name, definition) in type_name_entries {
97            module
98                .definitions
99                .entry(qualified_name.into())
100                .or_insert(definition);
101        }
102
103        let file_data: Vec<_> = {
104            let module = self
105                .store
106                .get_module(id)
107                .expect("module must exist for declaration");
108            module
109                .files
110                .iter()
111                .chain(module.typedefs.iter())
112                .map(|(file_id, f)| (*file_id, f.imports()))
113                .collect()
114        };
115
116        for (file_id, imports) in &file_data {
117            self.reset_scopes();
118            self.cursor.file_id = Some(*file_id);
119
120            self.put_prelude_in_scope();
121            self.put_unprefixed_module_in_scope(id);
122            self.put_imported_modules_in_scope(imports);
123
124            let items = std::mem::take(
125                &mut self
126                    .store
127                    .get_file_mut(*file_id)
128                    .expect("file must exist for registration")
129                    .items,
130            );
131
132            self.register_types(&items);
133            self.register_values(&items, &Visibility::Private);
134
135            self.store
136                .get_file_mut(*file_id)
137                .expect("file must exist after registration")
138                .items = items;
139        }
140
141        self.cursor.file_id = None;
142
143        let module = self.store.get_module(id).expect("module must exist");
144        let ufcs_entries = crate::call_classification::compute_module_ufcs(module, id);
145        self.ufcs_methods.extend(ufcs_entries);
146    }
147
148    /// Register a Go stdlib module. Unlike regular modules, Go modules:
149    /// - Export everything as public
150    /// - Don't put their own module in scope (no self-references like `MyModule.Type`)
151    pub fn parse_and_register_go_module(&mut self, module_id: &str, source: &str) {
152        if self.store.is_visited(module_id) {
153            return;
154        }
155
156        self.store.mark_visited(module_id);
157        self.store.add_module(module_id);
158
159        let file_id = self.store.new_file_id();
160        let filename = format!("{}.d.lis", module_id.replace('/', "_"));
161
162        let build_result = syntax::build_ast(source, file_id);
163        if build_result.failed() {
164            for error in &build_result.errors {
165                eprintln!("bindgen: error parsing {}: {:?}", filename, error);
166            }
167        }
168
169        let file = File {
170            id: file_id,
171            module_id: module_id.to_string(),
172            name: filename,
173            source: source.to_string(),
174            items: build_result.ast,
175        };
176
177        let imports = file.imports();
178
179        for import in &imports {
180            if let Some(go_pkg) = import.name.strip_prefix("go:") {
181                let import_module_id = format!("go:{}", go_pkg);
182                if let Some(import_source) = get_go_stdlib_typedef(go_pkg) {
183                    self.parse_and_register_go_module(&import_module_id, import_source);
184                }
185            }
186        }
187
188        self.store.store_file(module_id, file);
189
190        let prev_module_id = self.cursor.module_id.clone();
191        self.cursor.module_id = module_id.to_string();
192
193        self.reset_scopes();
194        self.cursor.file_id = Some(file_id);
195        self.put_prelude_in_scope();
196        self.put_imported_modules_in_scope(&imports);
197
198        let items = std::mem::take(
199            &mut self
200                .store
201                .get_file_mut(file_id)
202                .expect("file must exist after store_file")
203                .items,
204        );
205        self.register_types_and_values(&items, &Visibility::Public);
206
207        self.cursor.file_id = None;
208        self.cursor.module_id = prev_module_id;
209    }
210
211    pub fn register_types_and_values(&mut self, items: &[Expression], visibility: &Visibility) {
212        self.register_type_names(items, visibility);
213        self.register_types(items);
214        self.register_values(items, visibility);
215    }
216
217    pub fn register_type_names(&mut self, items: &[Expression], visibility: &Visibility) {
218        let entries = self.collect_type_name_entries(items, visibility);
219        let module = self
220            .store
221            .get_module_mut(&self.cursor.module_id)
222            .expect("current module must exist in store");
223        for (qualified_name, definition) in entries {
224            module
225                .definitions
226                .entry(qualified_name.into())
227                .or_insert(definition);
228        }
229    }
230
231    fn collect_type_name_entries(
232        &self,
233        items: &[Expression],
234        visibility: &Visibility,
235    ) -> Vec<(String, Definition)> {
236        let mut entries = Vec::new();
237
238        for item in items {
239            let (name, generics, syntactic_visibility) = match item {
240                Expression::Enum {
241                    name,
242                    generics,
243                    visibility,
244                    ..
245                } => (name, generics, *visibility),
246                Expression::ValueEnum {
247                    name, visibility, ..
248                } => (name, &Vec::new() as &Vec<Generic>, *visibility),
249                Expression::Struct {
250                    name,
251                    generics,
252                    visibility,
253                    ..
254                } => (name, generics, *visibility),
255                Expression::Interface {
256                    name,
257                    generics,
258                    visibility,
259                    ..
260                } => (name, generics, *visibility),
261                Expression::TypeAlias {
262                    name,
263                    generics,
264                    visibility,
265                    ..
266                } => (name, generics, *visibility),
267                _ => continue,
268            };
269
270            let qualified_name = self.qualify_name(name);
271            let args: Vec<Type> = generics
272                .iter()
273                .map(|g| Type::Parameter(g.name.clone()))
274                .collect();
275
276            let constructor_ty = Type::Constructor {
277                id: qualified_name.clone().into(),
278                params: args,
279                underlying_ty: None,
280            };
281
282            let ty = if generics.is_empty() {
283                constructor_ty
284            } else {
285                Type::Forall {
286                    vars: generics.iter().map(|g| g.name.clone()).collect(),
287                    body: Box::new(constructor_ty),
288                }
289            };
290
291            let item_visibility = match visibility {
292                Visibility::Local => Visibility::Local,
293                _ => {
294                    if syntactic_visibility == SyntacticVisibility::Public || self.is_d_lis() {
295                        Visibility::Public
296                    } else {
297                        Visibility::Private
298                    }
299                }
300            };
301
302            entries.push((
303                qualified_name,
304                Definition::Value {
305                    visibility: item_visibility,
306                    ty,
307                    name_span: None,
308                    allowed_lints: vec![],
309                    go_hints: vec![],
310                    go_name: None,
311                    doc: None,
312                },
313            ));
314        }
315
316        entries
317    }
318
319    pub fn register_types(&mut self, items: &[Expression]) {
320        for item in items {
321            match item {
322                Expression::Enum {
323                    name,
324                    name_span,
325                    generics,
326                    variants,
327                    span,
328                    doc,
329                    ..
330                } => self.populate_enum(name, name_span, generics, variants, span, doc),
331                Expression::ValueEnum {
332                    name,
333                    name_span,
334                    underlying_ty,
335                    variants,
336                    doc,
337                    ..
338                } => {
339                    self.populate_value_enum(name, name_span, underlying_ty.as_ref(), variants, doc)
340                }
341                Expression::Struct {
342                    name,
343                    name_span,
344                    generics,
345                    fields,
346                    kind,
347                    span,
348                    doc,
349                    ..
350                } => self.populate_struct(name, name_span, generics, fields, *kind, span, doc),
351                Expression::ImplBlock {
352                    annotation,
353                    methods,
354                    generics,
355                    span,
356                    ..
357                } => self.populate_impl_methods(annotation, generics, methods, span),
358                Expression::Interface {
359                    name,
360                    name_span,
361                    generics,
362                    parents,
363                    method_signatures,
364                    span,
365                    doc,
366                    ..
367                } => self.populate_interface(
368                    name,
369                    name_span,
370                    generics,
371                    parents,
372                    method_signatures,
373                    span,
374                    doc,
375                ),
376                Expression::TypeAlias {
377                    name,
378                    name_span,
379                    generics,
380                    annotation,
381                    span,
382                    doc,
383                    ..
384                } => self.populate_type_alias(name, name_span, generics, annotation, span, doc),
385                _ => (),
386            }
387        }
388    }
389
390    fn compute_item_visibility(
391        &self,
392        syntactic: &SyntacticVisibility,
393        scope: &Visibility,
394    ) -> Visibility {
395        match scope {
396            Visibility::Local => Visibility::Local,
397            _ if *syntactic == SyntacticVisibility::Public || self.is_d_lis() => Visibility::Public,
398            _ => Visibility::Private,
399        }
400    }
401
402    pub fn register_values(&mut self, items: &[Expression], visibility: &Visibility) {
403        for item in items {
404            match item {
405                Expression::Function { .. } => self.register_function_value(item, visibility),
406                Expression::Const { .. } => self.register_const_value(item, visibility),
407                Expression::VariableDeclaration { .. } => {
408                    self.register_variable_declaration(item, visibility)
409                }
410                Expression::Struct {
411                    kind: StructKind::Tuple,
412                    ..
413                } => self.register_tuple_struct_constructor(item),
414                _ => (),
415            }
416        }
417    }
418
419    fn register_function_value(&mut self, item: &Expression, visibility: &Visibility) {
420        let Expression::Function {
421            name,
422            name_span,
423            attributes,
424            generics,
425            span,
426            body,
427            visibility: syntactic_visibility,
428            doc,
429            ..
430        } = item
431        else {
432            return;
433        };
434
435        if body.is_noop() && self.is_lis() {
436            self.sink
437                .push(diagnostics::infer::bodyless_function_outside_typedef(*span));
438        }
439
440        let fn_sig = item.to_function_signature();
441        let qualified_name = self.qualify_name(name);
442
443        self.scopes.push();
444        self.put_in_scope(generics);
445
446        let fn_ty = self.extract_function_signature(&fn_sig, span);
447
448        self.scopes.pop();
449
450        let item_visibility = self.compute_item_visibility(syntactic_visibility, visibility);
451
452        if self.is_lis() && self.definition_exists(&qualified_name) {
453            self.sink.push(diagnostics::infer::duplicate_definition(
454                "function", name, *name_span,
455            ));
456        }
457
458        let module = self
459            .store
460            .get_module_mut(&self.cursor.module_id)
461            .expect("current module must exist in store");
462        module.definitions.insert(
463            qualified_name.into(),
464            Definition::Value {
465                visibility: item_visibility,
466                ty: fn_ty,
467                name_span: Some(*name_span),
468                allowed_lints: extract_attribute_flags(attributes, "allow"),
469                go_hints: extract_attribute_flags(attributes, "go"),
470                go_name: extract_go_name(attributes),
471                doc: doc.clone(),
472            },
473        );
474    }
475
476    fn register_const_value(&mut self, item: &Expression, visibility: &Visibility) {
477        let Expression::Const {
478            identifier,
479            identifier_span,
480            annotation: maybe_annotation,
481            expression,
482            span,
483            visibility: syntactic_visibility,
484            doc,
485            ..
486        } = item
487        else {
488            return;
489        };
490
491        let has_value = !expression.is_noop();
492
493        if !has_value && self.is_lis() {
494            self.sink
495                .push(diagnostics::infer::valueless_const_outside_typedef(*span));
496        }
497
498        if !has_value && maybe_annotation.is_none() && self.is_d_lis() {
499            self.sink
500                .push(diagnostics::infer::valueless_const_missing_annotation(
501                    *span,
502                ));
503        }
504
505        let qualified_name = self.qualify_name(identifier);
506
507        let const_ty = if let Some(annotation) = maybe_annotation {
508            self.convert_to_type(annotation, span)
509        } else {
510            self.type_from_literal_expression(expression)
511                .unwrap_or_else(|| self.new_type_var())
512        };
513
514        let item_visibility = self.compute_item_visibility(syntactic_visibility, visibility);
515
516        if self.is_lis() && self.definition_exists(&qualified_name) {
517            self.sink.push(diagnostics::infer::duplicate_definition(
518                "constant",
519                identifier,
520                *identifier_span,
521            ));
522        }
523
524        let module = self
525            .store
526            .get_module_mut(&self.cursor.module_id)
527            .expect("current module must exist in store");
528        module.definitions.insert(
529            qualified_name.into(),
530            Definition::Value {
531                visibility: item_visibility,
532                ty: const_ty,
533                name_span: Some(*identifier_span),
534                allowed_lints: vec![],
535                go_hints: vec![],
536                go_name: None,
537                doc: doc.clone(),
538            },
539        );
540    }
541
542    fn register_variable_declaration(&mut self, item: &Expression, visibility: &Visibility) {
543        let Expression::VariableDeclaration {
544            name,
545            name_span,
546            annotation,
547            span,
548            visibility: syntactic_visibility,
549            doc,
550            ..
551        } = item
552        else {
553            return;
554        };
555
556        if self.is_lis() {
557            self.sink
558                .push(diagnostics::infer::variable_declaration_outside_typedef(
559                    *span,
560                ));
561        }
562
563        let qualified_name = self.qualify_name(name);
564        let var_ty = self.convert_to_type(annotation, span);
565
566        let item_visibility = self.compute_item_visibility(syntactic_visibility, visibility);
567
568        let module = self
569            .store
570            .get_module_mut(&self.cursor.module_id)
571            .expect("current module must exist in store");
572        module.definitions.insert(
573            qualified_name.into(),
574            Definition::Value {
575                visibility: item_visibility,
576                ty: var_ty,
577                name_span: Some(*name_span),
578                allowed_lints: vec![],
579                go_hints: vec![],
580                go_name: None,
581                doc: doc.clone(),
582            },
583        );
584    }
585
586    fn register_tuple_struct_constructor(&mut self, item: &Expression) {
587        let Expression::Struct {
588            name,
589            generics,
590            fields,
591            kind: StructKind::Tuple,
592            span,
593            ..
594        } = item
595        else {
596            return;
597        };
598
599        let qualified_name = self.qualify_name(name);
600        let struct_ty = self
601            .store
602            .get_type(&qualified_name)
603            .expect("struct type scheme must exist")
604            .clone();
605
606        self.scopes.push();
607        self.put_in_scope(generics);
608
609        let field_types: Vec<Type> = fields
610            .iter()
611            .map(|f| self.convert_to_type(&f.annotation, span))
612            .collect();
613
614        self.scopes.pop();
615
616        let constructor_ty =
617            tuple_struct_constructor_type_from_fields(&field_types, &struct_ty, generics);
618
619        let scope = self.scopes.current_mut();
620        scope
621            .values
622            .insert(qualified_name.clone(), constructor_ty.clone());
623        scope
624            .values
625            .insert(name.to_string(), constructor_ty.clone());
626
627        let module = self
628            .store
629            .get_module_mut(&self.cursor.module_id)
630            .expect("current module must exist in store");
631        if let Some(Definition::Struct { constructor, .. }) =
632            module.definitions.get_mut(qualified_name.as_str())
633        {
634            *constructor = Some(constructor_ty);
635        }
636    }
637
638    pub(crate) fn extract_function_signature(
639        &mut self,
640        function: &FunctionDefinition,
641        span: &Span,
642    ) -> Type {
643        let generics = &function.generics;
644
645        self.scopes.push();
646        self.put_in_scope(generics);
647
648        let mut bounds = vec![];
649
650        for g in generics {
651            let qualified_name = self.qualify_name(&g.name);
652
653            for b in &g.bounds {
654                let bound_ty = self.convert_to_type(b, span);
655
656                self.scopes
657                    .current_mut()
658                    .trait_bounds
659                    .get_or_insert_with(HashMap::default)
660                    .entry(qualified_name.clone())
661                    .or_default()
662                    .push(bound_ty.clone());
663
664                bounds.push(syntax::types::Bound {
665                    param_name: g.name.clone(),
666                    generic: Type::Parameter(g.name.clone()),
667                    ty: bound_ty,
668                });
669            }
670        }
671
672        let param_types: Vec<Type> = function
673            .params
674            .iter()
675            .map(|binding| {
676                binding
677                    .annotation
678                    .as_ref()
679                    .map(|a| self.convert_to_type(a, span))
680                    .unwrap_or_else(|| self.new_type_var())
681            })
682            .collect();
683
684        let return_ty = match &function.annotation {
685            Annotation::Unknown => self.type_unit(),
686            _ => self.convert_to_type(&function.annotation, span),
687        };
688
689        self.scopes.pop();
690
691        let param_mutability: Vec<bool> = function.params.iter().map(|b| b.mutable).collect();
692
693        let base_fn_ty = Type::Function {
694            params: param_types,
695            param_mutability,
696            bounds,
697            return_type: return_ty.into(),
698        };
699
700        if generics.is_empty() {
701            base_fn_ty
702        } else {
703            Type::Forall {
704                vars: generics.iter().map(|g| g.name.clone()).collect(),
705                body: Box::new(base_fn_ty),
706            }
707        }
708    }
709}
710
711pub(super) fn enum_variant_constructor_type(
712    enum_variant: &EnumVariant,
713    enum_ty: &Type,
714    generics: &[Generic],
715) -> Type {
716    if enum_variant.fields.is_empty() {
717        return enum_ty.clone();
718    }
719
720    let return_type = match enum_ty {
721        Type::Forall { body, .. } => body.as_ref().clone(),
722        _ => enum_ty.clone(),
723    };
724
725    let fn_ty = Type::Function {
726        param_mutability: vec![false; enum_variant.fields.len()],
727        params: enum_variant.fields.iter().map(|f| f.ty.clone()).collect(),
728        bounds: Default::default(),
729        return_type: return_type.into(),
730    };
731
732    if generics.is_empty() {
733        fn_ty
734    } else {
735        Type::Forall {
736            vars: generics.iter().map(|g| g.name.clone()).collect(),
737            body: Box::new(fn_ty),
738        }
739    }
740}
741
742fn tuple_struct_constructor_type_from_fields(
743    field_types: &[Type],
744    struct_ty: &Type,
745    generics: &[Generic],
746) -> Type {
747    let return_type = match struct_ty {
748        Type::Forall { body, .. } => body.as_ref().clone(),
749        _ => struct_ty.clone(),
750    };
751
752    let fn_ty = Type::Function {
753        param_mutability: vec![false; field_types.len()],
754        params: field_types.to_vec(),
755        bounds: Default::default(),
756        return_type: return_type.into(),
757    };
758
759    if generics.is_empty() {
760        fn_ty
761    } else {
762        Type::Forall {
763            vars: generics.iter().map(|g| g.name.clone()).collect(),
764            body: Box::new(fn_ty),
765        }
766    }
767}
768
769pub(super) fn wrap_with_impl_generics(
770    fn_ty: &Type,
771    generics: &[Generic],
772    impl_bounds: &[syntax::types::Bound],
773) -> Type {
774    if generics.is_empty() {
775        return fn_ty.clone();
776    }
777
778    let impl_vars: Vec<syntax::EcoString> = generics.iter().map(|g| g.name.clone()).collect();
779
780    let add_impl_bounds = |existing_bounds: &[syntax::types::Bound]| -> Vec<syntax::types::Bound> {
781        impl_bounds
782            .iter()
783            .cloned()
784            .chain(existing_bounds.iter().cloned())
785            .collect()
786    };
787
788    match fn_ty {
789        Type::Forall { vars, body } => {
790            let new_body = match body.as_ref() {
791                Type::Function {
792                    params,
793                    param_mutability,
794                    bounds,
795                    return_type,
796                } => Type::Function {
797                    params: params.clone(),
798                    param_mutability: param_mutability.clone(),
799                    bounds: add_impl_bounds(bounds),
800                    return_type: return_type.clone(),
801                },
802                _ => *body.clone(),
803            };
804            Type::Forall {
805                vars: impl_vars.into_iter().chain(vars.clone()).collect(),
806                body: Box::new(new_body),
807            }
808        }
809        Type::Function {
810            params,
811            param_mutability,
812            bounds,
813            return_type,
814        } => Type::Forall {
815            vars: impl_vars,
816            body: Box::new(Type::Function {
817                params: params.clone(),
818                param_mutability: param_mutability.clone(),
819                bounds: add_impl_bounds(bounds),
820                return_type: return_type.clone(),
821            }),
822        },
823        _ => Type::Forall {
824            vars: impl_vars,
825            body: Box::new(fn_ty.clone()),
826        },
827    }
828}
829
830fn type_contains_constructor(target_id: &str, ty: &Type) -> bool {
831    walk_type(ty, &|id, _| id == target_id)
832}
833
834/// Check if a type contains a recursive generic instantiation.
835/// E.g., a method on `Box<T>` returning `Box<Box<T>>` creates a Go instantiation cycle.
836/// Returns true if `ty` contains `target_id` nested within itself (e.g. `Box<Box<T>>`).
837pub(super) fn has_recursive_instantiation(target_id: &str, ty: &Type) -> bool {
838    walk_type(ty, &|id, params| {
839        id == target_id
840            && params
841                .iter()
842                .any(|p| type_contains_constructor(target_id, p))
843    })
844}
845
846fn walk_type(ty: &Type, predicate: &dyn Fn(&str, &[Type]) -> bool) -> bool {
847    match ty {
848        Type::Constructor { id, params, .. } => {
849            predicate(id, params) || params.iter().any(|p| walk_type(p, predicate))
850        }
851        Type::Function {
852            params,
853            return_type,
854            ..
855        } => params.iter().any(|p| walk_type(p, predicate)) || walk_type(return_type, predicate),
856        Type::Tuple(elems) => elems.iter().any(|e| walk_type(e, predicate)),
857        Type::Forall { body, .. } => walk_type(body, predicate),
858        _ => false,
859    }
860}