1use std::sync::Arc;
2
3use cairo_lang_defs::ids::{
4 EnumId, LanguageElementId, LookupItemId, ModuleItemId, VariantId, VariantLongId,
5};
6use cairo_lang_diagnostics::{Diagnostics, Maybe, ToMaybe};
7use cairo_lang_proc_macros::{DebugWithDb, SemanticObject};
8use cairo_lang_syntax::attribute::structured::{Attribute, AttributeListStructurize};
9use cairo_lang_syntax::node::{Terminal, TypedStablePtr, TypedSyntaxNode, ast};
10use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
11use cairo_lang_utils::{Intern, LookupIntern, Upcast};
12use itertools::enumerate;
13use smol_str::SmolStr;
14
15use super::attribute::SemanticQueryAttrs;
16use super::feature_kind::extract_item_feature_config;
17use super::generics::{GenericParamsData, semantic_generic_params};
18use crate::corelib::unit_ty;
19use crate::db::SemanticGroup;
20use crate::diagnostic::SemanticDiagnosticKind::*;
21use crate::diagnostic::{SemanticDiagnostics, SemanticDiagnosticsBuilder};
22use crate::expr::inference::InferenceId;
23use crate::expr::inference::canonic::ResultNoErrEx;
24use crate::resolve::{Resolver, ResolverData};
25use crate::substitution::{GenericSubstitution, SemanticRewriter};
26use crate::types::{add_type_based_diagnostics, resolve_type};
27use crate::{ConcreteEnumId, SemanticDiagnostic, semantic};
28
29#[cfg(test)]
30#[path = "enm_test.rs"]
31mod test;
32
33#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
35#[debug_db(dyn SemanticGroup + 'static)]
36pub struct EnumDeclarationData {
37 diagnostics: Diagnostics<SemanticDiagnostic>,
38 generic_params: Vec<semantic::GenericParam>,
39 attributes: Vec<Attribute>,
40 resolver_data: Arc<ResolverData>,
41}
42
43pub fn priv_enum_declaration_data(
45 db: &dyn SemanticGroup,
46 enum_id: EnumId,
47) -> Maybe<EnumDeclarationData> {
48 let mut diagnostics = SemanticDiagnostics::default();
49 let enum_ast = db.module_enum_by_id(enum_id)?.to_maybe()?;
53 let syntax_db = db.upcast();
54
55 let generic_params_data = db.enum_generic_params_data(enum_id)?;
57 let inference_id =
58 InferenceId::LookupItemDeclaration(LookupItemId::ModuleItem(ModuleItemId::Enum(enum_id)));
59 let mut resolver = Resolver::with_data(
60 db,
61 (*generic_params_data.resolver_data).clone_with_inference_id(db, inference_id),
62 );
63 diagnostics.extend(generic_params_data.diagnostics);
64 let generic_params = generic_params_data.generic_params;
65 let attributes = enum_ast.attributes(syntax_db).structurize(syntax_db);
66
67 let inference = &mut resolver.inference();
69 inference.finalize(&mut diagnostics, enum_ast.stable_ptr().untyped());
70
71 let generic_params = inference.rewrite(generic_params).no_err();
72
73 let resolver_data = Arc::new(resolver.data);
74 Ok(EnumDeclarationData {
75 diagnostics: diagnostics.build(),
76 generic_params,
77 attributes,
78 resolver_data,
79 })
80}
81
82pub fn enum_declaration_diagnostics(
84 db: &dyn SemanticGroup,
85 enum_id: EnumId,
86) -> Diagnostics<SemanticDiagnostic> {
87 db.priv_enum_declaration_data(enum_id).map(|data| data.diagnostics).unwrap_or_default()
88}
89
90pub fn enum_generic_params(
92 db: &dyn SemanticGroup,
93 enum_id: EnumId,
94) -> Maybe<Vec<semantic::GenericParam>> {
95 Ok(db.enum_generic_params_data(enum_id)?.generic_params)
96}
97
98pub fn enum_generic_params_data(
100 db: &dyn SemanticGroup,
101 enum_id: EnumId,
102) -> Maybe<GenericParamsData> {
103 let module_file_id = enum_id.module_file_id(db.upcast());
104 let mut diagnostics = SemanticDiagnostics::default();
105 let enum_ast = db.module_enum_by_id(enum_id)?.to_maybe()?;
106
107 let inference_id =
109 InferenceId::LookupItemGenerics(LookupItemId::ModuleItem(ModuleItemId::Enum(enum_id)));
110 let mut resolver = Resolver::new(db, module_file_id, inference_id);
111 resolver.set_feature_config(&enum_id, &enum_ast, &mut diagnostics);
112 let generic_params = semantic_generic_params(
113 db,
114 &mut diagnostics,
115 &mut resolver,
116 module_file_id,
117 &enum_ast.generic_params(db.upcast()),
118 );
119 let inference = &mut resolver.inference();
120 inference.finalize(&mut diagnostics, enum_ast.stable_ptr().untyped());
121
122 let generic_params = inference.rewrite(generic_params).no_err();
123 let resolver_data = Arc::new(resolver.data);
124 Ok(GenericParamsData { generic_params, diagnostics: diagnostics.build(), resolver_data })
125}
126
127pub fn enum_attributes(db: &dyn SemanticGroup, enum_id: EnumId) -> Maybe<Vec<Attribute>> {
129 Ok(db.priv_enum_declaration_data(enum_id)?.attributes)
130}
131
132pub fn enum_declaration_resolver_data(
134 db: &dyn SemanticGroup,
135 enum_id: EnumId,
136) -> Maybe<Arc<ResolverData>> {
137 Ok(db.priv_enum_declaration_data(enum_id)?.resolver_data)
138}
139
140#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
142#[debug_db(dyn SemanticGroup + 'static)]
143pub struct EnumDefinitionData {
144 diagnostics: Diagnostics<SemanticDiagnostic>,
145 variants: OrderedHashMap<SmolStr, VariantId>,
146 variant_semantic: OrderedHashMap<VariantId, Variant>,
147 resolver_data: Arc<ResolverData>,
148}
149
150#[derive(Clone, Debug, Hash, PartialEq, Eq, DebugWithDb)]
151#[debug_db(dyn SemanticGroup + 'static)]
152pub struct Variant {
153 pub enum_id: EnumId,
154 pub id: VariantId,
155 pub ty: semantic::TypeId,
156 pub idx: usize,
158}
159
160#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
161pub struct ConcreteVariant {
162 pub concrete_enum_id: ConcreteEnumId,
163 pub id: VariantId,
164 pub ty: semantic::TypeId,
165 #[dont_rewrite]
167 pub idx: usize,
168}
169
170#[derive(Clone, Debug, Hash, PartialEq, Eq, DebugWithDb, SemanticObject)]
173#[debug_db(dyn SemanticGroup + 'static)]
174pub struct ValueSelectorArm {
175 #[dont_rewrite]
176 pub value: usize,
177}
178
179#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
181pub enum MatchArmSelector {
182 VariantId(ConcreteVariant),
183 Value(ValueSelectorArm),
184}
185
186pub fn priv_enum_definition_data(
188 db: &dyn SemanticGroup,
189 enum_id: EnumId,
190) -> Maybe<EnumDefinitionData> {
191 let defs_db = db.upcast();
192
193 let module_file_id = enum_id.module_file_id(defs_db);
194 let crate_id = module_file_id.0.owning_crate(defs_db);
195 let mut diagnostics = SemanticDiagnostics::default();
196 let enum_ast = db.module_enum_by_id(enum_id)?.to_maybe()?;
200 let syntax_db = db.upcast();
201
202 let generic_params_data = db.enum_generic_params_data(enum_id)?;
204 let inference_id =
205 InferenceId::LookupItemDefinition(LookupItemId::ModuleItem(ModuleItemId::Enum(enum_id)));
206 let mut resolver = Resolver::with_data(
207 db,
208 (*generic_params_data.resolver_data).clone_with_inference_id(db, inference_id),
209 );
210 diagnostics.extend(generic_params_data.diagnostics);
211
212 let mut variants = OrderedHashMap::default();
214 let mut variant_semantic = OrderedHashMap::default();
215 for (variant_idx, variant) in enumerate(enum_ast.variants(syntax_db).elements(syntax_db)) {
216 let feature_restore = resolver
217 .data
218 .feature_config
219 .override_with(extract_item_feature_config(db, crate_id, &variant, &mut diagnostics));
220 let id = VariantLongId(module_file_id, variant.stable_ptr()).intern(db);
221 let ty = match variant.type_clause(syntax_db) {
222 ast::OptionTypeClause::Empty(_) => unit_ty(db),
223 ast::OptionTypeClause::TypeClause(type_clause) => {
224 resolve_type(db, &mut diagnostics, &mut resolver, &type_clause.ty(db.upcast()))
225 }
226 };
227 let variant_name = variant.name(syntax_db).text(syntax_db);
228 if let Some(_other_variant) = variants.insert(variant_name.clone(), id) {
229 diagnostics.report(&variant, EnumVariantRedefinition { enum_id, variant_name });
230 }
231 variant_semantic.insert(id, Variant { enum_id, id, ty, idx: variant_idx });
232 resolver.data.feature_config.restore(feature_restore);
233 }
234
235 let inference = &mut resolver.inference();
237 inference.finalize(&mut diagnostics, enum_ast.stable_ptr().untyped());
238
239 for (_, variant) in variant_semantic.iter_mut() {
240 variant.ty = inference.rewrite(variant.ty).no_err();
241 }
242
243 let resolver_data = Arc::new(resolver.data);
244 Ok(EnumDefinitionData {
245 diagnostics: diagnostics.build(),
246 variants,
247 variant_semantic,
248 resolver_data,
249 })
250}
251
252pub fn enum_definition_diagnostics(
254 db: &dyn SemanticGroup,
255 enum_id: EnumId,
256) -> Diagnostics<SemanticDiagnostic> {
257 let Ok(data) = db.priv_enum_definition_data(enum_id) else {
258 return Default::default();
259 };
260
261 let crate_id = data.resolver_data.module_file_id.0.owning_crate(db.upcast());
262
263 if db
266 .declared_phantom_type_attributes(crate_id)
267 .iter()
268 .any(|attr| enum_id.has_attr(db, attr).unwrap_or_default())
269 {
270 return data.diagnostics;
271 }
272 let mut diagnostics = SemanticDiagnostics::from(data.diagnostics);
273 for (_, variant) in data.variant_semantic.iter() {
274 let stable_ptr = variant.id.stable_ptr(db.upcast());
275 add_type_based_diagnostics(db, &mut diagnostics, variant.ty, stable_ptr);
276 if variant.ty.is_phantom(db) {
277 diagnostics.report(stable_ptr, NonPhantomTypeContainingPhantomType);
278 }
279 }
280 diagnostics.build()
281}
282
283pub fn enum_definition_resolver_data(
285 db: &dyn SemanticGroup,
286 enum_id: EnumId,
287) -> Maybe<Arc<ResolverData>> {
288 Ok(db.priv_enum_definition_data(enum_id)?.resolver_data)
289}
290
291pub fn enum_variants(
293 db: &dyn SemanticGroup,
294 enum_id: EnumId,
295) -> Maybe<OrderedHashMap<SmolStr, VariantId>> {
296 Ok(db.priv_enum_definition_data(enum_id)?.variants)
297}
298
299pub fn variant_semantic(
301 db: &dyn SemanticGroup,
302 enum_id: EnumId,
303 variant_id: VariantId,
304) -> Maybe<Variant> {
305 let data = db.priv_enum_definition_data(enum_id)?;
306 data.variant_semantic.get(&variant_id).cloned().to_maybe()
307}
308
309pub trait SemanticEnumEx<'a>: Upcast<dyn SemanticGroup + 'a> {
311 fn concrete_enum_variant(
313 &self,
314 concrete_enum_id: ConcreteEnumId,
315 variant: &Variant,
316 ) -> Maybe<ConcreteVariant> {
317 let db = self.upcast();
320 let generic_params = db.enum_generic_params(concrete_enum_id.enum_id(db))?;
321 let generic_args = concrete_enum_id.lookup_intern(db).generic_args;
322 GenericSubstitution::new(&generic_params, &generic_args).substitute(
323 db,
324 ConcreteVariant { concrete_enum_id, id: variant.id, ty: variant.ty, idx: variant.idx },
325 )
326 }
327
328 fn concrete_enum_variants(
330 &self,
331 concrete_enum_id: ConcreteEnumId,
332 ) -> Maybe<Vec<ConcreteVariant>> {
333 let db = self.upcast();
336 let enum_id = concrete_enum_id.enum_id(db);
337 db.enum_variants(enum_id)?
338 .values()
339 .map(|variant_id| {
340 db.concrete_enum_variant(
341 concrete_enum_id,
342 &db.variant_semantic(enum_id, *variant_id)?,
343 )
344 })
345 .collect()
346 }
347}
348
349impl<'a, T: Upcast<dyn SemanticGroup + 'a> + ?Sized> SemanticEnumEx<'a> for T {}