cairo_lang_semantic/resolve/
mod.rs

1use std::iter::Peekable;
2use std::marker::PhantomData;
3use std::ops::{Deref, DerefMut};
4use std::sync::Arc;
5
6use cairo_lang_defs::ids::{
7    GenericKind, GenericParamId, GenericTypeId, ImplDefId, LanguageElementId, LookupItemId,
8    ModuleFileId, ModuleId, ModuleItemId, TopLevelLanguageElementId, TraitId, TraitItemId,
9    VariantId,
10};
11use cairo_lang_diagnostics::{Maybe, skip_diagnostic};
12use cairo_lang_filesystem::db::{CORELIB_CRATE_NAME, CrateSettings};
13use cairo_lang_filesystem::ids::{CrateId, CrateLongId};
14use cairo_lang_proc_macros::DebugWithDb;
15use cairo_lang_syntax as syntax;
16use cairo_lang_syntax::node::ast::TerminalIdentifier;
17use cairo_lang_syntax::node::helpers::PathSegmentEx;
18use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
19use cairo_lang_syntax::node::{Terminal, TypedSyntaxNode, ast};
20use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
21use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
22use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
23use cairo_lang_utils::{Intern, LookupIntern, extract_matches, require, try_extract_matches};
24pub use item::{ResolvedConcreteItem, ResolvedGenericItem};
25use itertools::Itertools;
26use smol_str::SmolStr;
27use syntax::node::TypedStablePtr;
28use syntax::node::db::SyntaxGroup;
29use syntax::node::helpers::QueryAttrs;
30
31use crate::corelib::{core_submodule, get_submodule};
32use crate::db::SemanticGroup;
33use crate::diagnostic::SemanticDiagnosticKind::{self, *};
34use crate::diagnostic::{NotFoundItemType, SemanticDiagnostics, SemanticDiagnosticsBuilder};
35use crate::expr::compute::{
36    ComputationContext, ContextFunction, Environment, compute_expr_semantic,
37    get_statement_item_by_name,
38};
39use crate::expr::inference::canonic::ResultNoErrEx;
40use crate::expr::inference::conform::InferenceConform;
41use crate::expr::inference::infers::InferenceEmbeddings;
42use crate::expr::inference::{Inference, InferenceData, InferenceId};
43use crate::items::constant::{ConstValue, ImplConstantId, resolve_const_expr_and_evaluate};
44use crate::items::enm::SemanticEnumEx;
45use crate::items::feature_kind::{
46    FeatureConfig, FeatureKind, HasFeatureKind, extract_feature_config,
47};
48use crate::items::functions::{GenericFunctionId, ImplGenericFunctionId};
49use crate::items::generics::generic_params_to_args;
50use crate::items::imp::{
51    ConcreteImplId, ConcreteImplLongId, DerefInfo, ImplImplId, ImplLongId, ImplLookupContext,
52};
53use crate::items::module::ModuleItemInfo;
54use crate::items::trt::{
55    ConcreteTraitConstantLongId, ConcreteTraitGenericFunctionLongId, ConcreteTraitId,
56    ConcreteTraitImplLongId, ConcreteTraitLongId, ConcreteTraitTypeId,
57};
58use crate::items::{TraitOrImplContext, visibility};
59use crate::keyword::{CRATE_KW, SELF_TYPE_KW, SUPER_KW};
60use crate::substitution::{GenericSubstitution, SemanticRewriter};
61use crate::types::{ConcreteEnumLongId, ImplTypeId, are_coupons_enabled, resolve_type};
62use crate::{
63    ConcreteFunction, ConcreteTypeId, ConcreteVariant, FunctionId, FunctionLongId,
64    GenericArgumentId, GenericParam, Member, Mutability, TypeId, TypeLongId,
65};
66
67#[cfg(test)]
68mod test;
69
70mod item;
71
72// Remove when this becomes an actual crate.
73const STARKNET_CRATE_NAME: &str = "starknet";
74
75/// Lookback maps for item resolving. Can be used to quickly check what is the semantic resolution
76/// of any path segment.
77#[derive(Clone, Default, Debug, PartialEq, Eq, DebugWithDb)]
78#[debug_db(dyn SemanticGroup + 'static)]
79pub struct ResolvedItems {
80    pub concrete: UnorderedHashMap<ast::TerminalIdentifierPtr, ResolvedConcreteItem>,
81    pub generic: UnorderedHashMap<ast::TerminalIdentifierPtr, ResolvedGenericItem>,
82}
83impl ResolvedItems {
84    // Relates a path segment to a ResolvedConcreteItem, and adds to a resolved_items map. This will
85    // be used in "Go to definition".
86    pub fn mark_concrete(
87        &mut self,
88        db: &dyn SemanticGroup,
89        segment: &syntax::node::ast::PathSegment,
90        resolved_item: ResolvedConcreteItem,
91    ) -> ResolvedConcreteItem {
92        let identifier = segment.identifier_ast(db.upcast());
93        if let Some(generic_item) = resolved_item.generic(db) {
94            // Mark the generic item as well, for language server resolved_items.
95            self.generic.insert(identifier.stable_ptr(), generic_item);
96        }
97        self.concrete.insert(identifier.stable_ptr(), resolved_item.clone());
98        resolved_item
99    }
100    // Relates a path segment to a ResolvedGenericItem, and adds to a resolved_items map. This will
101    // be used in "Go to definition".
102    pub fn mark_generic(
103        &mut self,
104        db: &dyn SemanticGroup,
105        segment: &syntax::node::ast::PathSegment,
106        resolved_item: ResolvedGenericItem,
107    ) -> ResolvedGenericItem {
108        let identifier = segment.identifier_ast(db.upcast());
109        self.generic.insert(identifier.stable_ptr(), resolved_item.clone());
110        resolved_item
111    }
112}
113
114/// The enriched members of a type, including direct members of structs, as well as members of
115/// targets of `Deref` and `DerefMut` of the type.
116#[derive(Debug, PartialEq, Eq, DebugWithDb, Clone)]
117#[debug_db(dyn SemanticGroup + 'static)]
118pub struct EnrichedMembers {
119    /// A map from member names to their semantic representation and the number of deref operations
120    /// needed to access them.
121    pub members: OrderedHashMap<SmolStr, (Member, usize)>,
122    /// The sequence of deref needed to access the members.
123    pub deref_chain: Arc<[DerefInfo]>,
124    // The number of derefs that were explored.
125    pub explored_derefs: usize,
126}
127impl EnrichedMembers {
128    /// Returns `EnrichedTypeMemberAccess` for a single member if exists.
129    pub fn get_member(&self, name: &str) -> Option<EnrichedTypeMemberAccess> {
130        let (member, n_derefs) = self.members.get(name)?;
131        Some(EnrichedTypeMemberAccess {
132            member: member.clone(),
133            deref_functions: self
134                .deref_chain
135                .iter()
136                .map(|deref_info| (deref_info.function_id, deref_info.self_mutability))
137                .take(*n_derefs)
138                .collect(),
139        })
140    }
141}
142
143/// The enriched member of a type, including the member itself and the deref functions needed to
144/// access it.
145pub struct EnrichedTypeMemberAccess {
146    /// The member itself.
147    pub member: Member,
148    /// The sequence of deref functions needed to access the member.
149    pub deref_functions: Vec<(FunctionId, Mutability)>,
150}
151
152#[derive(Debug, PartialEq, Eq, DebugWithDb)]
153#[debug_db(dyn SemanticGroup + 'static)]
154pub struct ResolverData {
155    /// Current module in which to resolve the path.
156    pub module_file_id: ModuleFileId,
157    /// Named generic parameters accessible to the resolver.
158    generic_param_by_name: OrderedHashMap<SmolStr, GenericParamId>,
159    /// All generic parameters accessible to the resolver.
160    pub generic_params: Vec<GenericParamId>,
161    /// The enriched members per type and its mutability in the resolver context.
162    pub type_enriched_members: OrderedHashMap<(TypeId, bool), EnrichedMembers>,
163    /// Lookback map for resolved identifiers in path. Used in "Go to definition".
164    pub resolved_items: ResolvedItems,
165    /// Inference data for the resolver.
166    pub inference_data: InferenceData,
167    /// The trait/impl context the resolver is currently in. Used to resolve "Self::" paths.
168    pub trait_or_impl_ctx: TraitOrImplContext,
169    /// The configuration of allowed features.
170    pub feature_config: FeatureConfig,
171    /// The set of used items in the current context.
172    pub used_items: OrderedHashSet<LookupItemId>,
173}
174impl ResolverData {
175    pub fn new(module_file_id: ModuleFileId, inference_id: InferenceId) -> Self {
176        Self {
177            module_file_id,
178            generic_param_by_name: Default::default(),
179            generic_params: Default::default(),
180            type_enriched_members: Default::default(),
181            resolved_items: Default::default(),
182            inference_data: InferenceData::new(inference_id),
183            trait_or_impl_ctx: TraitOrImplContext::None,
184            feature_config: Default::default(),
185            used_items: Default::default(),
186        }
187    }
188    pub fn clone_with_inference_id(
189        &self,
190        db: &dyn SemanticGroup,
191        inference_id: InferenceId,
192    ) -> Self {
193        Self {
194            module_file_id: self.module_file_id,
195            generic_param_by_name: self.generic_param_by_name.clone(),
196            generic_params: self.generic_params.clone(),
197            type_enriched_members: self.type_enriched_members.clone(),
198            resolved_items: self.resolved_items.clone(),
199            inference_data: self.inference_data.clone_with_inference_id(db, inference_id),
200            trait_or_impl_ctx: self.trait_or_impl_ctx,
201            feature_config: self.feature_config.clone(),
202            used_items: self.used_items.clone(),
203        }
204    }
205}
206
207/// Resolves paths semantically.
208pub struct Resolver<'db> {
209    db: &'db dyn SemanticGroup,
210    pub data: ResolverData,
211    pub owning_crate_id: CrateId,
212    pub settings: CrateSettings,
213}
214impl Deref for Resolver<'_> {
215    type Target = ResolverData;
216
217    fn deref(&self) -> &Self::Target {
218        &self.data
219    }
220}
221impl DerefMut for Resolver<'_> {
222    fn deref_mut(&mut self) -> &mut Self::Target {
223        &mut self.data
224    }
225}
226impl Resolver<'_> {
227    /// Extracts the allowed node from the syntax, and sets it as the allowed features of the
228    /// resolver.
229    pub fn set_feature_config(
230        &mut self,
231        element_id: &impl LanguageElementId,
232        syntax: &impl QueryAttrs,
233        diagnostics: &mut SemanticDiagnostics,
234    ) {
235        self.feature_config =
236            extract_feature_config(self.db.upcast(), element_id, syntax, diagnostics);
237    }
238}
239
240/// The result of resolveing an item using `use *` imports.
241enum UseStarResult {
242    /// A unique path was found, considering only the `use *` imports.
243    UniquePathFound(ModuleItemInfo),
244    /// The path is ambiguous, considering only the `use *` imports.
245    AmbiguousPath(Vec<ModuleItemId>),
246    /// The path was not found, considering only the `use *` imports.
247    PathNotFound,
248    /// Item is not visible in the current module, considering only the `use *` imports.
249    ItemNotVisible(ModuleItemId, Vec<ModuleId>),
250}
251
252/// A trait for things that can be interpreted as a path of segments.
253pub trait AsSegments {
254    fn to_segments(self, db: &dyn SyntaxGroup) -> Vec<ast::PathSegment>;
255}
256impl AsSegments for &ast::ExprPath {
257    fn to_segments(self, db: &dyn SyntaxGroup) -> Vec<ast::PathSegment> {
258        self.elements(db)
259    }
260}
261impl AsSegments for Vec<ast::PathSegment> {
262    fn to_segments(self, _: &dyn SyntaxGroup) -> Vec<ast::PathSegment> {
263        self
264    }
265}
266
267impl<'db> Resolver<'db> {
268    pub fn new(
269        db: &'db dyn SemanticGroup,
270        module_file_id: ModuleFileId,
271        inference_id: InferenceId,
272    ) -> Self {
273        Self::with_data(db, ResolverData::new(module_file_id, inference_id))
274    }
275
276    pub fn with_data(db: &'db dyn SemanticGroup, data: ResolverData) -> Self {
277        let owning_crate_id = data.module_file_id.0.owning_crate(db.upcast());
278        let settings = db.crate_config(owning_crate_id).map(|c| c.settings).unwrap_or_default();
279        Self { owning_crate_id, settings, db, data }
280    }
281
282    pub fn inference(&mut self) -> Inference<'_> {
283        self.data.inference_data.inference(self.db)
284    }
285
286    /// Adds a generic param to an existing resolver.
287    /// This is required since a resolver needs to exist before resolving the generic params,
288    /// and thus, they are added to the Resolver only after they are resolved.
289    pub fn add_generic_param(&mut self, generic_param_id: GenericParamId) {
290        self.generic_params.push(generic_param_id);
291        let db = self.db.upcast();
292        if let Some(name) = generic_param_id.name(db) {
293            self.generic_param_by_name.insert(name, generic_param_id);
294        }
295    }
296
297    /// Resolves an item, given a path.
298    /// Guaranteed to result in at most one diagnostic.
299    fn resolve_path_inner<ResolvedItem: Clone>(
300        &mut self,
301        diagnostics: &mut SemanticDiagnostics,
302        path: impl AsSegments,
303        item_type: NotFoundItemType,
304        statement_env: Option<&mut Environment>,
305        mut callbacks: ResolvePathInnerCallbacks<
306            ResolvedItem,
307            impl FnMut(
308                &mut Resolver<'_>,
309                &mut SemanticDiagnostics,
310                &mut Peekable<std::slice::Iter<'_, ast::PathSegment>>,
311                Option<&mut Environment>,
312            ) -> Maybe<ResolvedItem>,
313            impl FnMut(
314                &mut Resolver<'_>,
315                &mut SemanticDiagnostics,
316                &ResolvedItem,
317                &ast::PathSegment,
318                NotFoundItemType,
319            ) -> Maybe<ResolvedItem>,
320            impl FnMut(&mut SemanticDiagnostics, &ast::PathSegment) -> Maybe<()>,
321            impl FnMut(
322                &mut ResolvedItems,
323                &dyn SemanticGroup,
324                &syntax::node::ast::PathSegment,
325                ResolvedItem,
326            ),
327        >,
328    ) -> Maybe<ResolvedItem> {
329        let db = self.db;
330        let syntax_db = db.upcast();
331        let elements_vec = path.to_segments(syntax_db);
332        let mut segments = elements_vec.iter().peekable();
333
334        // Find where the first segment lies in.
335        let mut item: ResolvedItem = (callbacks.resolve_path_first_segment)(
336            self,
337            diagnostics,
338            &mut segments,
339            statement_env,
340        )?;
341
342        // Follow modules.
343        while let Some(segment) = segments.next() {
344            (callbacks.validate_segment)(diagnostics, segment)?;
345
346            // If this is not the last segment, set the expected type to
347            // [NotFoundItemType::Identifier].
348            let cur_item_type =
349                if segments.peek().is_some() { NotFoundItemType::Identifier } else { item_type };
350            // `?` is ok here as the rest of the segments have no meaning if the current one can't
351            // be resolved.
352            item = (callbacks.resolve_path_next_segment)(
353                self,
354                diagnostics,
355                &item,
356                segment,
357                cur_item_type,
358            )?;
359            (callbacks.mark)(&mut self.resolved_items, db, segment, item.clone());
360        }
361        Ok(item)
362    }
363
364    /// Resolves a concrete item, given a path.
365    /// Guaranteed to result in at most one diagnostic.
366    /// Item not inside a statement.
367    pub fn resolve_concrete_path(
368        &mut self,
369        diagnostics: &mut SemanticDiagnostics,
370        path: impl AsSegments,
371        item_type: NotFoundItemType,
372    ) -> Maybe<ResolvedConcreteItem> {
373        self.resolve_concrete_path_ex(diagnostics, path, item_type, None)
374    }
375
376    /// Resolves a concrete item, given a path.
377    /// Guaranteed to result in at most one diagnostic.
378    pub fn resolve_concrete_path_ex(
379        &mut self,
380        diagnostics: &mut SemanticDiagnostics,
381        path: impl AsSegments,
382        item_type: NotFoundItemType,
383        statement_env: Option<&mut Environment>,
384    ) -> Maybe<ResolvedConcreteItem> {
385        self.resolve_path_inner::<ResolvedConcreteItem>(
386            diagnostics,
387            path,
388            item_type,
389            statement_env,
390            ResolvePathInnerCallbacks {
391                resolved_item_type: PhantomData,
392                resolve_path_first_segment: |resolver, diagnostics, segments, statement_env| {
393                    resolver.resolve_concrete_path_first_segment(
394                        diagnostics,
395                        segments,
396                        statement_env,
397                    )
398                },
399                resolve_path_next_segment: |resolver, diagnostics, item, segment, item_type| {
400                    resolver.resolve_path_next_segment_concrete(
401                        diagnostics,
402                        item,
403                        segment,
404                        item_type,
405                    )
406                },
407                validate_segment: |_, _| Ok(()),
408                mark: |resolved_items, db, segment, item| {
409                    resolved_items.mark_concrete(db, segment, item);
410                },
411            },
412        )
413    }
414
415    /// Specializes the item found in the current segment, and checks its usability.
416    fn specialize_generic_inner_item(
417        &mut self,
418        diagnostics: &mut SemanticDiagnostics,
419        module_id: ModuleId,
420        identifier: &TerminalIdentifier,
421        inner_item_info: ModuleItemInfo,
422        segment: &ast::PathSegment,
423    ) -> Maybe<ResolvedConcreteItem> {
424        let generic_args_syntax = segment.generic_args(self.db.upcast());
425        let segment_stable_ptr = segment.stable_ptr().untyped();
426        self.validate_module_item_usability(diagnostics, module_id, identifier, &inner_item_info);
427        self.data.used_items.insert(LookupItemId::ModuleItem(inner_item_info.item_id));
428        let inner_generic_item =
429            ResolvedGenericItem::from_module_item(self.db, inner_item_info.item_id)?;
430        let mut specialized_item = self.specialize_generic_module_item(
431            diagnostics,
432            identifier,
433            inner_generic_item.clone(),
434            generic_args_syntax.clone(),
435        )?;
436        self.data.resolved_items.generic.insert(identifier.stable_ptr(), inner_generic_item);
437        self.handle_same_impl_trait(
438            diagnostics,
439            &mut specialized_item,
440            &generic_args_syntax.unwrap_or_default(),
441            segment_stable_ptr,
442        );
443        Ok(specialized_item)
444    }
445
446    /// Resolves the first segment of a concrete path.
447    fn resolve_concrete_path_first_segment(
448        &mut self,
449        diagnostics: &mut SemanticDiagnostics,
450        segments: &mut Peekable<std::slice::Iter<'_, ast::PathSegment>>,
451        statement_env: Option<&mut Environment>,
452    ) -> Maybe<ResolvedConcreteItem> {
453        if let Some(base_module) = self.try_handle_super_segments(diagnostics, segments) {
454            return Ok(ResolvedConcreteItem::Module(base_module?));
455        }
456
457        let db = self.db;
458        let syntax_db = db.upcast();
459        Ok(match segments.peek().unwrap() {
460            syntax::node::ast::PathSegment::WithGenericArgs(generic_segment) => {
461                let identifier = generic_segment.ident(syntax_db);
462                // Identifier with generic args cannot be a local item.
463                match self.determine_base(&identifier, statement_env) {
464                    ResolvedBase::Module(module_id) => ResolvedConcreteItem::Module(module_id),
465                    ResolvedBase::Crate(_) => {
466                        // Crates do not have generics.
467                        return Err(diagnostics.report(
468                            &generic_segment.generic_args(syntax_db),
469                            UnexpectedGenericArgs,
470                        ));
471                    }
472                    ResolvedBase::StatementEnvironment(generic_item) => {
473                        let segment = segments.next().unwrap();
474                        self.specialize_generic_statement_arg(
475                            segment,
476                            diagnostics,
477                            &identifier,
478                            generic_item,
479                            segment.generic_args(syntax_db),
480                        )
481                    }
482                    ResolvedBase::FoundThroughGlobalUse {
483                        item_info: inner_module_item,
484                        containing_module: module_id,
485                    } => {
486                        let segment = segments.next().unwrap();
487                        self.specialize_generic_inner_item(
488                            diagnostics,
489                            module_id,
490                            &identifier,
491                            inner_module_item,
492                            segment,
493                        )?
494                    }
495                    ResolvedBase::Ambiguous(module_items) => {
496                        return Err(diagnostics.report(&identifier, AmbiguousPath(module_items)));
497                    }
498                    ResolvedBase::ItemNotVisible(module_item_id, containing_modules) => {
499                        return Err(diagnostics.report(
500                            &identifier,
501                            ItemNotVisible(module_item_id, containing_modules),
502                        ));
503                    }
504                }
505            }
506            syntax::node::ast::PathSegment::Simple(simple_segment) => {
507                let identifier = simple_segment.ident(syntax_db);
508
509                if let Some(resolved_item) =
510                    resolve_self_segment(db, diagnostics, &identifier, &self.data.trait_or_impl_ctx)
511                {
512                    // The first segment is `Self`. Consume it and return.
513                    segments.next().unwrap();
514                    return resolved_item;
515                }
516
517                if let Some(local_item) = self.determine_base_item_in_local_scope(&identifier) {
518                    self.resolved_items.mark_concrete(db, segments.next().unwrap(), local_item)
519                } else {
520                    match self.determine_base(&identifier, statement_env) {
521                        // This item lies inside a module.
522                        ResolvedBase::Module(module_id) => ResolvedConcreteItem::Module(module_id),
523                        ResolvedBase::Crate(crate_id) => self.resolved_items.mark_concrete(
524                            db,
525                            segments.next().unwrap(),
526                            ResolvedConcreteItem::Module(ModuleId::CrateRoot(crate_id)),
527                        ),
528                        ResolvedBase::StatementEnvironment(generic_item) => {
529                            let segment = segments.next().unwrap();
530                            self.specialize_generic_statement_arg(
531                                segment,
532                                diagnostics,
533                                &identifier,
534                                generic_item,
535                                segment.generic_args(syntax_db),
536                            )
537                        }
538                        ResolvedBase::FoundThroughGlobalUse {
539                            item_info: inner_module_item,
540                            containing_module: module_id,
541                        } => {
542                            let segment = segments.next().unwrap();
543                            self.specialize_generic_inner_item(
544                                diagnostics,
545                                module_id,
546                                &identifier,
547                                inner_module_item,
548                                segment,
549                            )?
550                        }
551                        ResolvedBase::Ambiguous(module_items) => {
552                            return Err(
553                                diagnostics.report(&identifier, AmbiguousPath(module_items))
554                            );
555                        }
556                        ResolvedBase::ItemNotVisible(module_item_id, containing_modules) => {
557                            return Err(diagnostics.report(
558                                &identifier,
559                                ItemNotVisible(module_item_id, containing_modules),
560                            ));
561                        }
562                    }
563                }
564            }
565            syntax::node::ast::PathSegment::Missing(_) => {
566                // A diagnostic for the missing segment should have been reported from the syntax
567                // phase.
568                return Err(skip_diagnostic());
569            }
570        })
571    }
572
573    /// Resolves a generic item, given a path.
574    /// Guaranteed to result in at most one diagnostic.
575    pub fn resolve_generic_path(
576        &mut self,
577        diagnostics: &mut SemanticDiagnostics,
578        path: impl AsSegments,
579        item_type: NotFoundItemType,
580        statement_env: Option<&mut Environment>,
581    ) -> Maybe<ResolvedGenericItem> {
582        self.resolve_generic_path_inner(diagnostics, path, item_type, false, statement_env)
583    }
584    /// Resolves a generic item, given a concrete item path, while ignoring the generic args.
585    /// Guaranteed to result in at most one diagnostic.
586    pub fn resolve_generic_path_with_args(
587        &mut self,
588        diagnostics: &mut SemanticDiagnostics,
589        path: impl AsSegments,
590        item_type: NotFoundItemType,
591        statement_env: Option<&mut Environment>,
592    ) -> Maybe<ResolvedGenericItem> {
593        self.resolve_generic_path_inner(diagnostics, path, item_type, true, statement_env)
594    }
595
596    /// Resolves a generic item, given a path.
597    /// Guaranteed to result in at most one diagnostic.
598    /// If `allow_generic_args` is true a path with generic args will be processed, but the generic
599    /// params will be ignored.
600    fn resolve_generic_path_inner(
601        &mut self,
602        diagnostics: &mut SemanticDiagnostics,
603        path: impl AsSegments,
604        item_type: NotFoundItemType,
605        allow_generic_args: bool,
606        statement_env: Option<&mut Environment>,
607    ) -> Maybe<ResolvedGenericItem> {
608        let validate_segment =
609            |diagnostics: &mut SemanticDiagnostics, segment: &ast::PathSegment| match segment {
610                ast::PathSegment::WithGenericArgs(generic_args) if !allow_generic_args => {
611                    Err(diagnostics.report(generic_args, UnexpectedGenericArgs))
612                }
613                _ => Ok(()),
614            };
615        self.resolve_path_inner::<ResolvedGenericItem>(
616            diagnostics,
617            path,
618            item_type,
619            statement_env,
620            ResolvePathInnerCallbacks {
621                resolved_item_type: PhantomData,
622                resolve_path_first_segment: |resolver, diagnostics, segments, statement_env| {
623                    resolver.resolve_generic_path_first_segment(
624                        diagnostics,
625                        segments,
626                        allow_generic_args,
627                        statement_env,
628                    )
629                },
630                resolve_path_next_segment: |resolver, diagnostics, item, segment, item_type| {
631                    let identifier = segment.identifier_ast(self.db.upcast());
632                    resolver.resolve_path_next_segment_generic(
633                        diagnostics,
634                        item,
635                        &identifier,
636                        item_type,
637                    )
638                },
639                validate_segment,
640                mark: |resolved_items, db, segment, item| {
641                    resolved_items.mark_generic(db, segment, item);
642                },
643            },
644        )
645    }
646
647    /// Resolves the first segment of a generic path.
648    /// If `allow_generic_args` is true the generic args will be ignored.
649    fn resolve_generic_path_first_segment(
650        &mut self,
651        diagnostics: &mut SemanticDiagnostics,
652        segments: &mut Peekable<std::slice::Iter<'_, ast::PathSegment>>,
653        allow_generic_args: bool,
654        statement_env: Option<&mut Environment>,
655    ) -> Maybe<ResolvedGenericItem> {
656        if let Some(base_module) = self.try_handle_super_segments(diagnostics, segments) {
657            return Ok(ResolvedGenericItem::Module(base_module?));
658        }
659        let db = self.db;
660        let syntax_db = db.upcast();
661        Ok(match segments.peek().unwrap() {
662            syntax::node::ast::PathSegment::WithGenericArgs(generic_segment) => {
663                if !allow_generic_args {
664                    return Err(diagnostics
665                        .report(&generic_segment.generic_args(syntax_db), UnexpectedGenericArgs));
666                }
667                let identifier = generic_segment.ident(syntax_db);
668                // Identifier with generic args cannot be a local item.
669                match self.determine_base(&identifier, statement_env) {
670                    ResolvedBase::Module(module_id) => ResolvedGenericItem::Module(module_id),
671                    ResolvedBase::Crate(_) => {
672                        // Crates do not have generics.
673                        return Err(diagnostics.report(
674                            &generic_segment.generic_args(syntax_db),
675                            UnexpectedGenericArgs,
676                        ));
677                    }
678                    ResolvedBase::StatementEnvironment(generic_item) => generic_item,
679                    ResolvedBase::FoundThroughGlobalUse {
680                        item_info: inner_module_item, ..
681                    } => {
682                        segments.next();
683                        self.data
684                            .used_items
685                            .insert(LookupItemId::ModuleItem(inner_module_item.item_id));
686                        ResolvedGenericItem::from_module_item(self.db, inner_module_item.item_id)?
687                    }
688                    ResolvedBase::Ambiguous(module_items) => {
689                        return Err(diagnostics.report(&identifier, AmbiguousPath(module_items)));
690                    }
691                    ResolvedBase::ItemNotVisible(module_item_id, containing_modules) => {
692                        return Err(diagnostics.report(
693                            &identifier,
694                            ItemNotVisible(module_item_id, containing_modules),
695                        ));
696                    }
697                }
698            }
699            syntax::node::ast::PathSegment::Simple(simple_segment) => {
700                let identifier = simple_segment.ident(syntax_db);
701                match self.determine_base(&identifier, statement_env) {
702                    // This item lies inside a module.
703                    ResolvedBase::Module(module_id) => ResolvedGenericItem::Module(module_id),
704                    ResolvedBase::Crate(crate_id) => self.resolved_items.mark_generic(
705                        db,
706                        segments.next().unwrap(),
707                        ResolvedGenericItem::Module(ModuleId::CrateRoot(crate_id)),
708                    ),
709                    ResolvedBase::StatementEnvironment(generic_item) => {
710                        segments.next();
711                        generic_item
712                    }
713                    ResolvedBase::FoundThroughGlobalUse {
714                        item_info: inner_module_item, ..
715                    } => {
716                        segments.next();
717                        self.data
718                            .used_items
719                            .insert(LookupItemId::ModuleItem(inner_module_item.item_id));
720                        ResolvedGenericItem::from_module_item(self.db, inner_module_item.item_id)?
721                    }
722                    ResolvedBase::Ambiguous(module_items) => {
723                        return Err(diagnostics.report(&identifier, AmbiguousPath(module_items)));
724                    }
725                    ResolvedBase::ItemNotVisible(module_item_id, containing_modules) => {
726                        return Err(diagnostics.report(
727                            &identifier,
728                            ItemNotVisible(module_item_id, containing_modules),
729                        ));
730                    }
731                }
732            }
733            syntax::node::ast::PathSegment::Missing(_) => {
734                // A diagnostic for the missing segment should have been reported from the syntax
735                // phase.
736                return Err(skip_diagnostic());
737            }
738        })
739    }
740
741    /// Handles `super::` initial segments, by removing them, and returning the valid module if
742    /// exists. If there's none - returns None.
743    /// If there are, but that's an invalid path, adds to diagnostics and returns `Some(Err)`.
744    fn try_handle_super_segments(
745        &self,
746        diagnostics: &mut SemanticDiagnostics,
747        segments: &mut Peekable<std::slice::Iter<'_, ast::PathSegment>>,
748    ) -> Option<Maybe<ModuleId>> {
749        let syntax_db = self.db.upcast();
750        let mut module_id = self.module_file_id.0;
751        for segment in segments.peeking_take_while(|segment| match segment {
752            ast::PathSegment::WithGenericArgs(_) | ast::PathSegment::Missing(_) => false,
753            ast::PathSegment::Simple(simple) => simple.ident(syntax_db).text(syntax_db) == SUPER_KW,
754        }) {
755            module_id = match module_id {
756                ModuleId::CrateRoot(_) => {
757                    return Some(Err(diagnostics.report(segment, SuperUsedInRootModule)));
758                }
759                ModuleId::Submodule(submodule_id) => submodule_id.parent_module(self.db.upcast()),
760            };
761        }
762        (module_id != self.module_file_id.0).then_some(Ok(module_id))
763    }
764
765    /// Resolves the inner item of a module, given the current segment of the path.
766    fn resolve_module_inner_item(
767        &mut self,
768        module_id: &ModuleId,
769        ident: SmolStr,
770        diagnostics: &mut SemanticDiagnostics,
771        identifier: &TerminalIdentifier,
772        item_type: NotFoundItemType,
773    ) -> Maybe<ModuleItemInfo> {
774        match self.db.module_item_info_by_name(*module_id, ident)? {
775            Some(info) => Ok(info),
776            None => match self.resolve_path_using_use_star(*module_id, identifier) {
777                UseStarResult::UniquePathFound(item_info) => Ok(item_info),
778                UseStarResult::AmbiguousPath(module_items) => {
779                    Err(diagnostics.report(identifier, AmbiguousPath(module_items)))
780                }
781                UseStarResult::PathNotFound => {
782                    Err(diagnostics.report(identifier, PathNotFound(item_type)))
783                }
784                UseStarResult::ItemNotVisible(module_item_id, containing_modules) => {
785                    Err(diagnostics
786                        .report(identifier, ItemNotVisible(module_item_id, containing_modules)))
787                }
788            },
789        }
790    }
791
792    /// Given the current resolved item, resolves the next segment.
793    fn resolve_path_next_segment_concrete(
794        &mut self,
795        diagnostics: &mut SemanticDiagnostics,
796        containing_item: &ResolvedConcreteItem,
797        segment: &ast::PathSegment,
798        item_type: NotFoundItemType,
799    ) -> Maybe<ResolvedConcreteItem> {
800        let syntax_db = self.db.upcast();
801        let identifier = &segment.identifier_ast(syntax_db);
802        let generic_args_syntax = segment.generic_args(syntax_db);
803
804        let ident = identifier.text(syntax_db);
805
806        if identifier.text(syntax_db) == SELF_TYPE_KW {
807            return Err(diagnostics.report(identifier, SelfMustBeFirst));
808        }
809
810        match containing_item {
811            ResolvedConcreteItem::Module(module_id) => {
812                // Prefix `super` segments should be removed earlier. Middle `super` segments are
813                // not allowed.
814                if ident == SUPER_KW {
815                    return Err(diagnostics.report(identifier, InvalidPath));
816                }
817                let inner_item_info = self.resolve_module_inner_item(
818                    module_id,
819                    ident,
820                    diagnostics,
821                    identifier,
822                    item_type,
823                )?;
824
825                self.specialize_generic_inner_item(
826                    diagnostics,
827                    *module_id,
828                    identifier,
829                    inner_item_info,
830                    segment,
831                )
832            }
833            ResolvedConcreteItem::Type(ty) => {
834                if let TypeLongId::Concrete(ConcreteTypeId::Enum(concrete_enum_id)) =
835                    ty.lookup_intern(self.db)
836                {
837                    let enum_id = concrete_enum_id.enum_id(self.db);
838                    let variants = self
839                        .db
840                        .enum_variants(enum_id)
841                        .map_err(|_| diagnostics.report(identifier, UnknownEnum))?;
842                    let variant_id = variants.get(&ident).ok_or_else(|| {
843                        diagnostics
844                            .report(identifier, NoSuchVariant { enum_id, variant_name: ident })
845                    })?;
846                    let variant = self.db.variant_semantic(enum_id, *variant_id)?;
847                    let concrete_variant =
848                        self.db.concrete_enum_variant(concrete_enum_id, &variant)?;
849                    Ok(ResolvedConcreteItem::Variant(concrete_variant))
850                } else {
851                    Err(diagnostics.report(identifier, InvalidPath))
852                }
853            }
854            ResolvedConcreteItem::SelfTrait(concrete_trait_id) => {
855                let impl_id = ImplLongId::SelfImpl(*concrete_trait_id).intern(self.db);
856                let Some(trait_item_id) = self
857                    .db
858                    .trait_item_by_name(concrete_trait_id.trait_id(self.db), ident.clone())?
859                else {
860                    return Err(diagnostics.report(identifier, InvalidPath));
861                };
862                if let Ok(Some(trait_item_info)) =
863                    self.db.trait_item_info_by_name(concrete_trait_id.trait_id(self.db), ident)
864                {
865                    self.validate_feature_constraints(diagnostics, identifier, &trait_item_info);
866                }
867                self.data.used_items.insert(LookupItemId::TraitItem(trait_item_id));
868                Ok(match trait_item_id {
869                    TraitItemId::Function(trait_function_id) => {
870                        ResolvedConcreteItem::Function(self.specialize_function(
871                            diagnostics,
872                            identifier.stable_ptr().untyped(),
873                            GenericFunctionId::Impl(ImplGenericFunctionId {
874                                impl_id,
875                                function: trait_function_id,
876                            }),
877                            &generic_args_syntax.unwrap_or_default(),
878                        )?)
879                    }
880                    TraitItemId::Type(trait_type_id) => ResolvedConcreteItem::Type(
881                        TypeLongId::ImplType(ImplTypeId::new(impl_id, trait_type_id, self.db))
882                            .intern(self.db),
883                    ),
884                    TraitItemId::Constant(trait_constant_id) => ResolvedConcreteItem::Constant(
885                        ConstValue::ImplConstant(ImplConstantId::new(
886                            impl_id,
887                            trait_constant_id,
888                            self.db,
889                        ))
890                        .intern(self.db),
891                    ),
892                    TraitItemId::Impl(trait_impl_id) => ResolvedConcreteItem::Impl(
893                        ImplLongId::ImplImpl(ImplImplId::new(impl_id, trait_impl_id, self.db))
894                            .intern(self.db),
895                    ),
896                })
897            }
898            ResolvedConcreteItem::Trait(concrete_trait_id) => {
899                let Some(trait_item_id) = self
900                    .db
901                    .trait_item_by_name(concrete_trait_id.trait_id(self.db), ident.clone())?
902                else {
903                    return Err(diagnostics.report(identifier, InvalidPath));
904                };
905
906                if let Ok(Some(trait_item_info)) =
907                    self.db.trait_item_info_by_name(concrete_trait_id.trait_id(self.db), ident)
908                {
909                    self.validate_feature_constraints(diagnostics, identifier, &trait_item_info);
910                }
911                self.data.used_items.insert(LookupItemId::TraitItem(trait_item_id));
912
913                match trait_item_id {
914                    TraitItemId::Function(trait_function_id) => {
915                        let concrete_trait_function = ConcreteTraitGenericFunctionLongId::new(
916                            self.db,
917                            *concrete_trait_id,
918                            trait_function_id,
919                        )
920                        .intern(self.db);
921                        let identifier_stable_ptr = identifier.stable_ptr().untyped();
922                        let impl_lookup_context = self.impl_lookup_context();
923                        let generic_function =
924                            GenericFunctionId::Impl(self.inference().infer_trait_generic_function(
925                                concrete_trait_function,
926                                &impl_lookup_context,
927                                Some(identifier_stable_ptr),
928                            ));
929
930                        Ok(ResolvedConcreteItem::Function(self.specialize_function(
931                            diagnostics,
932                            identifier_stable_ptr,
933                            generic_function,
934                            &generic_args_syntax.unwrap_or_default(),
935                        )?))
936                    }
937                    TraitItemId::Type(trait_type_id) => {
938                        let concrete_trait_type =
939                            ConcreteTraitTypeId::new(self.db, *concrete_trait_id, trait_type_id);
940
941                        let impl_lookup_context = self.impl_lookup_context();
942                        let identifier_stable_ptr = identifier.stable_ptr().untyped();
943                        let ty = self.inference().infer_trait_type(
944                            concrete_trait_type,
945                            &impl_lookup_context,
946                            Some(identifier_stable_ptr),
947                        );
948                        Ok(ResolvedConcreteItem::Type(self.inference().rewrite(ty).no_err()))
949                    }
950                    TraitItemId::Constant(trait_constant_id) => {
951                        let concrete_trait_constant = ConcreteTraitConstantLongId::new(
952                            self.db,
953                            *concrete_trait_id,
954                            trait_constant_id,
955                        )
956                        .intern(self.db);
957
958                        let impl_lookup_context = self.impl_lookup_context();
959                        let identifier_stable_ptr = identifier.stable_ptr().untyped();
960                        let imp_constant_id = self.inference().infer_trait_constant(
961                            concrete_trait_constant,
962                            &impl_lookup_context,
963                            Some(identifier_stable_ptr),
964                        );
965                        // Make sure the inference is solved for successful impl lookup
966                        // Ignore the result of the `solve()` call - the error, if any, will be
967                        // reported later.
968                        self.inference().solve().ok();
969
970                        Ok(ResolvedConcreteItem::Constant(
971                            ConstValue::ImplConstant(imp_constant_id).intern(self.db),
972                        ))
973                    }
974                    TraitItemId::Impl(trait_impl_id) => {
975                        let concrete_trait_impl = ConcreteTraitImplLongId::new(
976                            self.db,
977                            *concrete_trait_id,
978                            trait_impl_id,
979                        )
980                        .intern(self.db);
981
982                        let impl_lookup_context = self.impl_lookup_context();
983                        let identifier_stable_ptr = identifier.stable_ptr().untyped();
984                        let impl_impl_id = self.inference().infer_trait_impl(
985                            concrete_trait_impl,
986                            &impl_lookup_context,
987                            Some(identifier_stable_ptr),
988                        );
989                        // Make sure the inference is solved for successful impl lookup
990                        // Ignore the result of the `solve()` call - the error, if any, will be
991                        // reported later.
992                        self.inference().solve().ok();
993
994                        Ok(ResolvedConcreteItem::Impl(
995                            ImplLongId::ImplImpl(impl_impl_id).intern(self.db),
996                        ))
997                    }
998                }
999            }
1000            ResolvedConcreteItem::Impl(impl_id) => {
1001                let concrete_trait_id = self.db.impl_concrete_trait(*impl_id)?;
1002                let trait_id = concrete_trait_id.trait_id(self.db);
1003                let Some(trait_item_id) = self.db.trait_item_by_name(trait_id, ident.clone())?
1004                else {
1005                    return Err(diagnostics.report(identifier, InvalidPath));
1006                };
1007                if let Ok(Some(trait_item_info)) = self
1008                    .db
1009                    .trait_item_info_by_name(concrete_trait_id.trait_id(self.db), ident.clone())
1010                {
1011                    self.validate_feature_constraints(diagnostics, identifier, &trait_item_info);
1012                }
1013                if let ImplLongId::Concrete(concrete_impl) = impl_id.lookup_intern(self.db) {
1014                    let impl_def_id: ImplDefId = concrete_impl.impl_def_id(self.db);
1015
1016                    if let Ok(Some(impl_item_info)) =
1017                        self.db.impl_item_info_by_name(impl_def_id, ident)
1018                    {
1019                        self.validate_feature_constraints(diagnostics, identifier, &impl_item_info);
1020                    }
1021                }
1022                self.data.used_items.insert(LookupItemId::TraitItem(trait_item_id));
1023
1024                match trait_item_id {
1025                    TraitItemId::Function(trait_function_id) => {
1026                        let generic_function_id = GenericFunctionId::Impl(ImplGenericFunctionId {
1027                            impl_id: *impl_id,
1028                            function: trait_function_id,
1029                        });
1030
1031                        Ok(ResolvedConcreteItem::Function(self.specialize_function(
1032                            diagnostics,
1033                            identifier.stable_ptr().untyped(),
1034                            generic_function_id,
1035                            &generic_args_syntax.unwrap_or_default(),
1036                        )?))
1037                    }
1038                    TraitItemId::Type(trait_type_id) => {
1039                        let impl_type_id = ImplTypeId::new(*impl_id, trait_type_id, self.db);
1040                        let ty = self
1041                            .inference()
1042                            .reduce_impl_ty(impl_type_id)
1043                            .unwrap_or_else(|_| TypeLongId::ImplType(impl_type_id).intern(self.db));
1044                        Ok(ResolvedConcreteItem::Type(ty))
1045                    }
1046                    TraitItemId::Constant(trait_constant_id) => {
1047                        let impl_constant_id =
1048                            ImplConstantId::new(*impl_id, trait_constant_id, self.db);
1049
1050                        let constant =
1051                            self.inference().reduce_impl_constant(impl_constant_id).unwrap_or_else(
1052                                |_| ConstValue::ImplConstant(impl_constant_id).intern(self.db),
1053                            );
1054
1055                        Ok(ResolvedConcreteItem::Constant(constant))
1056                    }
1057                    TraitItemId::Impl(trait_impl_id) => {
1058                        let impl_impl_id = ImplImplId::new(*impl_id, trait_impl_id, self.db);
1059                        let imp = self
1060                            .inference()
1061                            .reduce_impl_impl(impl_impl_id)
1062                            .unwrap_or_else(|_| ImplLongId::ImplImpl(impl_impl_id).intern(self.db));
1063
1064                        Ok(ResolvedConcreteItem::Impl(imp))
1065                    }
1066                }
1067            }
1068            ResolvedConcreteItem::Function(function_id) if ident == "Coupon" => {
1069                if !are_coupons_enabled(self.db, self.module_file_id) {
1070                    diagnostics.report(identifier, CouponsDisabled);
1071                }
1072                if matches!(
1073                    function_id.get_concrete(self.db).generic_function,
1074                    GenericFunctionId::Extern(_)
1075                ) {
1076                    return Err(diagnostics.report(identifier, CouponForExternFunctionNotAllowed));
1077                }
1078                Ok(ResolvedConcreteItem::Type(TypeLongId::Coupon(*function_id).intern(self.db)))
1079            }
1080            _ => Err(diagnostics.report(identifier, InvalidPath)),
1081        }
1082    }
1083
1084    /// Specializes a ResolvedGenericItem that came from a ModuleItem.
1085    fn specialize_generic_module_item(
1086        &mut self,
1087        diagnostics: &mut SemanticDiagnostics,
1088        identifier: &syntax::node::ast::TerminalIdentifier,
1089        generic_item: ResolvedGenericItem,
1090        generic_args_syntax: Option<Vec<ast::GenericArg>>,
1091    ) -> Maybe<ResolvedConcreteItem> {
1092        Ok(match generic_item {
1093            ResolvedGenericItem::GenericConstant(id) => {
1094                ResolvedConcreteItem::Constant(self.db.constant_const_value(id)?)
1095            }
1096            ResolvedGenericItem::Module(module_id) => {
1097                if generic_args_syntax.is_some() {
1098                    return Err(diagnostics.report(identifier, UnexpectedGenericArgs));
1099                }
1100                ResolvedConcreteItem::Module(module_id)
1101            }
1102            ResolvedGenericItem::GenericFunction(generic_function) => {
1103                ResolvedConcreteItem::Function(self.specialize_function(
1104                    diagnostics,
1105                    identifier.stable_ptr().untyped(),
1106                    generic_function,
1107                    &generic_args_syntax.unwrap_or_default(),
1108                )?)
1109            }
1110            ResolvedGenericItem::GenericType(generic_type) => {
1111                ResolvedConcreteItem::Type(self.specialize_type(
1112                    diagnostics,
1113                    identifier.stable_ptr().untyped(),
1114                    generic_type,
1115                    &generic_args_syntax.unwrap_or_default(),
1116                )?)
1117            }
1118            ResolvedGenericItem::GenericTypeAlias(module_type_alias_id) => {
1119                let ty = self.db.module_type_alias_resolved_type(module_type_alias_id)?;
1120                let generic_params =
1121                    self.db.module_type_alias_generic_params(module_type_alias_id)?;
1122                let generic_args = self.resolve_generic_args(
1123                    diagnostics,
1124                    GenericSubstitution::default(),
1125                    &generic_params,
1126                    &generic_args_syntax.unwrap_or_default(),
1127                    identifier.stable_ptr().untyped(),
1128                )?;
1129                ResolvedConcreteItem::Type(
1130                    GenericSubstitution::new(&generic_params, &generic_args)
1131                        .substitute(self.db, ty)?,
1132                )
1133            }
1134            ResolvedGenericItem::GenericImplAlias(impl_alias_id) => {
1135                let impl_id = self.db.impl_alias_resolved_impl(impl_alias_id)?;
1136                let generic_params = self.db.impl_alias_generic_params(impl_alias_id)?;
1137                let generic_args = self.resolve_generic_args(
1138                    diagnostics,
1139                    GenericSubstitution::default(),
1140                    &generic_params,
1141                    &generic_args_syntax.unwrap_or_default(),
1142                    identifier.stable_ptr().untyped(),
1143                )?;
1144                ResolvedConcreteItem::Impl(
1145                    GenericSubstitution::new(&generic_params, &generic_args)
1146                        .substitute(self.db, impl_id)?,
1147                )
1148            }
1149            ResolvedGenericItem::Trait(trait_id) => {
1150                ResolvedConcreteItem::Trait(self.specialize_trait(
1151                    diagnostics,
1152                    identifier.stable_ptr().untyped(),
1153                    trait_id,
1154                    &generic_args_syntax.unwrap_or_default(),
1155                )?)
1156            }
1157            ResolvedGenericItem::Impl(impl_def_id) => ResolvedConcreteItem::Impl(
1158                ImplLongId::Concrete(self.specialize_impl(
1159                    diagnostics,
1160                    identifier.stable_ptr().untyped(),
1161                    impl_def_id,
1162                    &generic_args_syntax.unwrap_or_default(),
1163                )?)
1164                .intern(self.db),
1165            ),
1166            ResolvedGenericItem::Variant(var) => {
1167                ResolvedConcreteItem::Variant(self.specialize_variant(
1168                    diagnostics,
1169                    identifier.stable_ptr().untyped(),
1170                    var.id,
1171                    &generic_args_syntax.unwrap_or_default(),
1172                )?)
1173            }
1174            ResolvedGenericItem::Variable(_) => panic!("Variable is not a module item."),
1175            ResolvedGenericItem::TraitItem(id) => {
1176                panic!("`{}` is not a module item.", id.full_path(self.db.upcast()))
1177            }
1178        })
1179    }
1180
1181    /// Resolves an item using the `use *` imports.
1182    fn resolve_path_using_use_star(
1183        &mut self,
1184        module_id: ModuleId,
1185        identifier: &ast::TerminalIdentifier,
1186    ) -> UseStarResult {
1187        let mut item_info = None;
1188        let mut module_items_found: OrderedHashSet<ModuleItemId> = OrderedHashSet::default();
1189        let imported_modules = self.db.priv_module_use_star_modules(module_id);
1190        for (star_module_id, item_module_id) in &imported_modules.accessible {
1191            if let Some(inner_item_info) =
1192                self.resolve_item_in_imported_module(*item_module_id, identifier)
1193            {
1194                if self.is_item_visible(*item_module_id, &inner_item_info, *star_module_id)
1195                    && self.is_item_feature_usable(&inner_item_info)
1196                {
1197                    item_info = Some(inner_item_info.clone());
1198                    module_items_found.insert(inner_item_info.item_id);
1199                }
1200            }
1201        }
1202        if module_items_found.len() > 1 {
1203            return UseStarResult::AmbiguousPath(module_items_found.iter().cloned().collect());
1204        }
1205        match item_info {
1206            Some(item_info) => UseStarResult::UniquePathFound(item_info),
1207            None => {
1208                let mut containing_modules = vec![];
1209                for star_module_id in &imported_modules.all {
1210                    if let Some(inner_item_info) =
1211                        self.resolve_item_in_imported_module(*star_module_id, identifier)
1212                    {
1213                        item_info = Some(inner_item_info.clone());
1214                        module_items_found.insert(inner_item_info.item_id);
1215                        containing_modules.push(*star_module_id);
1216                    }
1217                }
1218                if let Some(item_info) = item_info {
1219                    if module_items_found.len() > 1 {
1220                        UseStarResult::AmbiguousPath(module_items_found.iter().cloned().collect())
1221                    } else {
1222                        UseStarResult::ItemNotVisible(item_info.item_id, containing_modules)
1223                    }
1224                } else {
1225                    UseStarResult::PathNotFound
1226                }
1227            }
1228        }
1229    }
1230
1231    /// Resolves an item in an imported module.
1232    fn resolve_item_in_imported_module(
1233        &mut self,
1234        module_id: ModuleId,
1235        identifier: &ast::TerminalIdentifier,
1236    ) -> Option<ModuleItemInfo> {
1237        let inner_item_info =
1238            self.db.module_item_info_by_name(module_id, identifier.text(self.db.upcast()));
1239        if let Ok(Some(inner_item_info)) = inner_item_info {
1240            self.data.used_items.insert(LookupItemId::ModuleItem(inner_item_info.item_id));
1241            return Some(inner_item_info);
1242        }
1243        None
1244    }
1245
1246    /// Given the current resolved item, resolves the next segment.
1247    fn resolve_path_next_segment_generic(
1248        &mut self,
1249        diagnostics: &mut SemanticDiagnostics,
1250        containing_item: &ResolvedGenericItem,
1251        identifier: &ast::TerminalIdentifier,
1252        item_type: NotFoundItemType,
1253    ) -> Maybe<ResolvedGenericItem> {
1254        let syntax_db = self.db.upcast();
1255        let ident = identifier.text(syntax_db);
1256        match containing_item {
1257            ResolvedGenericItem::Module(module_id) => {
1258                let inner_item_info = self.resolve_module_inner_item(
1259                    module_id,
1260                    ident,
1261                    diagnostics,
1262                    identifier,
1263                    item_type,
1264                )?;
1265
1266                self.validate_module_item_usability(
1267                    diagnostics,
1268                    *module_id,
1269                    identifier,
1270                    &inner_item_info,
1271                );
1272                self.data.used_items.insert(LookupItemId::ModuleItem(inner_item_info.item_id));
1273                ResolvedGenericItem::from_module_item(self.db, inner_item_info.item_id)
1274            }
1275            ResolvedGenericItem::GenericType(GenericTypeId::Enum(enum_id)) => {
1276                let variants = self.db.enum_variants(*enum_id)?;
1277                let variant_id = variants.get(&ident).ok_or_else(|| {
1278                    diagnostics.report(
1279                        identifier,
1280                        NoSuchVariant { enum_id: *enum_id, variant_name: ident },
1281                    )
1282                })?;
1283                let variant = self.db.variant_semantic(*enum_id, *variant_id)?;
1284                Ok(ResolvedGenericItem::Variant(variant))
1285            }
1286            _ => Err(diagnostics.report(identifier, InvalidPath)),
1287        }
1288    }
1289
1290    /// Determines whether the first identifier of a path is a local item.
1291    fn determine_base_item_in_local_scope(
1292        &mut self,
1293        identifier: &ast::TerminalIdentifier,
1294    ) -> Option<ResolvedConcreteItem> {
1295        let syntax_db = self.db.upcast();
1296        let ident = identifier.text(syntax_db);
1297
1298        // If a generic param with this name is found, use it.
1299        if let Some(generic_param_id) = self.data.generic_param_by_name.get(&ident) {
1300            let item = match generic_param_id.kind(self.db.upcast()) {
1301                GenericKind::Type => ResolvedConcreteItem::Type(
1302                    TypeLongId::GenericParameter(*generic_param_id).intern(self.db),
1303                ),
1304                GenericKind::Const => ResolvedConcreteItem::Constant(
1305                    ConstValue::Generic(*generic_param_id).intern(self.db),
1306                ),
1307                GenericKind::Impl => ResolvedConcreteItem::Impl(
1308                    ImplLongId::GenericParameter(*generic_param_id).intern(self.db),
1309                ),
1310                GenericKind::NegImpl => return None,
1311            };
1312            return Some(item);
1313        }
1314        // TODO(spapini): Resolve local variables.
1315
1316        None
1317    }
1318
1319    /// Determines the base module or crate for the path resolving. Looks only in non-local scope
1320    /// (i.e. current module, or crates).
1321    fn determine_base(
1322        &mut self,
1323        identifier: &ast::TerminalIdentifier,
1324        statement_env: Option<&mut Environment>,
1325    ) -> ResolvedBase {
1326        let syntax_db = self.db.upcast();
1327        let ident = identifier.text(syntax_db);
1328        let module_id = self.module_file_id.0;
1329        if let Some(env) = statement_env {
1330            if let Some(inner_generic_arg) = get_statement_item_by_name(env, &ident) {
1331                return ResolvedBase::StatementEnvironment(inner_generic_arg);
1332            }
1333        }
1334
1335        // If an item with this name is found inside the current module, use the current module.
1336        if let Ok(Some(_)) = self.db.module_item_by_name(module_id, ident.clone()) {
1337            return ResolvedBase::Module(module_id);
1338        }
1339
1340        // If the first element is `crate`, use the crate's root module as the base module.
1341        if ident == CRATE_KW {
1342            return ResolvedBase::Crate(self.owning_crate_id);
1343        }
1344        // If the first segment is a name of a crate, use the crate's root module as the base
1345        // module.
1346        if let Some(dep) = self.settings.dependencies.get(ident.as_str()) {
1347            let dep_crate_id =
1348                CrateLongId::Real { name: ident, discriminator: dep.discriminator.clone() }
1349                    .intern(self.db);
1350            let configs = self.db.crate_configs();
1351            if !configs.contains_key(&dep_crate_id) {
1352                let get_long_id = |crate_id: CrateId| crate_id.lookup_intern(self.db);
1353                panic!(
1354                    "Invalid crate dependency: {:?}\nconfigured crates: {:#?}",
1355                    get_long_id(dep_crate_id),
1356                    configs.keys().cloned().map(get_long_id).collect_vec()
1357                );
1358            }
1359
1360            return ResolvedBase::Crate(dep_crate_id);
1361        }
1362        // If the first segment is `core` - and it was not overridden by a dependency - using it.
1363        if ident == CORELIB_CRATE_NAME {
1364            return ResolvedBase::Crate(CrateId::core(self.db));
1365        }
1366        // TODO(orizi): Remove when `starknet` becomes a proper crate.
1367        if ident == STARKNET_CRATE_NAME {
1368            // Making sure we don't look for it in `*` modules, to prevent cycles.
1369            return ResolvedBase::Module(self.prelude_submodule());
1370        }
1371        // If an item with this name is found in one of the 'use *' imports, use the module that
1372        match self.resolve_path_using_use_star(module_id, identifier) {
1373            UseStarResult::UniquePathFound(inner_module_item) => {
1374                return ResolvedBase::FoundThroughGlobalUse {
1375                    item_info: inner_module_item,
1376                    containing_module: module_id,
1377                };
1378            }
1379            UseStarResult::AmbiguousPath(module_items) => {
1380                return ResolvedBase::Ambiguous(module_items);
1381            }
1382            UseStarResult::PathNotFound => {}
1383            UseStarResult::ItemNotVisible(module_item_id, containing_modules) => {
1384                let prelude = self.prelude_submodule();
1385                if let Ok(Some(_)) = self.db.module_item_by_name(prelude, ident) {
1386                    return ResolvedBase::Module(prelude);
1387                }
1388                return ResolvedBase::ItemNotVisible(module_item_id, containing_modules);
1389            }
1390        }
1391        ResolvedBase::Module(self.prelude_submodule())
1392    }
1393
1394    /// Returns the crate's `prelude` submodule.
1395    pub fn prelude_submodule(&self) -> ModuleId {
1396        let prelude_submodule_name = self.settings.edition.prelude_submodule_name();
1397        let core_prelude_submodule = core_submodule(self.db, "prelude");
1398        get_submodule(self.db, core_prelude_submodule, prelude_submodule_name).unwrap_or_else(
1399            || {
1400                panic!(
1401                    "expected prelude submodule `{prelude_submodule_name}` not found in \
1402                     `core::prelude`."
1403                )
1404            },
1405        )
1406    }
1407
1408    /// Specializes a trait.
1409    fn specialize_trait(
1410        &mut self,
1411        diagnostics: &mut SemanticDiagnostics,
1412        stable_ptr: SyntaxStablePtrId,
1413        trait_id: TraitId,
1414        generic_args: &[ast::GenericArg],
1415    ) -> Maybe<ConcreteTraitId> {
1416        // TODO(lior): Should we report diagnostic if `trait_generic_params` failed?
1417        let generic_params = self
1418            .db
1419            .trait_generic_params(trait_id)
1420            .map_err(|_| diagnostics.report(stable_ptr, UnknownTrait))?;
1421        let generic_args = self.resolve_generic_args(
1422            diagnostics,
1423            GenericSubstitution::default(),
1424            &generic_params,
1425            generic_args,
1426            stable_ptr,
1427        )?;
1428
1429        Ok(ConcreteTraitLongId { trait_id, generic_args }.intern(self.db))
1430    }
1431
1432    /// Specializes an impl.
1433    fn specialize_impl(
1434        &mut self,
1435        diagnostics: &mut SemanticDiagnostics,
1436        stable_ptr: SyntaxStablePtrId,
1437        impl_def_id: ImplDefId,
1438        generic_args: &[ast::GenericArg],
1439    ) -> Maybe<ConcreteImplId> {
1440        // TODO(lior): Should we report diagnostic if `impl_def_generic_params` failed?
1441        let generic_params = self
1442            .db
1443            .impl_def_generic_params(impl_def_id)
1444            .map_err(|_| diagnostics.report(stable_ptr, UnknownImpl))?;
1445        let generic_args = self.resolve_generic_args(
1446            diagnostics,
1447            GenericSubstitution::default(),
1448            &generic_params,
1449            generic_args,
1450            stable_ptr,
1451        )?;
1452
1453        Ok(ConcreteImplLongId { impl_def_id, generic_args }.intern(self.db))
1454    }
1455
1456    /// Specializes a variant.
1457    fn specialize_variant(
1458        &mut self,
1459        diagnostics: &mut SemanticDiagnostics,
1460        stable_ptr: SyntaxStablePtrId,
1461        variant_id: VariantId,
1462        generic_args: &[ast::GenericArg],
1463    ) -> Maybe<ConcreteVariant> {
1464        let concrete_enum_id = ConcreteEnumLongId {
1465            enum_id: variant_id.enum_id(self.db.upcast()),
1466            generic_args: self.resolve_generic_args(
1467                diagnostics,
1468                GenericSubstitution::default(),
1469                &self.db.enum_generic_params(variant_id.enum_id(self.db.upcast()))?,
1470                generic_args,
1471                stable_ptr,
1472            )?,
1473        }
1474        .intern(self.db);
1475        self.db.concrete_enum_variant(
1476            concrete_enum_id,
1477            &self.db.variant_semantic(variant_id.enum_id(self.db.upcast()), variant_id)?,
1478        )
1479    }
1480
1481    /// Specializes a generic function.
1482    pub fn specialize_function(
1483        &mut self,
1484        diagnostics: &mut SemanticDiagnostics,
1485        stable_ptr: SyntaxStablePtrId,
1486        generic_function: GenericFunctionId,
1487        generic_args: &[ast::GenericArg],
1488    ) -> Maybe<FunctionId> {
1489        // TODO(lior): Should we report diagnostic if `impl_def_generic_params` failed?
1490        let generic_params: Vec<_> = generic_function.generic_params(self.db)?;
1491        let generic_args = self.resolve_generic_args(
1492            diagnostics,
1493            GenericSubstitution::default(),
1494            &generic_params,
1495            generic_args,
1496            stable_ptr,
1497        )?;
1498
1499        Ok(FunctionLongId { function: ConcreteFunction { generic_function, generic_args } }
1500            .intern(self.db))
1501    }
1502
1503    /// Specializes a generic type.
1504    pub fn specialize_type(
1505        &mut self,
1506        diagnostics: &mut SemanticDiagnostics,
1507        stable_ptr: SyntaxStablePtrId,
1508        generic_type: GenericTypeId,
1509        generic_args: &[ast::GenericArg],
1510    ) -> Maybe<TypeId> {
1511        let generic_params = self
1512            .db
1513            .generic_type_generic_params(generic_type)
1514            .map_err(|_| diagnostics.report(stable_ptr, UnknownType))?;
1515        let generic_args = self.resolve_generic_args(
1516            diagnostics,
1517            GenericSubstitution::default(),
1518            &generic_params,
1519            generic_args,
1520            stable_ptr,
1521        )?;
1522
1523        Ok(TypeLongId::Concrete(ConcreteTypeId::new(self.db, generic_type, generic_args))
1524            .intern(self.db))
1525    }
1526
1527    pub fn impl_lookup_context(&self) -> ImplLookupContext {
1528        let mut lookup_context =
1529            ImplLookupContext::new(self.module_file_id.0, self.generic_params.clone());
1530
1531        if let TraitOrImplContext::Impl(impl_def_id) = &self.trait_or_impl_ctx {
1532            let Ok(generic_params) = self.db.impl_def_generic_params(*impl_def_id) else {
1533                return lookup_context;
1534            };
1535            let generic_args = generic_params_to_args(generic_params.as_slice(), self.db);
1536            let impl_id: ConcreteImplId =
1537                ConcreteImplLongId { impl_def_id: *impl_def_id, generic_args }.intern(self.db);
1538            lookup_context.insert_impl(ImplLongId::Concrete(impl_id).intern(self.db));
1539        }
1540        lookup_context
1541    }
1542
1543    /// Resolves generic arguments.
1544    /// For each generic argument, if the syntax is provided, it will be resolved by the inference.
1545    /// Otherwise, resolved by type.
1546    pub fn resolve_generic_args(
1547        &mut self,
1548        diagnostics: &mut SemanticDiagnostics,
1549        mut substitution: GenericSubstitution,
1550        generic_params: &[GenericParam],
1551        generic_args_syntax: &[ast::GenericArg],
1552        stable_ptr: SyntaxStablePtrId,
1553    ) -> Maybe<Vec<GenericArgumentId>> {
1554        let mut resolved_args = vec![];
1555        let arg_syntax_per_param =
1556            self.get_arg_syntax_per_param(diagnostics, generic_params, generic_args_syntax)?;
1557
1558        for generic_param in generic_params.iter() {
1559            let generic_param = substitution.substitute(self.db, generic_param.clone())?;
1560            let generic_arg = self.resolve_generic_arg(
1561                &generic_param,
1562                arg_syntax_per_param
1563                    .get(&generic_param.id())
1564                    .and_then(|arg_syntax| {
1565                        if let ast::GenericArgValue::Expr(expr) = arg_syntax {
1566                            Some(expr.expr(self.db.upcast()))
1567                        } else {
1568                            None
1569                        }
1570                    })
1571                    .as_ref(),
1572                stable_ptr,
1573                diagnostics,
1574            )?;
1575            resolved_args.push(generic_arg);
1576            substitution.insert(generic_param.id(), generic_arg);
1577        }
1578
1579        Ok(resolved_args)
1580    }
1581
1582    /// Returns a map of generic param id -> its assigned arg syntax.
1583    fn get_arg_syntax_per_param(
1584        &self,
1585        diagnostics: &mut SemanticDiagnostics,
1586        generic_params: &[GenericParam],
1587        generic_args_syntax: &[ast::GenericArg],
1588    ) -> Maybe<UnorderedHashMap<GenericParamId, ast::GenericArgValue>> {
1589        let syntax_db = self.db.upcast();
1590        let mut arg_syntax_per_param =
1591            UnorderedHashMap::<GenericParamId, ast::GenericArgValue>::default();
1592        let mut last_named_arg_index = None;
1593        let generic_param_by_name = generic_params
1594            .iter()
1595            .enumerate()
1596            .filter_map(|(i, param)| Some((param.id().name(self.db.upcast())?, (i, param.id()))))
1597            .collect::<UnorderedHashMap<_, _>>();
1598        for (idx, generic_arg_syntax) in generic_args_syntax.iter().enumerate() {
1599            match generic_arg_syntax {
1600                ast::GenericArg::Named(arg_syntax) => {
1601                    let name = arg_syntax.name(syntax_db).text(syntax_db);
1602                    let Some((index, generic_param_id)) = generic_param_by_name.get(&name) else {
1603                        return Err(diagnostics.report(arg_syntax, UnknownGenericParam(name)));
1604                    };
1605                    if let Some(prev_index) = last_named_arg_index {
1606                        if prev_index > index {
1607                            return Err(diagnostics.report(arg_syntax, GenericArgOutOfOrder(name)));
1608                        }
1609                    }
1610                    last_named_arg_index = Some(index);
1611                    if arg_syntax_per_param
1612                        .insert(*generic_param_id, arg_syntax.value(syntax_db))
1613                        .is_some()
1614                    {
1615                        return Err(diagnostics.report(arg_syntax, GenericArgDuplicate(name)));
1616                    }
1617                }
1618                ast::GenericArg::Unnamed(arg_syntax) => {
1619                    if last_named_arg_index.is_some() {
1620                        return Err(diagnostics.report(arg_syntax, PositionalGenericAfterNamed));
1621                    }
1622                    let generic_param = generic_params.get(idx).ok_or_else(|| {
1623                        diagnostics.report(
1624                            arg_syntax,
1625                            TooManyGenericArguments {
1626                                expected: generic_params.len(),
1627                                actual: generic_args_syntax.len(),
1628                            },
1629                        )
1630                    })?;
1631                    assert_eq!(
1632                        arg_syntax_per_param
1633                            .insert(generic_param.id(), arg_syntax.value(syntax_db)),
1634                        None,
1635                        "Unexpected duplication in ordered params."
1636                    );
1637                }
1638            }
1639        }
1640        Ok(arg_syntax_per_param)
1641    }
1642
1643    /// Resolves a generic argument.
1644    /// If no syntax Expr is provided, inference will be used.
1645    /// If a syntax Expr is provided, it will be resolved by type.
1646    fn resolve_generic_arg(
1647        &mut self,
1648        generic_param: &GenericParam,
1649        generic_arg_syntax_opt: Option<&ast::Expr>,
1650        stable_ptr: SyntaxStablePtrId,
1651        diagnostics: &mut SemanticDiagnostics,
1652    ) -> Result<GenericArgumentId, cairo_lang_diagnostics::DiagnosticAdded> {
1653        let Some(generic_arg_syntax) = generic_arg_syntax_opt else {
1654            let lookup_context = self.impl_lookup_context();
1655            let inference = &mut self.data.inference_data.inference(self.db);
1656            return inference
1657                .infer_generic_arg(generic_param, lookup_context, Some(stable_ptr))
1658                .map_err(|err_set| {
1659                    inference.report_on_pending_error(err_set, diagnostics, stable_ptr)
1660                });
1661        };
1662        Ok(match generic_param {
1663            GenericParam::Type(_) => {
1664                let ty = resolve_type(self.db, diagnostics, self, generic_arg_syntax);
1665                GenericArgumentId::Type(ty)
1666            }
1667            GenericParam::Const(const_param) => {
1668                // TODO(spapini): Currently no bound checks are performed. Move literal validation
1669                // to inference finalization and use inference here. This will become more relevant
1670                // when we support constant expressions, which need inference.
1671                let environment = Environment::empty();
1672
1673                // Using the resolver's data in the constant computation context, so impl vars are
1674                // added to the correct inference.
1675                let mut resolver_data =
1676                    ResolverData::new(self.module_file_id, self.inference_data.inference_id);
1677                std::mem::swap(&mut self.data, &mut resolver_data);
1678
1679                let mut ctx = ComputationContext::new(
1680                    self.db,
1681                    diagnostics,
1682                    Resolver::with_data(self.db, resolver_data),
1683                    None,
1684                    environment,
1685                    ContextFunction::Global,
1686                );
1687                let value = compute_expr_semantic(&mut ctx, generic_arg_syntax);
1688
1689                let const_value = resolve_const_expr_and_evaluate(
1690                    self.db,
1691                    &mut ctx,
1692                    &value,
1693                    generic_arg_syntax.stable_ptr().untyped(),
1694                    const_param.ty,
1695                    false,
1696                );
1697
1698                // Update `self` data with const_eval_resolver's data.
1699                std::mem::swap(&mut ctx.resolver.data, &mut self.data);
1700
1701                GenericArgumentId::Constant(const_value.intern(self.db))
1702            }
1703
1704            GenericParam::Impl(param) => {
1705                let expr_path = try_extract_matches!(generic_arg_syntax, ast::Expr::Path)
1706                    .ok_or_else(|| diagnostics.report(generic_arg_syntax, UnknownImpl))?;
1707                let resolved_impl = match self.resolve_concrete_path(
1708                    diagnostics,
1709                    expr_path,
1710                    NotFoundItemType::Impl,
1711                )? {
1712                    ResolvedConcreteItem::Impl(resolved_impl) => resolved_impl,
1713                    ResolvedConcreteItem::SelfTrait(concrete_trait_id) => {
1714                        ImplLongId::SelfImpl(concrete_trait_id).intern(self.db)
1715                    }
1716                    _ => return Err(diagnostics.report(generic_arg_syntax, UnknownImpl)),
1717                };
1718                let impl_def_concrete_trait = self.db.impl_concrete_trait(resolved_impl)?;
1719                let expected_concrete_trait = param.concrete_trait?;
1720                if let Err(err_set) = self
1721                    .inference()
1722                    .conform_traits(impl_def_concrete_trait, expected_concrete_trait)
1723                {
1724                    let diag_added = diagnostics.report(
1725                        generic_arg_syntax,
1726                        TraitMismatch {
1727                            expected_trt: expected_concrete_trait,
1728                            actual_trt: impl_def_concrete_trait,
1729                        },
1730                    );
1731                    self.inference().consume_reported_error(err_set, diag_added);
1732                } else {
1733                    for (trait_ty, ty1) in param.type_constraints.iter() {
1734                        let ty0 = TypeLongId::ImplType(ImplTypeId::new(
1735                            resolved_impl,
1736                            *trait_ty,
1737                            self.db,
1738                        ))
1739                        .intern(self.db);
1740                        let _ = self.inference().conform_ty(ty0, *ty1).map_err(|err_set| {
1741                            self.inference().report_on_pending_error(
1742                                err_set,
1743                                diagnostics,
1744                                stable_ptr,
1745                            )
1746                        });
1747                    }
1748                }
1749                GenericArgumentId::Impl(resolved_impl)
1750            }
1751            GenericParam::NegImpl(_) => {
1752                return Err(diagnostics.report(generic_arg_syntax, ArgPassedToNegativeImpl));
1753            }
1754        })
1755    }
1756    /// Should visibility checks not actually happen for lookups in this module.
1757    // TODO(orizi): Remove this check when performing a major Cairo update.
1758    pub fn ignore_visibility_checks(&self, module_id: ModuleId) -> bool {
1759        let module_crate = module_id.owning_crate(self.db.upcast());
1760        let module_edition =
1761            self.db.crate_config(module_crate).map(|c| c.settings.edition).unwrap_or_default();
1762        module_edition.ignore_visibility()
1763            || self.settings.edition.ignore_visibility() && module_crate == self.db.core_crate()
1764    }
1765
1766    /// Validates whether a given item is allowed based on its feature kind.
1767    /// This function checks if the item's feature kind is allowed in the current
1768    /// configuration. If the item uses an unstable, deprecated, or internal feature
1769    /// that is not permitted, a corresponding diagnostic error is reported.
1770    pub fn validate_feature_constraints<T: HasFeatureKind>(
1771        &self,
1772        diagnostics: &mut SemanticDiagnostics,
1773        identifier: &ast::TerminalIdentifier,
1774        item_info: &T,
1775    ) {
1776        match &item_info.feature_kind() {
1777            FeatureKind::Unstable { feature, note }
1778                if !self.data.feature_config.allowed_features.contains(feature) =>
1779            {
1780                diagnostics.report(
1781                    identifier,
1782                    UnstableFeature { feature_name: feature.clone(), note: note.clone() },
1783                );
1784            }
1785            FeatureKind::Deprecated { feature, note }
1786                if !self.data.feature_config.allow_deprecated
1787                    && !self.data.feature_config.allowed_features.contains(feature) =>
1788            {
1789                diagnostics.report(
1790                    identifier,
1791                    DeprecatedFeature { feature_name: feature.clone(), note: note.clone() },
1792                );
1793            }
1794            FeatureKind::Internal { feature, note }
1795                if !self.data.feature_config.allowed_features.contains(feature) =>
1796            {
1797                diagnostics.report(
1798                    identifier,
1799                    InternalFeature { feature_name: feature.clone(), note: note.clone() },
1800                );
1801            }
1802            _ => {}
1803        }
1804    }
1805
1806    /// Validates that an item is usable from the current module or adds a diagnostic.
1807    /// This includes visibility checks and feature checks.
1808    fn validate_module_item_usability(
1809        &self,
1810        diagnostics: &mut SemanticDiagnostics,
1811        containing_module_id: ModuleId,
1812        identifier: &ast::TerminalIdentifier,
1813        item_info: &ModuleItemInfo,
1814    ) {
1815        if !self.is_item_visible(containing_module_id, item_info, self.module_file_id.0) {
1816            diagnostics.report(identifier, ItemNotVisible(item_info.item_id, vec![]));
1817        }
1818
1819        self.validate_feature_constraints(diagnostics, identifier, item_info);
1820    }
1821
1822    /// Checks if an item is visible from the current module.
1823    fn is_item_visible(
1824        &self,
1825        containing_module_id: ModuleId,
1826        item_info: &ModuleItemInfo,
1827        user_module: ModuleId,
1828    ) -> bool {
1829        let db = self.db.upcast();
1830        self.ignore_visibility_checks(containing_module_id)
1831            || visibility::peek_visible_in(
1832                db,
1833                item_info.visibility,
1834                containing_module_id,
1835                user_module,
1836            )
1837    }
1838
1839    /// Checks if an item uses a feature that is not allowed.
1840    fn is_item_feature_usable(&self, item_info: &ModuleItemInfo) -> bool {
1841        match &item_info.feature_kind {
1842            FeatureKind::Unstable { feature, .. }
1843            | FeatureKind::Deprecated { feature, .. }
1844            | FeatureKind::Internal { feature, .. } => {
1845                self.data.feature_config.allowed_features.contains(feature)
1846            }
1847            _ => true,
1848        }
1849    }
1850
1851    // TODO(yuval): on a breaking version change, consider changing warnings to errors.
1852    /// Warns about the use of a trait in a path inside the same trait or an impl of it, and the use
1853    /// of an impl in a path inside the same impl, additionally, converts a `Self` equivalent
1854    /// trait to be resolved as `Self`.
1855    /// That is, warns about using the actual path equivalent to `Self`, where `Self` can be used.
1856    fn handle_same_impl_trait(
1857        &mut self,
1858        diagnostics: &mut SemanticDiagnostics,
1859        specialized_item: &mut ResolvedConcreteItem,
1860        generic_args_syntax_slice: &[ast::GenericArg],
1861        segment_stable_ptr: SyntaxStablePtrId,
1862    ) {
1863        match *specialized_item {
1864            ResolvedConcreteItem::Trait(current_segment_concrete_trait) => {
1865                match self.trait_or_impl_ctx {
1866                    TraitOrImplContext::None => {}
1867                    TraitOrImplContext::Trait(ctx_trait) => {
1868                        if self
1869                            .warn_trait_in_same_trait(
1870                                diagnostics,
1871                                current_segment_concrete_trait.trait_id(self.db),
1872                                generic_args_syntax_slice,
1873                                ctx_trait,
1874                                segment_stable_ptr,
1875                            )
1876                            .is_err()
1877                        {
1878                            *specialized_item =
1879                                ResolvedConcreteItem::SelfTrait(current_segment_concrete_trait);
1880                        }
1881                    }
1882                    TraitOrImplContext::Impl(ctx_impl_def_id) => {
1883                        self.warn_trait_in_its_impl(
1884                            diagnostics,
1885                            current_segment_concrete_trait,
1886                            ctx_impl_def_id,
1887                            segment_stable_ptr,
1888                        )
1889                        .ok();
1890                    }
1891                };
1892            }
1893            ResolvedConcreteItem::Impl(current_segment_impl_id) => {
1894                if let TraitOrImplContext::Impl(ctx_impl) = self.trait_or_impl_ctx {
1895                    // A ResolvedConcreteItem::Impl returned by
1896                    // `specialize_generic_module_item` must be ImplLongId::Concrete.
1897                    let current_segment_concrete_impl_id = extract_matches!(
1898                        current_segment_impl_id.lookup_intern(self.db),
1899                        ImplLongId::Concrete
1900                    );
1901                    self.warn_impl_in_same_impl(
1902                        diagnostics,
1903                        current_segment_concrete_impl_id.impl_def_id(self.db),
1904                        generic_args_syntax_slice,
1905                        ctx_impl,
1906                        segment_stable_ptr,
1907                    )
1908                    .ok();
1909                }
1910            }
1911            _ => {}
1912        };
1913    }
1914
1915    /// Raises a warning diagnostic (and returns an error) if the segment describes an impl in the
1916    /// context of that impl (that is, could be expressed as `Self`).
1917    fn warn_impl_in_same_impl(
1918        &mut self,
1919        diagnostics: &mut SemanticDiagnostics,
1920        current_segment_impl_def_id: ImplDefId,
1921        current_segment_generic_args: &[ast::GenericArg],
1922        ctx_impl: ImplDefId,
1923        segment_stable_ptr: SyntaxStablePtrId,
1924    ) -> Maybe<()> {
1925        if current_segment_impl_def_id != ctx_impl {
1926            return Ok(());
1927        }
1928
1929        let generic_params = self.db.impl_def_generic_params(ctx_impl)?;
1930        self.compare_segment_args_to_params(
1931            diagnostics,
1932            current_segment_generic_args,
1933            generic_params,
1934            segment_stable_ptr,
1935            ImplInImplMustBeExplicit,
1936            ImplItemForbiddenInTheImpl,
1937        )
1938    }
1939
1940    /// Raises a warning diagnostic (and returns an error) if the segment is of a concrete trait in
1941    /// the context of an impl of that concrete trait (that is, could be expressed as `Self`).
1942    fn warn_trait_in_its_impl(
1943        &mut self,
1944        diagnostics: &mut SemanticDiagnostics,
1945        current_segment_concrete_trait_id: ConcreteTraitId,
1946        impl_ctx: ImplDefId,
1947        segment_stable_ptr: SyntaxStablePtrId,
1948    ) -> Maybe<()> {
1949        let ctx_impl_trait = self.db.impl_def_trait(impl_ctx)?;
1950        if current_segment_concrete_trait_id.trait_id(self.db) != ctx_impl_trait {
1951            return Ok(());
1952        }
1953
1954        let ctx_impl_concrete_trait = self.db.impl_def_concrete_trait(impl_ctx)?;
1955        if ctx_impl_concrete_trait.generic_args(self.db)
1956            == current_segment_concrete_trait_id.generic_args(self.db)
1957        {
1958            return Err(diagnostics.report(segment_stable_ptr, TraitItemForbiddenInItsImpl));
1959        }
1960        Ok(())
1961    }
1962
1963    /// Raises a warning diagnostic (and returns an error) if the segment describes a trait in the
1964    /// context of that trait (that is, could be expressed as `Self`).
1965    fn warn_trait_in_same_trait(
1966        &mut self,
1967        diagnostics: &mut SemanticDiagnostics,
1968        current_segment_trait_id: TraitId,
1969        current_segment_generic_args: &[ast::GenericArg],
1970        ctx_trait: TraitId,
1971        segment_stable_ptr: SyntaxStablePtrId,
1972    ) -> Maybe<()> {
1973        if current_segment_trait_id != ctx_trait {
1974            return Ok(());
1975        }
1976
1977        let generic_params = self.db.trait_generic_params(ctx_trait)?;
1978        self.compare_segment_args_to_params(
1979            diagnostics,
1980            current_segment_generic_args,
1981            generic_params,
1982            segment_stable_ptr,
1983            TraitInTraitMustBeExplicit,
1984            TraitItemForbiddenInTheTrait,
1985        )
1986    }
1987
1988    /// Check if the given generic arguments exactly match the given generic parameters.
1989    /// This is used to check if a path segment is forbidden in the context of the item referred to
1990    /// by the segment.
1991    ///
1992    /// Fails if not all arguments are explicitly specified or if they are all specified and exactly
1993    /// the same as the parameters.
1994    fn compare_segment_args_to_params(
1995        &mut self,
1996        diagnostics: &mut SemanticDiagnostics,
1997        current_segment_generic_args: &[ast::GenericArg],
1998        generic_params: Vec<GenericParam>,
1999        segment_stable_ptr: SyntaxStablePtrId,
2000        must_be_explicit_error: SemanticDiagnosticKind,
2001        item_forbidden_in_itself_explicit_error: SemanticDiagnosticKind,
2002    ) -> Maybe<()> {
2003        // This assumes the current segment item and the context items are equal. In this specific
2004        // case we disallow implicit arguments.
2005        if current_segment_generic_args.len() < generic_params.len() {
2006            return Err(diagnostics.report(segment_stable_ptr, must_be_explicit_error));
2007        }
2008        let resolved_args = self.resolve_generic_args(
2009            diagnostics,
2010            GenericSubstitution::default(),
2011            &generic_params,
2012            current_segment_generic_args,
2013            segment_stable_ptr,
2014        )?;
2015
2016        if generic_params
2017            .iter()
2018            .zip_eq(resolved_args.iter())
2019            .all(|(gparam, garg)| gparam.as_arg(self.db) == *garg)
2020        {
2021            return Err(
2022                diagnostics.report(segment_stable_ptr, item_forbidden_in_itself_explicit_error)
2023            );
2024        }
2025        Ok(())
2026    }
2027
2028    /// Specializes a ResolvedGenericItem that came from a Statement Environment.
2029    fn specialize_generic_statement_arg(
2030        &mut self,
2031        segment: &ast::PathSegment,
2032        diagnostics: &mut SemanticDiagnostics,
2033        identifier: &ast::TerminalIdentifier,
2034        inner_generic_item: ResolvedGenericItem,
2035        generic_args_syntax: Option<Vec<ast::GenericArg>>,
2036    ) -> ResolvedConcreteItem {
2037        let segment_stable_ptr = segment.stable_ptr().untyped();
2038        let mut specialized_item = self
2039            .specialize_generic_module_item(
2040                diagnostics,
2041                identifier,
2042                inner_generic_item,
2043                generic_args_syntax.clone(),
2044            )
2045            .unwrap();
2046        self.handle_same_impl_trait(
2047            diagnostics,
2048            &mut specialized_item,
2049            &generic_args_syntax.unwrap_or_default(),
2050            segment_stable_ptr,
2051        );
2052        specialized_item
2053    }
2054}
2055
2056/// Resolves the segment if it's `Self`. Returns the Some(ResolvedConcreteItem) or Some(Err) if
2057/// segment == `Self` or None otherwise.
2058fn resolve_self_segment(
2059    db: &dyn SemanticGroup,
2060    diagnostics: &mut SemanticDiagnostics,
2061    identifier: &ast::TerminalIdentifier,
2062    trait_or_impl_ctx: &TraitOrImplContext,
2063) -> Option<Maybe<ResolvedConcreteItem>> {
2064    require(identifier.text(db.upcast()) == SELF_TYPE_KW)?;
2065    Some(resolve_actual_self_segment(db, diagnostics, identifier, trait_or_impl_ctx))
2066}
2067
2068/// Resolves the `Self` segment given that it's actually `Self`.
2069fn resolve_actual_self_segment(
2070    db: &dyn SemanticGroup,
2071    diagnostics: &mut SemanticDiagnostics,
2072    identifier: &ast::TerminalIdentifier,
2073    trait_or_impl_ctx: &TraitOrImplContext,
2074) -> Maybe<ResolvedConcreteItem> {
2075    match trait_or_impl_ctx {
2076        TraitOrImplContext::None => Err(diagnostics.report(identifier, SelfNotSupportedInContext)),
2077        TraitOrImplContext::Trait(trait_id) => {
2078            let generic_parameters = db.trait_generic_params(*trait_id)?;
2079            let concrete_trait_id = ConcreteTraitLongId {
2080                trait_id: *trait_id,
2081                generic_args: generic_params_to_args(&generic_parameters, db),
2082            }
2083            .intern(db);
2084            Ok(ResolvedConcreteItem::SelfTrait(concrete_trait_id))
2085        }
2086        TraitOrImplContext::Impl(impl_def_id) => {
2087            let generic_parameters = db.impl_def_generic_params(*impl_def_id)?;
2088            let impl_id = ImplLongId::Concrete(
2089                ConcreteImplLongId {
2090                    impl_def_id: *impl_def_id,
2091                    generic_args: generic_params_to_args(&generic_parameters, db),
2092                }
2093                .intern(db),
2094            );
2095            Ok(ResolvedConcreteItem::Impl(impl_id.intern(db)))
2096        }
2097    }
2098}
2099
2100/// The base module or crate for the path resolving.
2101enum ResolvedBase {
2102    /// The base module is a module.
2103    Module(ModuleId),
2104    /// The base module is a crate.
2105    Crate(CrateId),
2106    /// The base module to address is the statement
2107    StatementEnvironment(ResolvedGenericItem),
2108    /// The item is imported using global use.
2109    FoundThroughGlobalUse { item_info: ModuleItemInfo, containing_module: ModuleId },
2110    /// The base module is ambiguous.
2111    Ambiguous(Vec<ModuleItemId>),
2112    /// The base module is inaccessible.
2113    ItemNotVisible(ModuleItemId, Vec<ModuleId>),
2114}
2115
2116/// The callbacks to be used by `resolve_path_inner`.
2117struct ResolvePathInnerCallbacks<ResolvedItem, ResolveFirst, ResolveNext, Validate, Mark>
2118where
2119    ResolveFirst: FnMut(
2120        &mut Resolver<'_>,
2121        &mut SemanticDiagnostics,
2122        &mut Peekable<std::slice::Iter<'_, ast::PathSegment>>,
2123        Option<&mut Environment>,
2124    ) -> Maybe<ResolvedItem>,
2125    ResolveNext: FnMut(
2126        &mut Resolver<'_>,
2127        &mut SemanticDiagnostics,
2128        &ResolvedItem,
2129        &ast::PathSegment,
2130        NotFoundItemType,
2131    ) -> Maybe<ResolvedItem>,
2132    Validate: FnMut(&mut SemanticDiagnostics, &ast::PathSegment) -> Maybe<()>,
2133    Mark: FnMut(
2134        &mut ResolvedItems,
2135        &dyn SemanticGroup,
2136        &syntax::node::ast::PathSegment,
2137        ResolvedItem,
2138    ),
2139{
2140    /// Type for the resolved item pointed by the path segments.
2141    resolved_item_type: PhantomData<ResolvedItem>,
2142    /// Resolves the first segment of a path.
2143    resolve_path_first_segment: ResolveFirst,
2144    /// Given the current resolved item, resolves the next segment.
2145    resolve_path_next_segment: ResolveNext,
2146    /// An additional validation to perform for each segment. If it fails, the whole resolution
2147    /// fails.
2148    validate_segment: Validate,
2149    mark: Mark,
2150}