@@ -22,8 +22,8 @@ use crate::expr::inference::InferenceId;
2222use crate :: expr:: inference:: canonic:: ResultNoErrEx ;
2323use crate :: resolve:: { Resolver , ResolverData } ;
2424use crate :: substitution:: { GenericSubstitution , SemanticRewriter } ;
25- use crate :: types:: { ConcreteStructId , add_type_based_diagnostics, resolve_type} ;
26- use crate :: { GenericParam , SemanticDiagnostic , semantic} ;
25+ use crate :: types:: { ConcreteStructId , ConcreteTypeId , add_type_based_diagnostics, resolve_type} ;
26+ use crate :: { GenericParam , SemanticDiagnostic , TypeId , TypeLongId , semantic} ;
2727
2828#[ cfg( test) ]
2929#[ path = "structure_test.rs" ]
@@ -121,6 +121,76 @@ pub struct Member<'db> {
121121 pub visibility : Visibility ,
122122}
123123
124+ /// A member or element of a type, abstracting over named struct members and positional tuple
125+ /// elements, enabling unified member access across both.
126+ #[ derive( Clone , Debug , PartialEq , Eq , DebugWithDb , SemanticObject , salsa:: Update ) ]
127+ #[ debug_db( dyn Database ) ]
128+ pub struct TypeMember < ' db > {
129+ pub ty : TypeId < ' db > ,
130+ #[ dont_rewrite]
131+ pub visibility : Visibility ,
132+ pub kind : TypeMemberKind < ' db > ,
133+ }
134+
135+ /// Distinguishes between named struct members and positional tuple elements.
136+ #[ derive( Clone , Debug , PartialEq , Eq , DebugWithDb , SemanticObject , salsa:: Update ) ]
137+ #[ debug_db( dyn Database ) ]
138+ pub enum TypeMemberKind < ' db > {
139+ /// A named struct member, identified by its `MemberId`.
140+ StructMember ( MemberId < ' db > ) ,
141+ /// A positional tuple element at the given index.
142+ TupleElement ( #[ dont_rewrite] usize ) ,
143+ }
144+
145+ /// Returns the members of a type keyed by their access name, or `None` for types without
146+ /// members.
147+ ///
148+ /// For struct types, members are keyed by their declared name. For tuple types, members are keyed
149+ /// by their stringified index ("0", "1", ...).
150+ #[ salsa:: tracked( returns( ref) ) ]
151+ fn type_members_query < ' db > (
152+ db : & ' db dyn Database ,
153+ ty : TypeId < ' db > ,
154+ ) -> Maybe < Option < OrderedHashMap < SmolStrId < ' db > , TypeMember < ' db > > > > {
155+ match ty. long ( db) {
156+ TypeLongId :: Concrete ( ConcreteTypeId :: Struct ( concrete_struct_id) ) => {
157+ let members = db. concrete_struct_members ( * concrete_struct_id) ?;
158+ Ok ( Some (
159+ members
160+ . iter ( )
161+ . map ( |( name, member) | {
162+ (
163+ * name,
164+ TypeMember {
165+ ty : member. ty ,
166+ visibility : member. visibility ,
167+ kind : TypeMemberKind :: StructMember ( member. id ) ,
168+ } ,
169+ )
170+ } )
171+ . collect ( ) ,
172+ ) )
173+ }
174+ TypeLongId :: Tuple ( tys) => Ok ( Some (
175+ tys. iter ( )
176+ . enumerate ( )
177+ . map ( |( i, & ty) | {
178+ let name = SmolStrId :: from ( db, i. to_string ( ) ) ;
179+ (
180+ name,
181+ TypeMember {
182+ ty,
183+ visibility : Visibility :: Public ,
184+ kind : TypeMemberKind :: TupleElement ( i) ,
185+ } ,
186+ )
187+ } )
188+ . collect ( ) ,
189+ ) ) ,
190+ _ => Ok ( None ) ,
191+ }
192+ }
193+
124194/// Returns the definition data of a struct.
125195#[ salsa:: tracked( returns( ref) ) ]
126196fn struct_definition_data < ' db > (
@@ -296,6 +366,14 @@ pub trait StructSemantic<'db>: Database {
296366 ) -> Maybe < & ' db OrderedHashMap < SmolStrId < ' db > , semantic:: Member < ' db > > > {
297367 concrete_struct_members ( self . as_dyn_database ( ) , concrete_struct_id) . maybe_as_ref ( )
298368 }
369+ /// Returns the members of a type keyed by their access name, or `None` for types without
370+ /// members. Works for both struct types and tuples.
371+ fn type_members (
372+ & ' db self ,
373+ ty : TypeId < ' db > ,
374+ ) -> Maybe < Option < & ' db OrderedHashMap < SmolStrId < ' db > , TypeMember < ' db > > > > {
375+ type_members_query ( self . as_dyn_database ( ) , ty) . maybe_as_ref ( ) . map ( |opt| opt. as_ref ( ) )
376+ }
299377}
300378
301379impl < ' db , T : Database + ?Sized > StructSemantic < ' db > for T { }
0 commit comments