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