Skip to main content

dada_ir_sym/
well_known.rs

1use dada_ir_ast::{
2    ast::Identifier,
3    diagnostic::{Diagnostic, Errors, Reported},
4    span::{Span, Spanned},
5};
6
7use crate::{
8    ir::{
9        classes::{SymAggregate, SymClassMember},
10        functions::SymFunction,
11        module::{SymItem, SymModule},
12        types::SymGenericKind,
13    },
14    prelude::Symbol,
15};
16
17/// Returns the span of the `libdada` prelude. Used when a span is needed for diagnostics
18/// for something built-in, like a primitive.
19pub fn prelude_span(db: &dyn crate::Db) -> Span<'_> {
20    prelude_module(db).span(db)
21}
22
23/// Returns the `libdada` prelude module. It must be present.
24fn prelude_module(db: &dyn crate::Db) -> SymModule<'_> {
25    let krate = db.root().libdada_crate(db);
26    let identifier = Identifier::prelude(db);
27    db.source_file(krate, &[identifier]).symbol(db)
28}
29
30/// Returns the member of the `libdada` prelude with the given name,
31/// reporting an error if it is not found.
32fn prelude_member<'db>(db: &'db dyn crate::Db, name: &str) -> Errors<SymItem<'db>> {
33    let identifier = Identifier::new(db, name);
34    let module = prelude_module(db);
35    module
36        .items(db)
37        .find(|item| item.name(db) == identifier)
38        .ok_or_else(|| report_not_found(db, module, &format!("`{name}` in the `libdada` prelude")))
39}
40
41/// Returns the `String` class from the `libdada` prelude.
42#[salsa::tracked]
43pub fn string_class<'db>(db: &'db dyn crate::Db) -> Errors<SymAggregate<'db>> {
44    match prelude_member(db, "String")? {
45        SymItem::SymClass(class) if class.is_class(db) => {
46            if !class.symbols(db).has_generics_of_kind(db, &[]) {
47                return Err(report_unexpected(
48                    db,
49                    class,
50                    "String",
51                    "it has generic parameters",
52                ));
53            }
54            Ok(class)
55        }
56        m => Err(report_unexpected(db, m, "String", "it is not a class")),
57    }
58}
59
60/// Returns the `literal` function of the `String` class from the `libdada` prelude.
61#[salsa::tracked]
62pub fn string_literal_fn<'db>(db: &'db dyn crate::Db) -> Errors<SymFunction<'db>> {
63    let string_class = string_class(db)?;
64    let literal_fn = string_class
65        .inherent_member_str(db, "literal")
66        .ok_or_else(|| {
67            report_unexpected(
68                db,
69                string_class,
70                "String",
71                "does not have a `literal` member",
72            )
73        })?;
74    match literal_fn {
75        SymClassMember::SymFunction(function) => {
76            if !function.symbols(db).has_generics_of_kind(db, &[]) {
77                return Err(report_unexpected(
78                    db,
79                    function,
80                    "String",
81                    "`literal` should not have generic parameters",
82                ));
83            }
84            Ok(function)
85        }
86        m => Err(report_unexpected(
87            db,
88            m,
89            "String",
90            "`literal` is not a function",
91        )),
92    }
93}
94
95/// Returns the `Pointer` struct from the `libdada` prelude.
96#[salsa::tracked]
97pub fn pointer_struct<'db>(db: &'db dyn crate::Db) -> Errors<SymAggregate<'db>> {
98    match prelude_member(db, "Pointer")? {
99        SymItem::SymClass(class) if class.is_struct(db) => {
100            if !class
101                .symbols(db)
102                .has_generics_of_kind(db, &[SymGenericKind::Type])
103            {
104                return Err(report_unexpected(
105                    db,
106                    class,
107                    "Pointer",
108                    "it should have 1 generic parameter",
109                ));
110            }
111            Ok(class)
112        }
113        m => Err(report_unexpected(db, m, "Pointer", "it is not a struct")),
114    }
115}
116
117fn report_not_found<'db>(db: &'db dyn crate::Db, module: SymModule<'db>, name: &str) -> Reported {
118    let module_span = module.span(db);
119    Diagnostic::error(db, module_span, format!("could not find {name}")).report(db)
120}
121
122fn report_unexpected<'db>(
123    db: &'db dyn crate::Db,
124    spanned: impl Spanned<'db>,
125    name: &str,
126    problem: &str,
127) -> Reported {
128    let span = spanned.span(db);
129    Diagnostic::error(db, span, format!("found {name} but {problem}")).report(db)
130}