cairo_lang_semantic/items/
function_with_body.rs

1use 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
22// === Declaration ===
23
24// --- Selectors ---
25
26/// Query implementation of [crate::db::SemanticGroup::function_declaration_diagnostics].
27pub 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
45/// Query implementation of [crate::db::SemanticGroup::function_declaration_inline_config].
46pub 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
63/// Query implementation of [SemanticGroup::function_declaration_implicit_precedence].
64pub 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
81/// Query implementation of [crate::db::SemanticGroup::function_with_body_signature].
82pub 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
95/// Query implementation of
96/// [crate::db::SemanticGroup::function_with_body_generic_params].
97pub 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
118/// Query implementation of [crate::db::SemanticGroup::function_with_body_attributes].
119pub 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// === Body ===
138
139#[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
156// --- Selectors ---
157
158/// Query implementation of [crate::db::SemanticGroup::function_body_diagnostics].
159pub 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
173/// Query implementation of [crate::db::SemanticGroup::function_body_expr].
174pub 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
181/// Query implementation of [crate::db::SemanticGroup::function_body].
182pub 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
195// =========================================================
196
197/// Query implementation of [crate::db::SemanticGroup::expr_semantic].
198pub 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
206/// Query implementation of [crate::db::SemanticGroup::pattern_semantic].
207pub 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
215/// Query implementation of [crate::db::SemanticGroup::statement_semantic].
216pub 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
256/// Get the inline configuration of the given function by parsing its attributes.
257pub 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            // If we have multiple inline attributes revert to InlineConfiguration::None.
301            config = InlineConfiguration::None;
302        }
303
304        seen_inline_attr = true;
305    }
306    Ok(config)
307}
308
309/// Get [ImplicitPrecedence] of the given function by looking at its attributes.
310///
311/// Returns the generated implicit precedence and the attribute used to get it, if one exists.
312/// If there is no implicit precedence influencing attribute, then this function returns
313/// [ImplicitPrecedence::UNSPECIFIED].
314pub 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    // Pick the last attribute if any.
322    let Some(attr) = attributes.next() else { return (ImplicitPrecedence::UNSPECIFIED, None) };
323
324    // Report warnings for overridden attributes if any.
325    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}