1use dada_ir_ast::{
2 ast::{AstItem, AstModule, AstUse, Identifier},
3 diagnostic::{Diagnostic, Level},
4 inputs::SourceFile,
5 span::{SourceSpanned, Span, Spanned},
6};
7use dada_parser::prelude::SourceFileParse;
8use dada_util::{FromImpls, Map, SalsaSerialize};
9
10use crate::{
11 check::{
12 scope::Scope,
13 scope_tree::{ScopeItem, ScopeTreeNode},
14 },
15 ir::{
16 classes::SymAggregate, functions::SymFunction, primitive::SymPrimitive,
17 variables::SymVariable,
18 },
19 prelude::Symbol,
20 well_known,
21};
22
23#[derive(SalsaSerialize)]
24#[salsa::tracked(debug)]
25pub struct SymModule<'db> {
26 pub source: AstModule<'db>,
27
28 #[tracked]
30 #[return_ref]
31 pub(crate) class_map: Map<Identifier<'db>, SymAggregate<'db>>,
32 #[tracked]
33 #[return_ref]
34 pub(crate) function_map: Map<Identifier<'db>, SymFunction<'db>>,
35 #[tracked]
36 #[return_ref]
37 pub(crate) ast_use_map: Map<Identifier<'db>, AstUse<'db>>,
38}
39
40impl<'db> Spanned<'db> for SymModule<'db> {
41 fn span(&self, db: &'db dyn dada_ir_ast::Db) -> Span<'db> {
42 self.source(db).span(db)
43 }
44}
45
46#[salsa::interned(debug)]
48pub struct SymPrelude<'db> {
49 pub items: Vec<SymItem<'db>>,
50}
51
52#[salsa::tracked]
53impl<'db> SymModule<'db> {
54 pub fn name(self, db: &'db dyn crate::Db) -> Identifier<'db> {
55 self.source(db).name(db)
56 }
57
58 pub fn mod_scope(self, db: &'db dyn crate::Db) -> Scope<'db, 'db> {
60 Scope::new(db, self.span(db)).with_link(self)
61 }
62
63 pub fn items(self, db: &'db dyn crate::Db) -> impl Iterator<Item = SymItem<'db>> {
65 self.class_map(db)
66 .values()
67 .copied()
68 .map(SymItem::from)
69 .chain(self.function_map(db).values().copied().map(SymItem::from))
70 }
71
72 pub fn function_named(
74 self,
75 db: &'db dyn crate::Db,
76 name: Identifier<'db>,
77 ) -> Option<SymFunction<'db>> {
78 self.function_map(db).get(&name).copied()
79 }
80}
81
82#[salsa::tracked]
83impl<'db> ScopeTreeNode<'db> for SymModule<'db> {
84 fn direct_super_scope(self, _db: &'db dyn crate::Db) -> Option<ScopeItem<'db>> {
85 None }
87
88 #[salsa::tracked(return_ref)]
89 fn direct_generic_parameters(self, _db: &'db dyn crate::Db) -> Vec<SymVariable<'db>> {
90 vec![] }
92
93 fn into_scope(self, db: &'db dyn crate::Db) -> Scope<'db, 'db> {
94 self.mod_scope(db)
95 }
96
97 fn push_direct_ast_where_clauses(
98 self,
99 _db: &'db dyn crate::Db,
100 _out: &mut Vec<dada_ir_ast::ast::AstWhereClause<'db>>,
101 ) {
102 }
104}
105
106impl<'db> Symbol<'db> for SourceFile {
107 type Output = SymModule<'db>;
108
109 fn symbol(self, db: &'db dyn crate::Db) -> Self::Output {
110 self.parse(db).symbol(db)
111 }
112}
113
114#[salsa::tracked]
115impl<'db> Symbol<'db> for AstModule<'db> {
116 type Output = SymModule<'db>;
117
118 #[salsa::tracked]
119 fn symbol(self, db: &'db dyn crate::Db) -> SymModule<'db> {
120 let mut class_map = Map::default();
121 let mut function_map = Map::default();
122 let mut ast_use_map = Map::default();
123 for item in self.items(db) {
124 match *item {
125 AstItem::SourceFile(_) => {}
126 AstItem::Use(ast_use) => {
127 let id = match ast_use.as_id(db) {
128 Some(as_id) => as_id.id,
129 None => ast_use.path(db).last_id(db).id,
130 };
131
132 insert(db, &mut ast_use_map, id, ast_use);
133 }
134 AstItem::Aggregate(ast_class_item) => {
135 insert(
136 db,
137 &mut class_map,
138 ast_class_item.name(db),
139 SymAggregate::new(db, self.into(), ast_class_item),
140 );
141 }
142 AstItem::Function(func) => {
143 insert(
144 db,
145 &mut function_map,
146 func.name(db).id,
147 SymFunction::new(db, self.into(), func.into()),
148 );
149 }
150 AstItem::MainFunction(mfunc) => {
151 insert(
152 db,
153 &mut function_map,
154 Identifier::main(db),
155 SymFunction::new(db, self.into(), mfunc.into()),
156 );
157 }
158 }
159 }
160
161 let canonical_map = &mut Map::default();
164 insert_into_canonical_map(db, canonical_map, &class_map);
165 insert_into_canonical_map(db, canonical_map, &function_map);
166 insert_into_canonical_map(db, canonical_map, &ast_use_map);
167
168 SymModule::new(db, self, class_map, function_map, ast_use_map)
169 }
170}
171
172impl<'db> ScopeTreeNode<'db> for AstModule<'db> {
173 fn direct_super_scope(self, db: &'db dyn crate::Db) -> Option<ScopeItem<'db>> {
174 self.symbol(db).direct_super_scope(db)
175 }
176
177 fn direct_generic_parameters(self, db: &'db dyn crate::Db) -> &'db Vec<SymVariable<'db>> {
178 self.symbol(db).direct_generic_parameters(db)
179 }
180
181 fn into_scope(self, db: &'db dyn crate::Db) -> Scope<'db, 'db> {
182 self.symbol(db).into_scope(db)
183 }
184
185 fn push_direct_ast_where_clauses(
186 self,
187 db: &'db dyn crate::Db,
188 out: &mut Vec<dada_ir_ast::ast::AstWhereClause<'db>>,
189 ) {
190 self.symbol(db).push_direct_ast_where_clauses(db, out);
191 }
192}
193
194fn insert<'db, V: Spanned<'db>>(
195 db: &'db dyn crate::Db,
196 map: &mut Map<Identifier<'db>, V>,
197 id: Identifier<'db>,
198 value: V,
199) {
200 if let Some(other_value) = map.get(&id) {
201 report_duplicate(db, id, value.span(db), other_value.span(db));
202 } else {
203 map.insert(id, value);
204 }
205}
206
207fn insert_into_canonical_map<'db>(
208 db: &'db dyn crate::Db,
209 canonical_map: &mut Map<Identifier<'db>, Span<'db>>,
210 map: &Map<Identifier<'db>, impl Spanned<'db>>,
211) {
212 for (id, value) in map.iter() {
213 let id = *id;
214 let value_span = value.span(db);
215 if let Some(canonical_span) = canonical_map.get(&id) {
216 report_duplicate(db, id, value_span, *canonical_span);
217 } else {
218 canonical_map.insert(id, value_span);
219 }
220 }
221}
222
223fn report_duplicate<'db>(
224 db: &'db dyn crate::Db,
225 id: Identifier<'db>,
226 value_span: Span<'db>,
227 canonical_span: Span<'db>,
228) {
229 Diagnostic::error(
230 db,
231 value_span,
232 "this definition is a duplicate and will be ignored",
233 )
234 .label(db, Level::Error, value_span, "duplicate definition")
235 .label(
236 db,
237 Level::Info,
238 canonical_span,
239 format!("we will map `{id:?}` to this other definition"),
240 )
241 .report(db);
242}
243
244#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, FromImpls)]
245pub enum SymItem<'db> {
246 SymClass(SymAggregate<'db>),
247 SymFunction(SymFunction<'db>),
248 SymPrimitive(SymPrimitive<'db>),
249}
250
251impl<'db> SymItem<'db> {
252 pub fn name(self, db: &'db dyn crate::Db) -> Identifier<'db> {
253 match self {
254 SymItem::SymClass(sym_class) => sym_class.name(db),
255 SymItem::SymFunction(sym_function) => sym_function.name(db),
256 SymItem::SymPrimitive(sym_primitive) => sym_primitive.name(db),
257 }
258 }
259}
260
261impl<'db> Spanned<'db> for SymItem<'db> {
262 fn span(&self, db: &'db dyn dada_ir_ast::Db) -> Span<'db> {
263 match self {
264 SymItem::SymClass(sym_class) => sym_class.span(db),
265 SymItem::SymFunction(sym_function) => sym_function.span(db),
266 SymItem::SymPrimitive(_) => well_known::prelude_span(db),
267 }
268 }
269}
270
271impl<'db> SourceSpanned<'db> for SymItem<'db> {
272 fn source_span(&self, db: &'db dyn dada_ir_ast::Db) -> Span<'db> {
273 match self {
274 SymItem::SymClass(a) => a.source_span(db),
275 SymItem::SymFunction(f) => f.source_span(db),
276 SymItem::SymPrimitive(_) => well_known::prelude_span(db),
277 }
278 }
279}