cairo_lang_semantic/items/
function_with_body.rs1use std::sync::Arc;
2
3use cairo_lang_defs::ids::FunctionWithBodyId;
4use cairo_lang_diagnostics::{DiagnosticAdded, Diagnostics, Maybe, ToMaybe};
5use cairo_lang_proc_macros::DebugWithDb;
6use cairo_lang_syntax::attribute::consts::{IMPLICIT_PRECEDENCE_ATTR, INLINE_ATTR};
7use cairo_lang_syntax::attribute::structured::{Attribute, AttributeArg, AttributeArgVariant};
8use cairo_lang_syntax::node::{TypedStablePtr, ast};
9use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
10use cairo_lang_utils::{Upcast, try_extract_matches};
11use itertools::Itertools;
12
13use super::functions::InlineConfiguration;
14use crate::db::SemanticGroup;
15use crate::diagnostic::{
16 NotFoundItemType, SemanticDiagnosticKind, SemanticDiagnostics, SemanticDiagnosticsBuilder,
17};
18use crate::items::functions::ImplicitPrecedence;
19use crate::resolve::{ResolvedConcreteItem, Resolver, ResolverData};
20use crate::{Arenas, ExprId, PatternId, SemanticDiagnostic, TypeId, semantic};
21
22pub fn function_declaration_diagnostics(
28 db: &dyn SemanticGroup,
29 function_id: FunctionWithBodyId,
30) -> Diagnostics<SemanticDiagnostic> {
31 let declaration_data = match function_id {
32 FunctionWithBodyId::Free(free_function_id) => {
33 db.priv_free_function_declaration_data(free_function_id)
34 }
35 FunctionWithBodyId::Impl(impl_function_id) => db
36 .priv_impl_function_declaration_data(impl_function_id)
37 .map(|x| x.function_declaration_data),
38 FunctionWithBodyId::Trait(trait_function_id) => {
39 db.priv_trait_function_declaration_data(trait_function_id)
40 }
41 };
42 declaration_data.map(|data| data.diagnostics).unwrap_or_default()
43}
44
45pub fn function_declaration_inline_config(
47 db: &dyn SemanticGroup,
48 function_id: FunctionWithBodyId,
49) -> Maybe<InlineConfiguration> {
50 match function_id {
51 FunctionWithBodyId::Free(free_function_id) => {
52 db.free_function_declaration_inline_config(free_function_id)
53 }
54 FunctionWithBodyId::Impl(impl_function_id) => {
55 db.impl_function_declaration_inline_config(impl_function_id)
56 }
57 FunctionWithBodyId::Trait(trait_function_id) => {
58 db.trait_function_declaration_inline_config(trait_function_id)
59 }
60 }
61}
62
63pub fn function_declaration_implicit_precedence(
65 db: &dyn SemanticGroup,
66 function_id: FunctionWithBodyId,
67) -> Maybe<ImplicitPrecedence> {
68 match function_id {
69 FunctionWithBodyId::Free(free_function_id) => {
70 db.free_function_declaration_implicit_precedence(free_function_id)
71 }
72 FunctionWithBodyId::Impl(impl_function_id) => {
73 db.impl_function_declaration_implicit_precedence(impl_function_id)
74 }
75 FunctionWithBodyId::Trait(trait_function_id) => {
76 db.trait_function_declaration_implicit_precedence(trait_function_id)
77 }
78 }
79}
80
81pub fn function_with_body_signature(
83 db: &dyn SemanticGroup,
84 function_id: FunctionWithBodyId,
85) -> Maybe<semantic::Signature> {
86 match function_id {
87 FunctionWithBodyId::Free(free_function_id) => db.free_function_signature(free_function_id),
88 FunctionWithBodyId::Impl(impl_function_id) => db.impl_function_signature(impl_function_id),
89 FunctionWithBodyId::Trait(trait_function_id) => {
90 db.trait_function_signature(trait_function_id)
91 }
92 }
93}
94
95pub fn function_with_body_generic_params(
98 db: &dyn SemanticGroup,
99 function_id: FunctionWithBodyId,
100) -> Maybe<Vec<semantic::GenericParam>> {
101 match function_id {
102 FunctionWithBodyId::Free(free_function_id) => {
103 db.free_function_generic_params(free_function_id)
104 }
105 FunctionWithBodyId::Impl(impl_function_id) => {
106 let mut res = db.impl_def_generic_params(impl_function_id.impl_def_id(db.upcast()))?;
107 res.extend(db.impl_function_generic_params(impl_function_id)?);
108 Ok(res)
109 }
110 FunctionWithBodyId::Trait(trait_function_id) => {
111 let mut res = db.trait_generic_params(trait_function_id.trait_id(db.upcast()))?;
112 res.extend(db.trait_function_generic_params(trait_function_id)?);
113 Ok(res)
114 }
115 }
116}
117
118pub fn function_with_body_attributes(
120 db: &dyn SemanticGroup,
121 function_id: FunctionWithBodyId,
122) -> Maybe<Vec<Attribute>> {
123 match function_id {
124 FunctionWithBodyId::Free(free_function_id) => {
125 Ok(db.priv_free_function_declaration_data(free_function_id)?.attributes)
126 }
127 FunctionWithBodyId::Impl(impl_function_id) => Ok(db
128 .priv_impl_function_declaration_data(impl_function_id)?
129 .function_declaration_data
130 .attributes),
131 FunctionWithBodyId::Trait(trait_function_id) => {
132 Ok(db.priv_trait_function_declaration_data(trait_function_id)?.attributes)
133 }
134 }
135}
136
137#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
140#[debug_db(dyn SemanticGroup + 'static)]
141pub struct FunctionBodyData {
142 pub diagnostics: Diagnostics<SemanticDiagnostic>,
143 pub expr_lookup: UnorderedHashMap<ast::ExprPtr, ExprId>,
144 pub pattern_lookup: UnorderedHashMap<ast::PatternPtr, PatternId>,
145 pub resolver_data: Arc<ResolverData>,
146 pub body: Arc<FunctionBody>,
147}
148
149#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
150#[debug_db(dyn SemanticGroup + 'static)]
151pub struct FunctionBody {
152 pub arenas: Arenas,
153 pub body_expr: semantic::ExprId,
154}
155
156pub fn function_body_diagnostics(
160 db: &dyn SemanticGroup,
161 function_id: FunctionWithBodyId,
162) -> Diagnostics<SemanticDiagnostic> {
163 let body_data = match function_id {
164 FunctionWithBodyId::Free(id) => db.priv_free_function_body_data(id),
165 FunctionWithBodyId::Impl(id) => db.priv_impl_function_body_data(id),
166 FunctionWithBodyId::Trait(id) => {
167 db.priv_trait_function_body_data(id).and_then(|x| x.ok_or(DiagnosticAdded))
168 }
169 };
170 body_data.map(|data| data.diagnostics).unwrap_or_default()
171}
172
173pub fn function_body_expr(
175 db: &dyn SemanticGroup,
176 function_id: FunctionWithBodyId,
177) -> Maybe<semantic::ExprId> {
178 Ok(db.function_body(function_id)?.body_expr)
179}
180
181pub fn function_body(
183 db: &dyn SemanticGroup,
184 function_id: FunctionWithBodyId,
185) -> Maybe<Arc<FunctionBody>> {
186 Ok(match function_id {
187 FunctionWithBodyId::Free(id) => db.priv_free_function_body_data(id)?.body,
188 FunctionWithBodyId::Impl(id) => db.priv_impl_function_body_data(id)?.body,
189 FunctionWithBodyId::Trait(id) => {
190 db.priv_trait_function_body_data(id)?.ok_or(DiagnosticAdded)?.body
191 }
192 })
193}
194
195pub fn expr_semantic(
199 db: &dyn SemanticGroup,
200 function_id: FunctionWithBodyId,
201 id: semantic::ExprId,
202) -> semantic::Expr {
203 db.function_body(function_id).unwrap().arenas.exprs.get(id).unwrap().clone()
204}
205
206pub fn pattern_semantic(
208 db: &dyn SemanticGroup,
209 function_id: FunctionWithBodyId,
210 id: semantic::PatternId,
211) -> semantic::Pattern {
212 db.function_body(function_id).unwrap().arenas.patterns.get(id).unwrap().clone()
213}
214
215pub fn statement_semantic(
217 db: &dyn SemanticGroup,
218 function_id: FunctionWithBodyId,
219 id: semantic::StatementId,
220) -> semantic::Statement {
221 db.function_body(function_id).unwrap().arenas.statements.get(id).unwrap().clone()
222}
223
224pub trait SemanticExprLookup<'a>: Upcast<dyn SemanticGroup + 'a> {
225 fn lookup_expr_by_ptr(
226 &self,
227 function_id: FunctionWithBodyId,
228 ptr: ast::ExprPtr,
229 ) -> Maybe<ExprId> {
230 let body_data = match function_id {
231 FunctionWithBodyId::Free(id) => self.upcast().priv_free_function_body_data(id)?,
232 FunctionWithBodyId::Impl(id) => self.upcast().priv_impl_function_body_data(id)?,
233 FunctionWithBodyId::Trait(id) => {
234 self.upcast().priv_trait_function_body_data(id)?.ok_or(DiagnosticAdded)?
235 }
236 };
237 body_data.expr_lookup.get(&ptr).copied().to_maybe()
238 }
239 fn lookup_pattern_by_ptr(
240 &self,
241 function_id: FunctionWithBodyId,
242 ptr: ast::PatternPtr,
243 ) -> Maybe<PatternId> {
244 let body_data = match function_id {
245 FunctionWithBodyId::Free(id) => self.upcast().priv_free_function_body_data(id)?,
246 FunctionWithBodyId::Impl(id) => self.upcast().priv_impl_function_body_data(id)?,
247 FunctionWithBodyId::Trait(id) => {
248 self.upcast().priv_trait_function_body_data(id)?.ok_or(DiagnosticAdded)?
249 }
250 };
251 body_data.pattern_lookup.get(&ptr).copied().to_maybe()
252 }
253}
254impl<'a, T: Upcast<dyn SemanticGroup + 'a> + ?Sized> SemanticExprLookup<'a> for T {}
255
256pub fn get_inline_config(
258 db: &dyn SemanticGroup,
259 diagnostics: &mut SemanticDiagnostics,
260 attributes: &[Attribute],
261) -> Maybe<InlineConfiguration> {
262 let mut config = InlineConfiguration::None;
263 let mut seen_inline_attr = false;
264 for attr in attributes {
265 if attr.id != INLINE_ATTR {
266 continue;
267 }
268
269 match &attr.args[..] {
270 [
271 AttributeArg {
272 variant: AttributeArgVariant::Unnamed(ast::Expr::Path(path)), ..
273 },
274 ] if &path.node.get_text(db.upcast()) == "always" => {
275 config = InlineConfiguration::Always(attr.clone());
276 }
277 [
278 AttributeArg {
279 variant: AttributeArgVariant::Unnamed(ast::Expr::Path(path)), ..
280 },
281 ] if &path.node.get_text(db.upcast()) == "never" => {
282 config = InlineConfiguration::Never(attr.clone());
283 }
284 [] => {
285 config = InlineConfiguration::Should(attr.clone());
286 }
287 _ => {
288 diagnostics.report(
289 attr.args_stable_ptr.untyped(),
290 SemanticDiagnosticKind::UnsupportedInlineArguments,
291 );
292 }
293 }
294
295 if seen_inline_attr {
296 diagnostics.report(
297 attr.id_stable_ptr.untyped(),
298 SemanticDiagnosticKind::RedundantInlineAttribute,
299 );
300 config = InlineConfiguration::None;
302 }
303
304 seen_inline_attr = true;
305 }
306 Ok(config)
307}
308
309pub fn get_implicit_precedence<'a>(
315 diagnostics: &mut SemanticDiagnostics,
316 resolver: &mut Resolver<'_>,
317 attributes: &'a [Attribute],
318) -> (ImplicitPrecedence, Option<&'a Attribute>) {
319 let mut attributes = attributes.iter().rev().filter(|attr| attr.id == IMPLICIT_PRECEDENCE_ATTR);
320
321 let Some(attr) = attributes.next() else { return (ImplicitPrecedence::UNSPECIFIED, None) };
323
324 for attr in attributes {
326 diagnostics.report(
327 attr.id_stable_ptr.untyped(),
328 SemanticDiagnosticKind::RedundantImplicitPrecedenceAttribute,
329 );
330 }
331
332 let Ok(types) = attr
333 .args
334 .iter()
335 .map(|arg| match &arg.variant {
336 AttributeArgVariant::Unnamed(value) => {
337 let ast::Expr::Path(path) = value else {
338 return Err(diagnostics.report(
339 value,
340 SemanticDiagnosticKind::UnsupportedImplicitPrecedenceArguments,
341 ));
342 };
343
344 resolver.resolve_concrete_path(diagnostics, path, NotFoundItemType::Type).and_then(
345 |resolved_item: crate::resolve::ResolvedConcreteItem| {
346 try_extract_matches!(resolved_item, ResolvedConcreteItem::Type).ok_or_else(
347 || diagnostics.report(value, SemanticDiagnosticKind::UnknownType),
348 )
349 },
350 )
351 }
352
353 _ => Err(diagnostics
354 .report(&arg.arg, SemanticDiagnosticKind::UnsupportedImplicitPrecedenceArguments)),
355 })
356 .try_collect::<TypeId, Vec<_>, _>()
357 else {
358 return (ImplicitPrecedence::UNSPECIFIED, None);
359 };
360
361 let precedence = ImplicitPrecedence::from_iter(types);
362
363 (precedence, Some(attr))
364}