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