diff --git a/crates/cairo-lang-defs/src/db.rs b/crates/cairo-lang-defs/src/db.rs index b3b518fdf77..3246bc47019 100644 --- a/crates/cairo-lang-defs/src/db.rs +++ b/crates/cairo-lang-defs/src/db.rs @@ -1395,13 +1395,15 @@ fn validate_attributes<'db>( } } ast::ModuleItem::Struct(item) => { - validate_attributes_element_list( - db, - allowed_attributes, - &extra_allowed_attributes, - item.members(db).elements(db), - plugin_diagnostics, - ); + if let ast::StructBody::Braces(body) = item.body(db) { + validate_attributes_element_list( + db, + allowed_attributes, + &extra_allowed_attributes, + body.members(db).elements(db), + plugin_diagnostics, + ); + } } ast::ModuleItem::Enum(item) => { validate_attributes_element_list( diff --git a/crates/cairo-lang-defs/src/ids.rs b/crates/cairo-lang-defs/src/ids.rs index 117e446ecb3..5135d9eebfd 100644 --- a/crates/cairo-lang-defs/src/ids.rs +++ b/crates/cairo-lang-defs/src/ids.rs @@ -992,7 +992,8 @@ define_named_language_element_id!(MemberId, MemberLongId, ast::Member<'db>); impl<'db> MemberId<'db> { pub fn struct_id(&self, db: &'db dyn Database) -> StructId<'db> { let MemberLongId(module_id, ptr) = self.long(db).clone(); - let struct_ptr = ast::ItemStructPtr(ptr.untyped().nth_parent(db, 2)); + // Member -> MemberList -> StructBodyBraces -> ItemStruct (3 levels up). + let struct_ptr = ast::ItemStructPtr(ptr.untyped().nth_parent(db, 3)); StructLongId(module_id, struct_ptr).intern(db) } } diff --git a/crates/cairo-lang-lowering/src/lower/mod.rs b/crates/cairo-lang-lowering/src/lower/mod.rs index 0c46a04c9c1..9a14a76734a 100644 --- a/crates/cairo-lang-lowering/src/lower/mod.rs +++ b/crates/cairo-lang-lowering/src/lower/mod.rs @@ -1834,12 +1834,30 @@ fn lower_expr_member_access<'db>( .collect(); (member_tys, member_idx) } - TypeMemberKind::TupleElement(idx) => { - let tys = extract_matches!(container_long_ty, TypeLongId::Tuple); - let member_tys = - tys.iter().map(|&ty| wrap_in_snapshots(ctx.db, ty, expr.n_snapshots)).collect(); - (member_tys, *idx) - } + TypeMemberKind::TupleElement(idx) => match container_long_ty { + TypeLongId::Tuple(tys) => { + let member_tys = + tys.iter().map(|&ty| wrap_in_snapshots(ctx.db, ty, expr.n_snapshots)).collect(); + (member_tys, *idx) + } + TypeLongId::Concrete(semantic::ConcreteTypeId::Struct(concrete_struct_id)) => { + let positional_tys = ctx + .db + .concrete_struct_positional_types(concrete_struct_id) + .map_err(LoweringFlowError::Failed)? + .ok_or_else(|| { + LoweringFlowError::Failed( + ctx.diagnostics.report(expr.stable_ptr.untyped(), UnexpectedError), + ) + })?; + let member_tys = positional_tys + .iter() + .map(|&(ty, _)| wrap_in_snapshots(ctx.db, ty, expr.n_snapshots)) + .collect(); + (member_tys, *idx) + } + _ => unreachable!("TupleElement access on unexpected container type"), + }, }; Ok(LoweredExpr::AtVariable( generators::StructMemberAccess { diff --git a/crates/cairo-lang-parser/src/parser.rs b/crates/cairo-lang-parser/src/parser.rs index 79241cb4347..a31f57efabb 100644 --- a/crates/cairo-lang-parser/src/parser.rs +++ b/crates/cairo-lang-parser/src/parser.rs @@ -486,7 +486,8 @@ impl<'a, 'mt> Parser<'a, 'mt> { } /// Assumes the current token is Struct. - /// Expected pattern: `struct{}` + /// Expected pattern: `struct{}` or + /// `struct()`. fn expect_item_struct( &mut self, attributes: AttributeListGreen<'a>, @@ -495,9 +496,17 @@ impl<'a, 'mt> Parser<'a, 'mt> { let struct_kw = self.take::>(); let name = self.parse_identifier(); let generic_params = self.parse_optional_generic_params(); - let lbrace = self.parse_token::>(); - let members = self.parse_member_list(); - let rbrace = self.parse_token::>(); + let body = if self.peek().kind == SyntaxKind::TerminalLParen { + let lparen = self.take::>(); + let members = self.parse_struct_arg_member_list(); + let rparen = self.parse_token::>(); + StructBodyParens::new_green(self.db, lparen, members, rparen).into() + } else { + let lbrace = self.parse_token::>(); + let members = self.parse_member_list(); + let rbrace = self.parse_token::>(); + StructBodyBraces::new_green(self.db, lbrace, members, rbrace).into() + }; ItemStruct::new_green( self.db, attributes, @@ -505,12 +514,34 @@ impl<'a, 'mt> Parser<'a, 'mt> { struct_kw, name, generic_params, - lbrace, - members, - rbrace, + body, ) } + /// Returns a GreenId of a node with kind StructArgMemberList. + fn parse_struct_arg_member_list(&mut self) -> StructArgMemberListGreen<'a> { + StructArgMemberList::new_green( + self.db, + &self.parse_separated_list::< + StructArgMember<'_>, + TerminalComma<'_>, + StructArgMemberListElementOrSeparatorGreen<'_>, + >( + Self::try_parse_struct_arg_member, + is_of_kind!(rparen, block, lbrace, rbrace, module_item_kw), + "type", + ), + ) + } + + /// Returns a GreenId of a node with kind StructArgMember, or TryParseFailure if a positional + /// struct member can't be parsed. + fn try_parse_struct_arg_member(&mut self) -> TryParseResult> { + let visibility = self.parse_visibility(); + let ty = self.try_parse_type_expr()?; + Ok(StructArgMember::new_green(self.db, visibility, ty)) + } + /// Assumes the current token is Enum. /// Expected pattern: `enum{}` fn expect_item_enum( diff --git a/crates/cairo-lang-parser/src/parser_test_data/full_trees/test1 b/crates/cairo-lang-parser/src/parser_test_data/full_trees/test1 index ca65edb1125..592a49a56be 100644 --- a/crates/cairo-lang-parser/src/parser_test_data/full_trees/test1 +++ b/crates/cairo-lang-parser/src/parser_test_data/full_trees/test1 @@ -743,70 +743,71 @@ error[E1023]: Missing tokens. Expected an item after attributes. │ │ │ │ └── item #1 (kind: GenericParamType) │ │ │ │ └── name (kind: TokenIdentifier): 'B' │ │ │ └── rangle (kind: TokenGT): '>' - │ │ ├── lbrace (kind: TokenLBrace): '{' - │ │ ├── members (kind: MemberList) - │ │ │ ├── item #0 (kind: Member) - │ │ │ │ ├── attributes (kind: AttributeList) [] - │ │ │ │ ├── visibility (kind: VisibilityPub) - │ │ │ │ │ ├── pub_kw (kind: TokenPub): 'pub' - │ │ │ │ │ └── argument_clause (kind: OptionVisibilityPubArgumentClauseEmpty) [] - │ │ │ │ ├── name (kind: TokenIdentifier): 'member' - │ │ │ │ └── type_clause (kind: TypeClause) - │ │ │ │ ├── colon (kind: TokenColon): ':' - │ │ │ │ └── ty (kind: ExprPath) - │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] - │ │ │ │ └── segments (kind: ExprPathInner) - │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ └── ident (kind: TokenIdentifier): 'bool' - │ │ │ ├── separator #0 (kind: TokenComma): ',' - │ │ │ └── item #1 (kind: Member) - │ │ │ ├── attributes (kind: AttributeList) - │ │ │ │ └── child #0 (kind: Attribute) - │ │ │ │ ├── hash (kind: TokenHash): '#' - │ │ │ │ ├── lbrack (kind: TokenLBrack): '[' - │ │ │ │ ├── attr (kind: ExprPath) - │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] - │ │ │ │ │ └── segments (kind: ExprPathInner) - │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ │ └── ident (kind: TokenIdentifier): 'annot' - │ │ │ │ ├── arguments (kind: ArgListParenthesized) - │ │ │ │ │ ├── lparen (kind: TokenLParen): '(' - │ │ │ │ │ ├── arguments (kind: ArgList) - │ │ │ │ │ │ └── item #0 (kind: Arg) - │ │ │ │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ │ │ │ └── value (kind: ExprPath) - │ │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] - │ │ │ │ │ │ └── segments (kind: ExprPathInner) - │ │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ │ │ └── ident (kind: TokenIdentifier): 'a' - │ │ │ │ │ └── rparen (kind: TokenRParen): ')' - │ │ │ │ └── rbrack (kind: TokenRBrack): ']' - │ │ │ ├── visibility (kind: VisibilityDefault) [] - │ │ │ ├── name (kind: TokenIdentifier): 'member2' - │ │ │ └── type_clause (kind: TypeClause) - │ │ │ ├── colon (kind: TokenColon): ':' - │ │ │ └── ty (kind: ExprListParenthesized) - │ │ │ ├── lparen (kind: TokenLParen): '(' - │ │ │ ├── expressions (kind: ExprList) - │ │ │ │ ├── item #0 (kind: ExprPath) - │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] - │ │ │ │ │ └── segments (kind: ExprPathInner) - │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ │ └── ident (kind: TokenIdentifier): 'bool' - │ │ │ │ ├── separator #0 (kind: TokenComma): ',' - │ │ │ │ ├── item #1 (kind: ExprPath) - │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] - │ │ │ │ │ └── segments (kind: ExprPathInner) - │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ │ └── ident (kind: TokenIdentifier): 'felt252' - │ │ │ │ ├── separator #1 (kind: TokenComma): ',' - │ │ │ │ └── item #2 (kind: ExprListParenthesized) - │ │ │ │ ├── lparen (kind: TokenLParen): '(' - │ │ │ │ ├── expressions (kind: ExprList) [] - │ │ │ │ └── rparen (kind: TokenRParen): ')' - │ │ │ └── rparen (kind: TokenRParen): ')' - │ │ └── rbrace (kind: TokenRBrace): '}' + │ │ └── body (kind: StructBodyBraces) + │ │ ├── lbrace (kind: TokenLBrace): '{' + │ │ ├── members (kind: MemberList) + │ │ │ ├── item #0 (kind: Member) + │ │ │ │ ├── attributes (kind: AttributeList) [] + │ │ │ │ ├── visibility (kind: VisibilityPub) + │ │ │ │ │ ├── pub_kw (kind: TokenPub): 'pub' + │ │ │ │ │ └── argument_clause (kind: OptionVisibilityPubArgumentClauseEmpty) [] + │ │ │ │ ├── name (kind: TokenIdentifier): 'member' + │ │ │ │ └── type_clause (kind: TypeClause) + │ │ │ │ ├── colon (kind: TokenColon): ':' + │ │ │ │ └── ty (kind: ExprPath) + │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] + │ │ │ │ └── segments (kind: ExprPathInner) + │ │ │ │ └── item #0 (kind: PathSegmentSimple) + │ │ │ │ └── ident (kind: TokenIdentifier): 'bool' + │ │ │ ├── separator #0 (kind: TokenComma): ',' + │ │ │ └── item #1 (kind: Member) + │ │ │ ├── attributes (kind: AttributeList) + │ │ │ │ └── child #0 (kind: Attribute) + │ │ │ │ ├── hash (kind: TokenHash): '#' + │ │ │ │ ├── lbrack (kind: TokenLBrack): '[' + │ │ │ │ ├── attr (kind: ExprPath) + │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] + │ │ │ │ │ └── segments (kind: ExprPathInner) + │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) + │ │ │ │ │ └── ident (kind: TokenIdentifier): 'annot' + │ │ │ │ ├── arguments (kind: ArgListParenthesized) + │ │ │ │ │ ├── lparen (kind: TokenLParen): '(' + │ │ │ │ │ ├── arguments (kind: ArgList) + │ │ │ │ │ │ └── item #0 (kind: Arg) + │ │ │ │ │ │ ├── modifiers (kind: ModifierList) [] + │ │ │ │ │ │ └── arg_clause (kind: ArgClauseUnnamed) + │ │ │ │ │ │ └── value (kind: ExprPath) + │ │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] + │ │ │ │ │ │ └── segments (kind: ExprPathInner) + │ │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) + │ │ │ │ │ │ └── ident (kind: TokenIdentifier): 'a' + │ │ │ │ │ └── rparen (kind: TokenRParen): ')' + │ │ │ │ └── rbrack (kind: TokenRBrack): ']' + │ │ │ ├── visibility (kind: VisibilityDefault) [] + │ │ │ ├── name (kind: TokenIdentifier): 'member2' + │ │ │ └── type_clause (kind: TypeClause) + │ │ │ ├── colon (kind: TokenColon): ':' + │ │ │ └── ty (kind: ExprListParenthesized) + │ │ │ ├── lparen (kind: TokenLParen): '(' + │ │ │ ├── expressions (kind: ExprList) + │ │ │ │ ├── item #0 (kind: ExprPath) + │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] + │ │ │ │ │ └── segments (kind: ExprPathInner) + │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) + │ │ │ │ │ └── ident (kind: TokenIdentifier): 'bool' + │ │ │ │ ├── separator #0 (kind: TokenComma): ',' + │ │ │ │ ├── item #1 (kind: ExprPath) + │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] + │ │ │ │ │ └── segments (kind: ExprPathInner) + │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) + │ │ │ │ │ └── ident (kind: TokenIdentifier): 'felt252' + │ │ │ │ ├── separator #1 (kind: TokenComma): ',' + │ │ │ │ └── item #2 (kind: ExprListParenthesized) + │ │ │ │ ├── lparen (kind: TokenLParen): '(' + │ │ │ │ ├── expressions (kind: ExprList) [] + │ │ │ │ └── rparen (kind: TokenRParen): ')' + │ │ │ └── rparen (kind: TokenRParen): ')' + │ │ └── rbrace (kind: TokenRBrace): '}' │ ├── child #7 (kind: ItemEnum) │ │ ├── attributes (kind: AttributeList) [] │ │ ├── visibility (kind: VisibilityPub) @@ -2726,155 +2727,156 @@ error[E1023]: Missing tokens. Expected an item after attributes. │ │ │ ├── token (kind: TokenGT): '>' │ │ │ └── trailing_trivia (kind: Trivia) │ │ │ └── child #0 (kind: TokenWhitespace). - │ │ ├── lbrace (kind: TerminalLBrace) - │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ ├── token (kind: TokenLBrace): '{' - │ │ │ └── trailing_trivia (kind: Trivia) - │ │ │ └── child #0 (kind: TokenNewline). - │ │ ├── members (kind: MemberList) - │ │ │ ├── item #0 (kind: Member) - │ │ │ │ ├── attributes (kind: AttributeList) [] - │ │ │ │ ├── visibility (kind: VisibilityPub) - │ │ │ │ │ ├── pub_kw (kind: TerminalPub) - │ │ │ │ │ │ ├── leading_trivia (kind: Trivia) - │ │ │ │ │ │ │ └── child #0 (kind: TokenWhitespace). - │ │ │ │ │ │ ├── token (kind: TokenPub): 'pub' - │ │ │ │ │ │ └── trailing_trivia (kind: Trivia) - │ │ │ │ │ │ └── child #0 (kind: TokenWhitespace). - │ │ │ │ │ └── argument_clause (kind: OptionVisibilityPubArgumentClauseEmpty) [] - │ │ │ │ ├── name (kind: TerminalIdentifier) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ ├── token (kind: TokenIdentifier): 'member' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ │ └── type_clause (kind: TypeClause) - │ │ │ │ ├── colon (kind: TerminalColon) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ ├── token (kind: TokenColon): ':' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) - │ │ │ │ │ └── child #0 (kind: TokenWhitespace). - │ │ │ │ └── ty (kind: ExprPath) - │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] - │ │ │ │ └── segments (kind: ExprPathInner) - │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ └── ident (kind: TerminalIdentifier) - │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ ├── token (kind: TokenIdentifier): 'bool' - │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ ├── separator #0 (kind: TerminalComma) - │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ ├── token (kind: TokenComma): ',' - │ │ │ │ └── trailing_trivia (kind: Trivia) - │ │ │ │ └── child #0 (kind: TokenNewline). - │ │ │ └── item #1 (kind: Member) - │ │ │ ├── attributes (kind: AttributeList) - │ │ │ │ └── child #0 (kind: Attribute) - │ │ │ │ ├── hash (kind: TerminalHash) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) - │ │ │ │ │ │ └── child #0 (kind: TokenWhitespace). - │ │ │ │ │ ├── token (kind: TokenHash): '#' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ │ ├── lbrack (kind: TerminalLBrack) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ ├── token (kind: TokenLBrack): '[' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ │ ├── attr (kind: ExprPath) - │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] - │ │ │ │ │ └── segments (kind: ExprPathInner) - │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ │ └── ident (kind: TerminalIdentifier) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ ├── token (kind: TokenIdentifier): 'annot' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ │ ├── arguments (kind: ArgListParenthesized) - │ │ │ │ │ ├── lparen (kind: TerminalLParen) - │ │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ │ ├── token (kind: TokenLParen): '(' - │ │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ │ │ ├── arguments (kind: ArgList) - │ │ │ │ │ │ └── item #0 (kind: Arg) - │ │ │ │ │ │ ├── modifiers (kind: ModifierList) [] - │ │ │ │ │ │ └── arg_clause (kind: ArgClauseUnnamed) - │ │ │ │ │ │ └── value (kind: ExprPath) - │ │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] - │ │ │ │ │ │ └── segments (kind: ExprPathInner) - │ │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ │ │ └── ident (kind: TerminalIdentifier) - │ │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ │ ├── token (kind: TokenIdentifier): 'a' - │ │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ │ │ └── rparen (kind: TerminalRParen) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ ├── token (kind: TokenRParen): ')' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ │ └── rbrack (kind: TerminalRBrack) - │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ ├── token (kind: TokenRBrack): ']' - │ │ │ │ └── trailing_trivia (kind: Trivia) - │ │ │ │ └── child #0 (kind: TokenNewline). - │ │ │ ├── visibility (kind: VisibilityDefault) [] - │ │ │ ├── name (kind: TerminalIdentifier) - │ │ │ │ ├── leading_trivia (kind: Trivia) - │ │ │ │ │ └── child #0 (kind: TokenWhitespace). - │ │ │ │ ├── token (kind: TokenIdentifier): 'member2' - │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ └── type_clause (kind: TypeClause) - │ │ │ ├── colon (kind: TerminalColon) - │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ ├── token (kind: TokenColon): ':' - │ │ │ │ └── trailing_trivia (kind: Trivia) - │ │ │ │ └── child #0 (kind: TokenWhitespace). - │ │ │ └── ty (kind: ExprListParenthesized) - │ │ │ ├── lparen (kind: TerminalLParen) - │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ ├── token (kind: TokenLParen): '(' - │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ ├── expressions (kind: ExprList) - │ │ │ │ ├── item #0 (kind: ExprPath) - │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] - │ │ │ │ │ └── segments (kind: ExprPathInner) - │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ │ └── ident (kind: TerminalIdentifier) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ ├── token (kind: TokenIdentifier): 'bool' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ │ ├── separator #0 (kind: TerminalComma) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ ├── token (kind: TokenComma): ',' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) - │ │ │ │ │ └── child #0 (kind: TokenWhitespace). - │ │ │ │ ├── item #1 (kind: ExprPath) - │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] - │ │ │ │ │ └── segments (kind: ExprPathInner) - │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ │ └── ident (kind: TerminalIdentifier) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ ├── token (kind: TokenIdentifier): 'felt252' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ │ ├── separator #1 (kind: TerminalComma) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ ├── token (kind: TokenComma): ',' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) - │ │ │ │ │ └── child #0 (kind: TokenWhitespace). - │ │ │ │ └── item #2 (kind: ExprListParenthesized) - │ │ │ │ ├── lparen (kind: TerminalLParen) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ ├── token (kind: TokenLParen): '(' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ │ ├── expressions (kind: ExprList) [] - │ │ │ │ └── rparen (kind: TerminalRParen) - │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ ├── token (kind: TokenRParen): ')' - │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ └── rparen (kind: TerminalRParen) - │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ ├── token (kind: TokenRParen): ')' - │ │ │ └── trailing_trivia (kind: Trivia) - │ │ │ └── child #0 (kind: TokenNewline). - │ │ └── rbrace (kind: TerminalRBrace) - │ │ ├── leading_trivia (kind: Trivia) [] - │ │ ├── token (kind: TokenRBrace): '}' - │ │ └── trailing_trivia (kind: Trivia) - │ │ └── child #0 (kind: TokenNewline). + │ │ └── body (kind: StructBodyBraces) + │ │ ├── lbrace (kind: TerminalLBrace) + │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ ├── token (kind: TokenLBrace): '{' + │ │ │ └── trailing_trivia (kind: Trivia) + │ │ │ └── child #0 (kind: TokenNewline). + │ │ ├── members (kind: MemberList) + │ │ │ ├── item #0 (kind: Member) + │ │ │ │ ├── attributes (kind: AttributeList) [] + │ │ │ │ ├── visibility (kind: VisibilityPub) + │ │ │ │ │ ├── pub_kw (kind: TerminalPub) + │ │ │ │ │ │ ├── leading_trivia (kind: Trivia) + │ │ │ │ │ │ │ └── child #0 (kind: TokenWhitespace). + │ │ │ │ │ │ ├── token (kind: TokenPub): 'pub' + │ │ │ │ │ │ └── trailing_trivia (kind: Trivia) + │ │ │ │ │ │ └── child #0 (kind: TokenWhitespace). + │ │ │ │ │ └── argument_clause (kind: OptionVisibilityPubArgumentClauseEmpty) [] + │ │ │ │ ├── name (kind: TerminalIdentifier) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ ├── token (kind: TokenIdentifier): 'member' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ │ └── type_clause (kind: TypeClause) + │ │ │ │ ├── colon (kind: TerminalColon) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ ├── token (kind: TokenColon): ':' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) + │ │ │ │ │ └── child #0 (kind: TokenWhitespace). + │ │ │ │ └── ty (kind: ExprPath) + │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] + │ │ │ │ └── segments (kind: ExprPathInner) + │ │ │ │ └── item #0 (kind: PathSegmentSimple) + │ │ │ │ └── ident (kind: TerminalIdentifier) + │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ ├── token (kind: TokenIdentifier): 'bool' + │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ ├── separator #0 (kind: TerminalComma) + │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ ├── token (kind: TokenComma): ',' + │ │ │ │ └── trailing_trivia (kind: Trivia) + │ │ │ │ └── child #0 (kind: TokenNewline). + │ │ │ └── item #1 (kind: Member) + │ │ │ ├── attributes (kind: AttributeList) + │ │ │ │ └── child #0 (kind: Attribute) + │ │ │ │ ├── hash (kind: TerminalHash) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) + │ │ │ │ │ │ └── child #0 (kind: TokenWhitespace). + │ │ │ │ │ ├── token (kind: TokenHash): '#' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ │ ├── lbrack (kind: TerminalLBrack) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ ├── token (kind: TokenLBrack): '[' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ │ ├── attr (kind: ExprPath) + │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] + │ │ │ │ │ └── segments (kind: ExprPathInner) + │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) + │ │ │ │ │ └── ident (kind: TerminalIdentifier) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ ├── token (kind: TokenIdentifier): 'annot' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ │ ├── arguments (kind: ArgListParenthesized) + │ │ │ │ │ ├── lparen (kind: TerminalLParen) + │ │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ │ ├── token (kind: TokenLParen): '(' + │ │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ │ │ ├── arguments (kind: ArgList) + │ │ │ │ │ │ └── item #0 (kind: Arg) + │ │ │ │ │ │ ├── modifiers (kind: ModifierList) [] + │ │ │ │ │ │ └── arg_clause (kind: ArgClauseUnnamed) + │ │ │ │ │ │ └── value (kind: ExprPath) + │ │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] + │ │ │ │ │ │ └── segments (kind: ExprPathInner) + │ │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) + │ │ │ │ │ │ └── ident (kind: TerminalIdentifier) + │ │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ │ ├── token (kind: TokenIdentifier): 'a' + │ │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ │ │ └── rparen (kind: TerminalRParen) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ ├── token (kind: TokenRParen): ')' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ │ └── rbrack (kind: TerminalRBrack) + │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ ├── token (kind: TokenRBrack): ']' + │ │ │ │ └── trailing_trivia (kind: Trivia) + │ │ │ │ └── child #0 (kind: TokenNewline). + │ │ │ ├── visibility (kind: VisibilityDefault) [] + │ │ │ ├── name (kind: TerminalIdentifier) + │ │ │ │ ├── leading_trivia (kind: Trivia) + │ │ │ │ │ └── child #0 (kind: TokenWhitespace). + │ │ │ │ ├── token (kind: TokenIdentifier): 'member2' + │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ └── type_clause (kind: TypeClause) + │ │ │ ├── colon (kind: TerminalColon) + │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ ├── token (kind: TokenColon): ':' + │ │ │ │ └── trailing_trivia (kind: Trivia) + │ │ │ │ └── child #0 (kind: TokenWhitespace). + │ │ │ └── ty (kind: ExprListParenthesized) + │ │ │ ├── lparen (kind: TerminalLParen) + │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ ├── token (kind: TokenLParen): '(' + │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ ├── expressions (kind: ExprList) + │ │ │ │ ├── item #0 (kind: ExprPath) + │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] + │ │ │ │ │ └── segments (kind: ExprPathInner) + │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) + │ │ │ │ │ └── ident (kind: TerminalIdentifier) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ ├── token (kind: TokenIdentifier): 'bool' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ │ ├── separator #0 (kind: TerminalComma) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ ├── token (kind: TokenComma): ',' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) + │ │ │ │ │ └── child #0 (kind: TokenWhitespace). + │ │ │ │ ├── item #1 (kind: ExprPath) + │ │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] + │ │ │ │ │ └── segments (kind: ExprPathInner) + │ │ │ │ │ └── item #0 (kind: PathSegmentSimple) + │ │ │ │ │ └── ident (kind: TerminalIdentifier) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ ├── token (kind: TokenIdentifier): 'felt252' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ │ ├── separator #1 (kind: TerminalComma) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ ├── token (kind: TokenComma): ',' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) + │ │ │ │ │ └── child #0 (kind: TokenWhitespace). + │ │ │ │ └── item #2 (kind: ExprListParenthesized) + │ │ │ │ ├── lparen (kind: TerminalLParen) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ ├── token (kind: TokenLParen): '(' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ │ ├── expressions (kind: ExprList) [] + │ │ │ │ └── rparen (kind: TerminalRParen) + │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ ├── token (kind: TokenRParen): ')' + │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ └── rparen (kind: TerminalRParen) + │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ ├── token (kind: TokenRParen): ')' + │ │ │ └── trailing_trivia (kind: Trivia) + │ │ │ └── child #0 (kind: TokenNewline). + │ │ └── rbrace (kind: TerminalRBrace) + │ │ ├── leading_trivia (kind: Trivia) [] + │ │ ├── token (kind: TokenRBrace): '}' + │ │ └── trailing_trivia (kind: Trivia) + │ │ └── child #0 (kind: TokenNewline). │ ├── child #7 (kind: ItemEnum) │ │ ├── attributes (kind: AttributeList) [] │ │ ├── visibility (kind: VisibilityPub) diff --git a/crates/cairo-lang-parser/src/parser_test_data/full_trees/test2 b/crates/cairo-lang-parser/src/parser_test_data/full_trees/test2 index 6a429de0efc..153c11885a7 100644 --- a/crates/cairo-lang-parser/src/parser_test_data/full_trees/test2 +++ b/crates/cairo-lang-parser/src/parser_test_data/full_trees/test2 @@ -421,32 +421,33 @@ skipped tokens │ │ ├── struct_kw (kind: TokenStruct): 'struct' │ │ ├── name (kind: TokenIdentifier): 'A' │ │ ├── generic_params (kind: OptionWrappedGenericParamListEmpty) [] - │ │ ├── lbrace (kind: TokenLBrace): '{' - │ │ ├── members (kind: MemberList) - │ │ │ ├── item #0 (kind: Member) - │ │ │ │ ├── attributes (kind: AttributeList) [] - │ │ │ │ ├── visibility (kind: VisibilityDefault) [] - │ │ │ │ ├── name (kind: TokenIdentifier): 'x' - │ │ │ │ └── type_clause (kind: TypeClause) - │ │ │ │ ├── colon (kind: TokenColon): ':' - │ │ │ │ └── ty (kind: ExprPath) - │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] - │ │ │ │ └── segments (kind: ExprPathInner) - │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ └── ident (kind: TokenIdentifier): 'a' - │ │ │ ├── separator #0 (kind: TokenComma): ',' - │ │ │ └── item #1 (kind: Member) - │ │ │ ├── attributes (kind: AttributeList) [] - │ │ │ ├── visibility (kind: VisibilityDefault) [] - │ │ │ ├── name (kind: TokenIdentifier): 'y' - │ │ │ └── type_clause (kind: TypeClause) - │ │ │ ├── colon (kind: TokenColon): ':' - │ │ │ └── ty (kind: ExprPath) - │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] - │ │ │ └── segments (kind: ExprPathInner) - │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ └── ident (kind: TokenIdentifier): 'int' - │ │ └── rbrace (kind: TokenRBrace): '}' + │ │ └── body (kind: StructBodyBraces) + │ │ ├── lbrace (kind: TokenLBrace): '{' + │ │ ├── members (kind: MemberList) + │ │ │ ├── item #0 (kind: Member) + │ │ │ │ ├── attributes (kind: AttributeList) [] + │ │ │ │ ├── visibility (kind: VisibilityDefault) [] + │ │ │ │ ├── name (kind: TokenIdentifier): 'x' + │ │ │ │ └── type_clause (kind: TypeClause) + │ │ │ │ ├── colon (kind: TokenColon): ':' + │ │ │ │ └── ty (kind: ExprPath) + │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] + │ │ │ │ └── segments (kind: ExprPathInner) + │ │ │ │ └── item #0 (kind: PathSegmentSimple) + │ │ │ │ └── ident (kind: TokenIdentifier): 'a' + │ │ │ ├── separator #0 (kind: TokenComma): ',' + │ │ │ └── item #1 (kind: Member) + │ │ │ ├── attributes (kind: AttributeList) [] + │ │ │ ├── visibility (kind: VisibilityDefault) [] + │ │ │ ├── name (kind: TokenIdentifier): 'y' + │ │ │ └── type_clause (kind: TypeClause) + │ │ │ ├── colon (kind: TokenColon): ':' + │ │ │ └── ty (kind: ExprPath) + │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] + │ │ │ └── segments (kind: ExprPathInner) + │ │ │ └── item #0 (kind: PathSegmentSimple) + │ │ │ └── ident (kind: TokenIdentifier): 'int' + │ │ └── rbrace (kind: TokenRBrace): '}' │ └── rbrace (kind: TokenRBrace): '}' └── eof (kind: TokenEndOfFile). @@ -1356,70 +1357,71 @@ skipped tokens │ │ │ ├── token (kind: TokenIdentifier): 'A' │ │ │ └── trailing_trivia (kind: Trivia) [] │ │ ├── generic_params (kind: OptionWrappedGenericParamListEmpty) [] - │ │ ├── lbrace (kind: TerminalLBrace) - │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ ├── token (kind: TokenLBrace): '{' - │ │ │ └── trailing_trivia (kind: Trivia) - │ │ │ └── child #0 (kind: TokenNewline). - │ │ ├── members (kind: MemberList) - │ │ │ ├── item #0 (kind: Member) - │ │ │ │ ├── attributes (kind: AttributeList) [] - │ │ │ │ ├── visibility (kind: VisibilityDefault) [] - │ │ │ │ ├── name (kind: TerminalIdentifier) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) - │ │ │ │ │ │ └── child #0 (kind: TokenWhitespace). - │ │ │ │ │ ├── token (kind: TokenIdentifier): 'x' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ │ └── type_clause (kind: TypeClause) - │ │ │ │ ├── colon (kind: TerminalColon) - │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ │ ├── token (kind: TokenColon): ':' - │ │ │ │ │ └── trailing_trivia (kind: Trivia) - │ │ │ │ │ └── child #0 (kind: TokenWhitespace). - │ │ │ │ └── ty (kind: ExprPath) - │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] - │ │ │ │ └── segments (kind: ExprPathInner) - │ │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ │ └── ident (kind: TerminalIdentifier) - │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ ├── token (kind: TokenIdentifier): 'a' - │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ ├── separator #0 (kind: TerminalComma) - │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ ├── token (kind: TokenComma): ',' - │ │ │ │ └── trailing_trivia (kind: Trivia) - │ │ │ │ ├── child #0 (kind: TokenWhitespace). - │ │ │ │ ├── child #1 (kind: TokenSingleLineComment): '// Comment.' - │ │ │ │ └── child #2 (kind: TokenNewline). - │ │ │ └── item #1 (kind: Member) - │ │ │ ├── attributes (kind: AttributeList) [] - │ │ │ ├── visibility (kind: VisibilityDefault) [] - │ │ │ ├── name (kind: TerminalIdentifier) - │ │ │ │ ├── leading_trivia (kind: Trivia) - │ │ │ │ │ └── child #0 (kind: TokenWhitespace). - │ │ │ │ ├── token (kind: TokenIdentifier): 'y' - │ │ │ │ └── trailing_trivia (kind: Trivia) [] - │ │ │ └── type_clause (kind: TypeClause) - │ │ │ ├── colon (kind: TerminalColon) - │ │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ │ ├── token (kind: TokenColon): ':' - │ │ │ │ └── trailing_trivia (kind: Trivia) - │ │ │ │ └── child #0 (kind: TokenWhitespace). - │ │ │ └── ty (kind: ExprPath) - │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] - │ │ │ └── segments (kind: ExprPathInner) - │ │ │ └── item #0 (kind: PathSegmentSimple) - │ │ │ └── ident (kind: TerminalIdentifier) - │ │ │ ├── leading_trivia (kind: Trivia) [] - │ │ │ ├── token (kind: TokenIdentifier): 'int' - │ │ │ └── trailing_trivia (kind: Trivia) - │ │ │ └── child #0 (kind: TokenNewline). - │ │ └── rbrace (kind: TerminalRBrace) - │ │ ├── leading_trivia (kind: Trivia) - │ │ │ └── child #0 (kind: TokenWhitespace). - │ │ ├── token (kind: TokenRBrace): '}' - │ │ └── trailing_trivia (kind: Trivia) - │ │ └── child #0 (kind: TokenNewline). + │ │ └── body (kind: StructBodyBraces) + │ │ ├── lbrace (kind: TerminalLBrace) + │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ ├── token (kind: TokenLBrace): '{' + │ │ │ └── trailing_trivia (kind: Trivia) + │ │ │ └── child #0 (kind: TokenNewline). + │ │ ├── members (kind: MemberList) + │ │ │ ├── item #0 (kind: Member) + │ │ │ │ ├── attributes (kind: AttributeList) [] + │ │ │ │ ├── visibility (kind: VisibilityDefault) [] + │ │ │ │ ├── name (kind: TerminalIdentifier) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) + │ │ │ │ │ │ └── child #0 (kind: TokenWhitespace). + │ │ │ │ │ ├── token (kind: TokenIdentifier): 'x' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ │ └── type_clause (kind: TypeClause) + │ │ │ │ ├── colon (kind: TerminalColon) + │ │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ │ ├── token (kind: TokenColon): ':' + │ │ │ │ │ └── trailing_trivia (kind: Trivia) + │ │ │ │ │ └── child #0 (kind: TokenWhitespace). + │ │ │ │ └── ty (kind: ExprPath) + │ │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] + │ │ │ │ └── segments (kind: ExprPathInner) + │ │ │ │ └── item #0 (kind: PathSegmentSimple) + │ │ │ │ └── ident (kind: TerminalIdentifier) + │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ ├── token (kind: TokenIdentifier): 'a' + │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ ├── separator #0 (kind: TerminalComma) + │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ ├── token (kind: TokenComma): ',' + │ │ │ │ └── trailing_trivia (kind: Trivia) + │ │ │ │ ├── child #0 (kind: TokenWhitespace). + │ │ │ │ ├── child #1 (kind: TokenSingleLineComment): '// Comment.' + │ │ │ │ └── child #2 (kind: TokenNewline). + │ │ │ └── item #1 (kind: Member) + │ │ │ ├── attributes (kind: AttributeList) [] + │ │ │ ├── visibility (kind: VisibilityDefault) [] + │ │ │ ├── name (kind: TerminalIdentifier) + │ │ │ │ ├── leading_trivia (kind: Trivia) + │ │ │ │ │ └── child #0 (kind: TokenWhitespace). + │ │ │ │ ├── token (kind: TokenIdentifier): 'y' + │ │ │ │ └── trailing_trivia (kind: Trivia) [] + │ │ │ └── type_clause (kind: TypeClause) + │ │ │ ├── colon (kind: TerminalColon) + │ │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ │ ├── token (kind: TokenColon): ':' + │ │ │ │ └── trailing_trivia (kind: Trivia) + │ │ │ │ └── child #0 (kind: TokenWhitespace). + │ │ │ └── ty (kind: ExprPath) + │ │ │ ├── dollar (kind: OptionTerminalDollarEmpty) [] + │ │ │ └── segments (kind: ExprPathInner) + │ │ │ └── item #0 (kind: PathSegmentSimple) + │ │ │ └── ident (kind: TerminalIdentifier) + │ │ │ ├── leading_trivia (kind: Trivia) [] + │ │ │ ├── token (kind: TokenIdentifier): 'int' + │ │ │ └── trailing_trivia (kind: Trivia) + │ │ │ └── child #0 (kind: TokenNewline). + │ │ └── rbrace (kind: TerminalRBrace) + │ │ ├── leading_trivia (kind: Trivia) + │ │ │ └── child #0 (kind: TokenWhitespace). + │ │ ├── token (kind: TokenRBrace): '}' + │ │ └── trailing_trivia (kind: Trivia) + │ │ └── child #0 (kind: TokenNewline). │ └── rbrace (kind: TerminalRBrace) │ ├── leading_trivia (kind: Trivia) │ │ └── child #0 (kind: TokenNewline). diff --git a/crates/cairo-lang-parser/src/parser_test_data/partial_trees/attrs b/crates/cairo-lang-parser/src/parser_test_data/partial_trees/attrs index f10f4297d03..ba91988a8db 100644 --- a/crates/cairo-lang-parser/src/parser_test_data/partial_trees/attrs +++ b/crates/cairo-lang-parser/src/parser_test_data/partial_trees/attrs @@ -158,9 +158,10 @@ VariantList │ ├── struct_kw (kind: TokenStruct): 'struct' │ ├── name (kind: TokenIdentifier): 'u256' │ ├── generic_params (kind: OptionWrappedGenericParamListEmpty) [] - │ ├── lbrace (kind: TokenLBrace): '{' - │ ├── members (kind: MemberList) - │ └── rbrace (kind: TokenRBrace): '}' + │ └── body (kind: StructBodyBraces) + │ ├── lbrace (kind: TokenLBrace): '{' + │ ├── members (kind: MemberList) + │ └── rbrace (kind: TokenRBrace): '}' ├── child #3 (kind: FunctionWithBody) │ ├── attributes (kind: AttributeList) │ │ └── child #0 (kind: Attribute) diff --git a/crates/cairo-lang-plugins/src/plugins/utils.rs b/crates/cairo-lang-plugins/src/plugins/utils.rs index 91b47198ad5..6e1a3f5489c 100644 --- a/crates/cairo-lang-plugins/src/plugins/utils.rs +++ b/crates/cairo-lang-plugins/src/plugins/utils.rs @@ -90,7 +90,10 @@ impl<'a> PluginTypeInfo<'a> { let generics = GenericParamsInfo::new(db, struct_ast.generic_params(db)); let interned = generics.param_names.iter().map(|s| SmolStrId::from(db, *s)).collect_vec(); - let members_info = extract_members(db, struct_ast.members(db), &interned); + let ast::StructBody::Braces(body) = struct_ast.body(db) else { + return None; + }; + let members_info = extract_members(db, body.members(db), &interned); Some(Self { name: struct_ast.name(db).text(db).long(db).as_str(), attributes: struct_ast.attributes(db), diff --git a/crates/cairo-lang-semantic/src/items/structure.rs b/crates/cairo-lang-semantic/src/items/structure.rs index 46ba7f900a9..a6b594b31e5 100644 --- a/crates/cairo-lang-semantic/src/items/structure.rs +++ b/crates/cairo-lang-semantic/src/items/structure.rs @@ -8,7 +8,7 @@ use cairo_lang_diagnostics::{Diagnostics, Maybe, MaybeAsRef}; use cairo_lang_filesystem::ids::SmolStrId; use cairo_lang_proc_macros::{DebugWithDb, SemanticObject}; use cairo_lang_syntax::attribute::structured::{Attribute, AttributeListStructurize}; -use cairo_lang_syntax::node::{Terminal, TypedStablePtr, TypedSyntaxNode}; +use cairo_lang_syntax::node::{Terminal, TypedStablePtr, TypedSyntaxNode, ast}; use cairo_lang_utils::Intern; use cairo_lang_utils::ordered_hash_map::OrderedHashMap; use salsa::Database; @@ -109,6 +109,7 @@ fn struct_generic_params_data<'db>( struct StructDefinitionData<'db> { diagnostics: Diagnostics<'db, SemanticDiagnostic<'db>>, members: OrderedHashMap, Member<'db>>, + positional_tys: Option, Visibility)>>, resolver_data: Arc>, } @@ -154,22 +155,44 @@ fn type_members_query<'db>( ) -> Maybe, TypeMember<'db>>>> { match ty.long(db) { TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)) => { - let members = db.concrete_struct_members(*concrete_struct_id)?; - Ok(Some( - members - .iter() - .map(|(name, member)| { - ( - *name, - TypeMember { - ty: member.ty, - visibility: member.visibility, - kind: TypeMemberKind::StructMember(member.id), - }, - ) - }) - .collect(), - )) + if let Some(positional_tys) = + db.concrete_struct_positional_types(*concrete_struct_id)? + { + Ok(Some( + positional_tys + .iter() + .enumerate() + .map(|(i, &(ty, visibility))| { + let name = SmolStrId::from(db, i.to_string()); + ( + name, + TypeMember { + ty, + visibility, + kind: TypeMemberKind::TupleElement(i), + }, + ) + }) + .collect(), + )) + } else { + let members = db.concrete_struct_members(*concrete_struct_id)?; + Ok(Some( + members + .iter() + .map(|(name, member)| { + ( + *name, + TypeMember { + ty: member.ty, + visibility: member.visibility, + kind: TypeMemberKind::StructMember(member.id), + }, + ) + }) + .collect(), + )) + } } TypeLongId::Tuple(tys) => Ok(Some( tys.iter() @@ -217,20 +240,52 @@ fn struct_definition_data<'db>( ); diagnostics.extend(generic_params_data.diagnostics.clone()); - // Members. + // Members (or positional types for tuple structs). let mut members = OrderedHashMap::default(); - for member in struct_ast.members(db).elements(db) { - let feature_restore = - resolver.extend_feature_config_from_item(db, crate_id, &mut diagnostics, &member); - let id = MemberLongId(module_id, member.stable_ptr(db)).intern(db); - let ty = resolve_type(db, &mut diagnostics, &mut resolver, &member.type_clause(db).ty(db)); - let visibility = Visibility::from_ast(db, &mut diagnostics, &member.visibility(db)); - let member_name = member.name(db).text(db); - if let Some(_other_member) = members.insert(member_name, Member { id, ty, visibility }) { - diagnostics - .report(member.stable_ptr(db), StructMemberRedefinition { struct_id, member_name }); + let mut positional_tys: Option, Visibility)>> = None; + match struct_ast.body(db) { + ast::StructBody::Braces(body) => { + for member in body.members(db).elements(db) { + let feature_restore = resolver.extend_feature_config_from_item( + db, + crate_id, + &mut diagnostics, + &member, + ); + let id = MemberLongId(module_id, member.stable_ptr(db)).intern(db); + let ty = resolve_type( + db, + &mut diagnostics, + &mut resolver, + &member.type_clause(db).ty(db), + ); + let visibility = Visibility::from_ast(db, &mut diagnostics, &member.visibility(db)); + let member_name = member.name(db).text(db); + if let Some(_other_member) = + members.insert(member_name, Member { id, ty, visibility }) + { + diagnostics.report( + member.stable_ptr(db), + StructMemberRedefinition { struct_id, member_name }, + ); + } + resolver.restore_feature_config(feature_restore); + } + } + ast::StructBody::Parens(body) => { + positional_tys = Some( + body.members(db) + .elements(db) + .map(|arg_member| { + let ty = + resolve_type(db, &mut diagnostics, &mut resolver, &arg_member.ty(db)); + let visibility = + Visibility::from_ast(db, &mut diagnostics, &arg_member.visibility(db)); + (ty, visibility) + }) + .collect(), + ); } - resolver.restore_feature_config(feature_restore); } // Check fully resolved. @@ -240,9 +295,19 @@ fn struct_definition_data<'db>( for (_, member) in members.iter_mut() { member.ty = inference.rewrite(member.ty).no_err(); } + if let Some(ref mut tys) = positional_tys { + for (ty, _) in tys.iter_mut() { + *ty = inference.rewrite(*ty).no_err(); + } + } let resolver_data = Arc::new(resolver.data); - Ok(StructDefinitionData { diagnostics: diagnostics.build(), members, resolver_data }) + Ok(StructDefinitionData { + diagnostics: diagnostics.build(), + members, + positional_tys, + resolver_data, + }) } /// Query implementation of [StructSemantic::struct_definition_diagnostics]. @@ -277,6 +342,21 @@ fn struct_definition_diagnostics<'db>( diagnostics.report(stable_ptr, NonPhantomTypeContainingPhantomType); } } + if let Some(positional_tys) = data.positional_tys.as_ref() { + if let Ok(struct_ast) = db.module_struct_by_id(struct_id) { + if let ast::StructBody::Parens(body) = struct_ast.body(db) { + for (arg_member, &(ty, _)) in + body.members(db).elements(db).zip(positional_tys.iter()) + { + let stable_ptr = arg_member.stable_ptr(db).untyped(); + add_type_based_diagnostics(db, &mut diagnostics, ty, stable_ptr); + if ty.is_phantom(db) { + diagnostics.report(stable_ptr, NonPhantomTypeContainingPhantomType); + } + } + } + } + } diagnostics.build() } @@ -302,6 +382,35 @@ fn concrete_struct_members<'db>( .collect() } +/// Returns the positional types of a tuple struct, or `None` for braced structs. +#[salsa::tracked(returns(ref))] +fn struct_positional_types<'db>( + db: &'db dyn Database, + struct_id: StructId<'db>, +) -> Maybe, Visibility)>>> { + Ok(struct_definition_data(db, struct_id).maybe_as_ref()?.positional_tys.clone()) +} + +/// Returns the substituted positional types of a concrete tuple struct, or `None` for braced +/// structs. +#[salsa::tracked(returns(ref))] +fn concrete_struct_positional_types<'db>( + db: &'db dyn Database, + concrete_struct_id: ConcreteStructId<'db>, +) -> Maybe, Visibility)>>> { + let generic_params = db.struct_generic_params(concrete_struct_id.struct_id(db))?; + let generic_args = &concrete_struct_id.long(db).generic_args; + let substitution = GenericSubstitution::new(generic_params, generic_args); + let Some(positional_tys) = db.struct_positional_types(concrete_struct_id.struct_id(db))? else { + return Ok(None); + }; + let substituted = positional_tys + .iter() + .map(|&(ty, visibility)| Ok((substitution.substitute(db, ty)?, visibility))) + .collect::>>()?; + Ok(Some(substituted)) +} + /// Trait for struct-related semantic queries. pub trait StructSemantic<'db>: Database { /// Returns the declaration diagnostics of a struct. @@ -366,6 +475,25 @@ pub trait StructSemantic<'db>: Database { ) -> Maybe<&'db OrderedHashMap, semantic::Member<'db>>> { concrete_struct_members(self.as_dyn_database(), concrete_struct_id).maybe_as_ref() } + /// Returns the positional types of a tuple struct, or `None` for braced structs. + fn struct_positional_types( + &'db self, + struct_id: StructId<'db>, + ) -> Maybe, Visibility)>>> { + struct_positional_types(self.as_dyn_database(), struct_id) + .maybe_as_ref() + .map(Option::as_ref) + } + /// Returns the substituted positional types of a concrete tuple struct, or `None` for braced + /// structs. + fn concrete_struct_positional_types( + &'db self, + concrete_struct_id: ConcreteStructId<'db>, + ) -> Maybe, Visibility)>>> { + concrete_struct_positional_types(self.as_dyn_database(), concrete_struct_id) + .maybe_as_ref() + .map(Option::as_ref) + } /// Returns the members of a type keyed by their access name, or `None` for types without /// members. Works for both struct types and tuples. fn type_members( diff --git a/crates/cairo-lang-semantic/src/items/structure_test.rs b/crates/cairo-lang-semantic/src/items/structure_test.rs index 0c3e6111ef1..171de225baf 100644 --- a/crates/cairo-lang-semantic/src/items/structure_test.rs +++ b/crates/cairo-lang-semantic/src/items/structure_test.rs @@ -2,13 +2,16 @@ use cairo_lang_debug::DebugWithDb; use cairo_lang_defs::ids::ModuleItemId; use cairo_lang_filesystem::ids::SmolStrId; use cairo_lang_test_utils::test; -use cairo_lang_utils::extract_matches; +use cairo_lang_utils::{Intern, extract_matches}; use indoc::indoc; use pretty_assertions::assert_eq; +use crate::TypeLongId; use crate::items::module::ModuleSemantic; -use crate::items::structure::StructSemantic; +use crate::items::structure::{StructSemantic, TypeMemberKind}; +use crate::items::visibility::Visibility; use crate::test_utils::{SemanticDatabaseForTesting, setup_test_module}; +use crate::types::{ConcreteStructLongId, ConcreteTypeId}; #[test] fn test_struct() { @@ -78,3 +81,46 @@ fn test_struct() { r#"Attribute { id: "inline", args: ["MyImpl1", "MyImpl2", ] }"# ); } + +#[test] +fn test_tuple_struct() { + let db_val = SemanticDatabaseForTesting::default(); + let db = &db_val; + let (test_module, diagnostics) = setup_test_module( + db, + indoc! {" + struct Point(pub u32, u64) + "}, + ) + .split(); + assert_eq!(diagnostics, ""); + let module_id = test_module.module_id; + + let struct_id = extract_matches!( + db.module_item_by_name(module_id, SmolStrId::from(db, "Point")).unwrap().unwrap(), + ModuleItemId::Struct + ); + + // Tuple structs have no named members. + assert!(db.struct_members(struct_id).unwrap().is_empty()); + + // Positional types are correctly extracted. + let positional_tys = db.struct_positional_types(struct_id).unwrap().unwrap(); + assert_eq!(positional_tys.len(), 2); + assert_eq!(positional_tys[0].1, Visibility::Public); + assert_eq!(positional_tys[1].1, Visibility::Private); + assert_eq!(format!("{:?}", positional_tys[0].0.debug(db)), "core::integer::u32"); + assert_eq!(format!("{:?}", positional_tys[1].0.debug(db)), "core::integer::u64"); + + // type_members returns TupleElement kinds for tuple structs. + let concrete_struct_id = ConcreteStructLongId { struct_id, generic_args: vec![] }.intern(db); + let ty = TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct_id)).intern(db); + let members = db.type_members(ty).unwrap().unwrap(); + assert_eq!(members.len(), 2); + let m0 = members.get(&SmolStrId::from(db, "0")).unwrap(); + let m1 = members.get(&SmolStrId::from(db, "1")).unwrap(); + assert!(matches!(m0.kind, TypeMemberKind::TupleElement(0))); + assert!(matches!(m1.kind, TypeMemberKind::TupleElement(1))); + assert_eq!(m0.visibility, Visibility::Public); + assert_eq!(m1.visibility, Visibility::Private); +} diff --git a/crates/cairo-lang-semantic/src/items/tests/struct b/crates/cairo-lang-semantic/src/items/tests/struct index f873a9fa238..8165ff2cefc 100644 --- a/crates/cairo-lang-semantic/src/items/tests/struct +++ b/crates/cairo-lang-semantic/src/items/tests/struct @@ -137,3 +137,23 @@ error[E2053]: Cannot have array of type "((), ())" that is zero sized. --> lib.cairo:2:5 member: Array<((), ())>, ^^^^^^^^^^^^^^^^^^^^^^^ + +//! > ========================================================================== + +//! > Test tuple struct member access. + +//! > test_runner_name +test_function_diagnostics(expect_diagnostics: false) + +//! > function_code +fn first(p: Point) -> u32 { + p.0 +} + +//! > function_name +first + +//! > module_code +struct Point (pub u32, u64) + +//! > expected_diagnostics diff --git a/crates/cairo-lang-starknet/src/plugin/derive/event.rs b/crates/cairo-lang-starknet/src/plugin/derive/event.rs index ad3c10d9340..e02a6dc4626 100644 --- a/crates/cairo-lang-starknet/src/plugin/derive/event.rs +++ b/crates/cairo-lang-starknet/src/plugin/derive/event.rs @@ -46,7 +46,10 @@ fn handle_struct<'db>( let mut append_members = vec![]; let mut ctor = vec![]; let mut members = vec![]; - for member in struct_ast.members(db).elements(db) { + let ast::StructBody::Braces(body) = struct_ast.body(db) else { + return None; + }; + for member in body.members(db).elements(db) { let member_name = RewriteNode::from_ast_trimmed(&member.name(db)); let member_kind = get_field_kind_for_member(db, diagnostics, &member, EventFieldKind::DataSerde); diff --git a/crates/cairo-lang-starknet/src/plugin/starknet_module/contract.rs b/crates/cairo-lang-starknet/src/plugin/starknet_module/contract.rs index c154c86c128..a77dfdaf02c 100644 --- a/crates/cairo-lang-starknet/src/plugin/starknet_module/contract.rs +++ b/crates/cairo-lang-starknet/src/plugin/starknet_module/contract.rs @@ -274,7 +274,10 @@ fn handle_contract_item<'db, 'a>( &mut data.common, metadata, ); - for member in item_struct.members(db).elements(db) { + let ast::StructBody::Braces(body) = item_struct.body(db) else { + return; + }; + for member in body.members(db).elements(db) { // v0 is not validated here to not create multiple diagnostics. It's already // verified in handle_storage_struct above. if member.has_attr(db, SUBSTORAGE_ATTR) { diff --git a/crates/cairo-lang-starknet/src/plugin/storage.rs b/crates/cairo-lang-starknet/src/plugin/storage.rs index eec653007a4..a4e7c2d13b2 100644 --- a/crates/cairo-lang-starknet/src/plugin/storage.rs +++ b/crates/cairo-lang-starknet/src/plugin/storage.rs @@ -33,7 +33,10 @@ pub fn handle_storage_struct<'db, 'a>( let mut substorage_members_init_code = vec![]; let mut storage_struct_members = vec![]; let configs = struct_members_storage_configs(db, &struct_ast, diagnostics); - for (member, config) in zip_eq(struct_ast.members(db).elements(db), &configs) { + let ast::StructBody::Braces(body) = struct_ast.body(db) else { + return; + }; + for (member, config) in zip_eq(body.members(db).elements(db), &configs) { if config.kind == StorageMemberKind::SubStorage { if let Some((struct_code, init_code)) = get_substorage_member_code(db, &member, metadata) diff --git a/crates/cairo-lang-starknet/src/plugin/storage_interfaces.rs b/crates/cairo-lang-starknet/src/plugin/storage_interfaces.rs index 94c6a37fb9c..1261ed28b51 100644 --- a/crates/cairo-lang-starknet/src/plugin/storage_interfaces.rs +++ b/crates/cairo-lang-starknet/src/plugin/storage_interfaces.rs @@ -595,7 +595,10 @@ fn add_interface_struct_definition<'db>( .into(), )); - for field in struct_ast.members(db).elements(db) { + let ast::StructBody::Braces(body) = struct_ast.body(db) else { + return; + }; + for field in body.members(db).elements(db) { let concrete_node_members_type = storage_node_info.concrete_node_members_type(&field); let field_visibility = if backwards_compatible_storage(metadata.edition) { RewriteNode::text("pub ") @@ -661,8 +664,10 @@ fn add_interface_impl<'db>( .into(), )); - let members = struct_ast.members(db); - let fields = members.elements(db); + let ast::StructBody::Braces(braced_body) = struct_ast.body(db) else { + return; + }; + let fields = braced_body.members(db).elements(db); let mut fields_iter = zip_eq(fields, configs).peekable(); while let Some((field, config)) = fields_iter.next() { let field_name = RewriteNode::from_ast_trimmed(&field.name(db)); @@ -686,7 +691,7 @@ fn add_interface_impl<'db>( &[("object_name".to_string(), struct_name)].into(), )); - for field in struct_ast.members(db).elements(db) { + for field in braced_body.members(db).elements(db) { builder.add_modified(RewriteNode::interpolate_patched( " $field_name$: __$field_name$_value__,\n", &[("field_name".to_string(), RewriteNode::from_ast_trimmed(&field.name(db)))].into(), @@ -833,8 +838,10 @@ pub fn struct_members_storage_configs<'db>( struct_ast: &ast::ItemStruct<'db>, diagnostics: &mut Vec>, ) -> Vec { - struct_ast - .members(db) + let ast::StructBody::Braces(body) = struct_ast.body(db) else { + return vec![]; + }; + body.members(db) .elements(db) .map(|member| get_member_storage_config(db, &member, diagnostics)) .collect() diff --git a/crates/cairo-lang-syntax-codegen/src/cairo_spec.rs b/crates/cairo-lang-syntax-codegen/src/cairo_spec.rs index c5c396c510d..ccef0ba2604 100644 --- a/crates/cairo-lang-syntax-codegen/src/cairo_spec.rs +++ b/crates/cairo-lang-syntax-codegen/src/cairo_spec.rs @@ -456,6 +456,27 @@ pub fn get_spec() -> Vec { .node("type_clause", "TypeClause") ) .add_separated_list("MemberList", "Member", "TerminalComma") + // Positional struct member for tuple struct syntax: `struct Foo(u32, u64)`. + .add_struct(StructBuilder::new("StructArgMember") + .node("visibility", "Visibility") + .node("ty", "Expr") + ) + .add_separated_list("StructArgMemberList", "StructArgMember", "TerminalComma") + // Struct body: either braced `{ fields }` or parenthesized `(types)`. + .add_struct(StructBuilder::new("StructBodyBraces") + .node("lbrace", "TerminalLBrace") + .node("members", "MemberList") + .node("rbrace", "TerminalRBrace") + ) + .add_struct(StructBuilder::new("StructBodyParens") + .node("lparen", "TerminalLParen") + .node("members", "StructArgMemberList") + .node("rparen", "TerminalRParen") + ) + .add_enum(EnumBuilder::new("StructBody") + .node_with_explicit_kind("Braces", "StructBodyBraces") + .node_with_explicit_kind("Parens", "StructBodyParens") + ) .add_struct(StructBuilder::new("Variant") .node("attributes" ,"AttributeList") .key_node("name", "TerminalIdentifier") @@ -678,9 +699,7 @@ pub fn get_spec() -> Vec { .node("struct_kw", "TerminalStruct") .key_node("name", "TerminalIdentifier") .node("generic_params", "OptionWrappedGenericParamList") - .node("lbrace", "TerminalLBrace") - .node("members", "MemberList") - .node("rbrace", "TerminalRBrace") + .node("body", "StructBody") ) .add_struct(StructBuilder::new("ItemEnum") .node("attributes" ,"AttributeList") diff --git a/crates/cairo-lang-syntax/src/node/ast.rs b/crates/cairo-lang-syntax/src/node/ast.rs index 4b60a72e707..1e8ddc43a70 100644 --- a/crates/cairo-lang-syntax/src/node/ast.rs +++ b/crates/cairo-lang-syntax/src/node/ast.rs @@ -12729,6 +12729,492 @@ impl<'db> TypedSyntaxNode<'db> for MemberList<'db> { } } #[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)] +pub struct StructArgMember<'db> { + node: SyntaxNode<'db>, +} +impl<'db> StructArgMember<'db> { + pub const INDEX_VISIBILITY: usize = 0; + pub const INDEX_TY: usize = 1; + pub fn new_green( + db: &'db dyn Database, + visibility: VisibilityGreen<'db>, + ty: ExprGreen<'db>, + ) -> StructArgMemberGreen<'db> { + let children = [visibility.0, ty.0]; + let width = children.into_iter().map(|id: GreenId<'_>| id.long(db).width(db)).sum(); + StructArgMemberGreen( + GreenNode { + kind: SyntaxKind::StructArgMember, + details: GreenNodeDetails::Node { children: children.into(), width }, + } + .intern(db), + ) + } +} +impl<'db> StructArgMember<'db> { + pub fn visibility(&self, db: &'db dyn Database) -> Visibility<'db> { + Visibility::from_syntax_node(db, self.node.get_children(db)[0]) + } + pub fn ty(&self, db: &'db dyn Database) -> Expr<'db> { + Expr::from_syntax_node(db, self.node.get_children(db)[1]) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, salsa::Update, HeapSize)] +pub struct StructArgMemberPtr<'db>(pub SyntaxStablePtrId<'db>); +impl<'db> StructArgMemberPtr<'db> {} +impl<'db> TypedStablePtr<'db> for StructArgMemberPtr<'db> { + type SyntaxNode = StructArgMember<'db>; + fn untyped(self) -> SyntaxStablePtrId<'db> { + self.0 + } + fn lookup(&self, db: &'db dyn Database) -> StructArgMember<'db> { + StructArgMember::from_syntax_node(db, self.0.lookup(db)) + } +} +impl<'db> From> for SyntaxStablePtrId<'db> { + fn from(ptr: StructArgMemberPtr<'db>) -> Self { + ptr.untyped() + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, salsa::Update)] +pub struct StructArgMemberGreen<'db>(pub GreenId<'db>); +impl<'db> TypedSyntaxNode<'db> for StructArgMember<'db> { + const OPTIONAL_KIND: Option = Some(SyntaxKind::StructArgMember); + type StablePtr = StructArgMemberPtr<'db>; + type Green = StructArgMemberGreen<'db>; + fn missing(db: &'db dyn Database) -> Self::Green { + StructArgMemberGreen( + GreenNode { + kind: SyntaxKind::StructArgMember, + details: GreenNodeDetails::Node { + children: [Visibility::missing(db).0, Expr::missing(db).0].into(), + width: TextWidth::default(), + }, + } + .intern(db), + ) + } + fn from_syntax_node(db: &'db dyn Database, node: SyntaxNode<'db>) -> Self { + let kind = node.kind(db); + assert_eq!( + kind, + SyntaxKind::StructArgMember, + "Unexpected SyntaxKind {:?}. Expected {:?}.", + kind, + SyntaxKind::StructArgMember + ); + Self { node } + } + fn cast(db: &'db dyn Database, node: SyntaxNode<'db>) -> Option { + let kind = node.kind(db); + if kind == SyntaxKind::StructArgMember { + Some(Self::from_syntax_node(db, node)) + } else { + None + } + } + fn as_syntax_node(&self) -> SyntaxNode<'db> { + self.node + } + fn stable_ptr(&self, db: &'db dyn Database) -> Self::StablePtr { + StructArgMemberPtr(self.node.stable_ptr(db)) + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)] +pub struct StructArgMemberList<'db>(ElementList<'db, StructArgMember<'db>, 2>); +impl<'db> Deref for StructArgMemberList<'db> { + type Target = ElementList<'db, StructArgMember<'db>, 2>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl<'db> StructArgMemberList<'db> { + pub fn new_green( + db: &'db dyn Database, + children: &[StructArgMemberListElementOrSeparatorGreen<'db>], + ) -> StructArgMemberListGreen<'db> { + let width = children.iter().map(|id| id.id().long(db).width(db)).sum(); + StructArgMemberListGreen( + GreenNode { + kind: SyntaxKind::StructArgMemberList, + details: GreenNodeDetails::Node { + children: children.iter().map(|x| x.id()).collect(), + width, + }, + } + .intern(db), + ) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, salsa::Update, HeapSize)] +pub struct StructArgMemberListPtr<'db>(pub SyntaxStablePtrId<'db>); +impl<'db> TypedStablePtr<'db> for StructArgMemberListPtr<'db> { + type SyntaxNode = StructArgMemberList<'db>; + fn untyped(self) -> SyntaxStablePtrId<'db> { + self.0 + } + fn lookup(&self, db: &'db dyn Database) -> StructArgMemberList<'db> { + StructArgMemberList::from_syntax_node(db, self.0.lookup(db)) + } +} +impl<'db> From> for SyntaxStablePtrId<'db> { + fn from(ptr: StructArgMemberListPtr<'db>) -> Self { + ptr.untyped() + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, salsa::Update)] +pub enum StructArgMemberListElementOrSeparatorGreen<'db> { + Separator(TerminalCommaGreen<'db>), + Element(StructArgMemberGreen<'db>), +} +impl<'db> From> for StructArgMemberListElementOrSeparatorGreen<'db> { + fn from(value: TerminalCommaGreen<'db>) -> Self { + StructArgMemberListElementOrSeparatorGreen::Separator(value) + } +} +impl<'db> From> for StructArgMemberListElementOrSeparatorGreen<'db> { + fn from(value: StructArgMemberGreen<'db>) -> Self { + StructArgMemberListElementOrSeparatorGreen::Element(value) + } +} +impl<'db> StructArgMemberListElementOrSeparatorGreen<'db> { + fn id(&self) -> GreenId<'db> { + match self { + StructArgMemberListElementOrSeparatorGreen::Separator(green) => green.0, + StructArgMemberListElementOrSeparatorGreen::Element(green) => green.0, + } + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, salsa::Update)] +pub struct StructArgMemberListGreen<'db>(pub GreenId<'db>); +impl<'db> TypedSyntaxNode<'db> for StructArgMemberList<'db> { + const OPTIONAL_KIND: Option = Some(SyntaxKind::StructArgMemberList); + type StablePtr = StructArgMemberListPtr<'db>; + type Green = StructArgMemberListGreen<'db>; + fn missing(db: &'db dyn Database) -> Self::Green { + StructArgMemberListGreen( + GreenNode { + kind: SyntaxKind::StructArgMemberList, + details: GreenNodeDetails::Node { + children: [].into(), + width: TextWidth::default(), + }, + } + .intern(db), + ) + } + fn from_syntax_node(db: &'db dyn Database, node: SyntaxNode<'db>) -> Self { + Self(ElementList::new(node)) + } + fn cast(db: &'db dyn Database, node: SyntaxNode<'db>) -> Option { + if node.kind(db) == SyntaxKind::StructArgMemberList { + Some(Self(ElementList::new(node))) + } else { + None + } + } + fn as_syntax_node(&self) -> SyntaxNode<'db> { + self.node + } + fn stable_ptr(&self, db: &'db dyn Database) -> Self::StablePtr { + StructArgMemberListPtr(self.node.stable_ptr(db)) + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)] +pub struct StructBodyBraces<'db> { + node: SyntaxNode<'db>, +} +impl<'db> StructBodyBraces<'db> { + pub const INDEX_LBRACE: usize = 0; + pub const INDEX_MEMBERS: usize = 1; + pub const INDEX_RBRACE: usize = 2; + pub fn new_green( + db: &'db dyn Database, + lbrace: TerminalLBraceGreen<'db>, + members: MemberListGreen<'db>, + rbrace: TerminalRBraceGreen<'db>, + ) -> StructBodyBracesGreen<'db> { + let children = [lbrace.0, members.0, rbrace.0]; + let width = children.into_iter().map(|id: GreenId<'_>| id.long(db).width(db)).sum(); + StructBodyBracesGreen( + GreenNode { + kind: SyntaxKind::StructBodyBraces, + details: GreenNodeDetails::Node { children: children.into(), width }, + } + .intern(db), + ) + } +} +impl<'db> StructBodyBraces<'db> { + pub fn lbrace(&self, db: &'db dyn Database) -> TerminalLBrace<'db> { + TerminalLBrace::from_syntax_node(db, self.node.get_children(db)[0]) + } + pub fn members(&self, db: &'db dyn Database) -> MemberList<'db> { + MemberList::from_syntax_node(db, self.node.get_children(db)[1]) + } + pub fn rbrace(&self, db: &'db dyn Database) -> TerminalRBrace<'db> { + TerminalRBrace::from_syntax_node(db, self.node.get_children(db)[2]) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, salsa::Update, HeapSize)] +pub struct StructBodyBracesPtr<'db>(pub SyntaxStablePtrId<'db>); +impl<'db> StructBodyBracesPtr<'db> {} +impl<'db> TypedStablePtr<'db> for StructBodyBracesPtr<'db> { + type SyntaxNode = StructBodyBraces<'db>; + fn untyped(self) -> SyntaxStablePtrId<'db> { + self.0 + } + fn lookup(&self, db: &'db dyn Database) -> StructBodyBraces<'db> { + StructBodyBraces::from_syntax_node(db, self.0.lookup(db)) + } +} +impl<'db> From> for SyntaxStablePtrId<'db> { + fn from(ptr: StructBodyBracesPtr<'db>) -> Self { + ptr.untyped() + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, salsa::Update)] +pub struct StructBodyBracesGreen<'db>(pub GreenId<'db>); +impl<'db> TypedSyntaxNode<'db> for StructBodyBraces<'db> { + const OPTIONAL_KIND: Option = Some(SyntaxKind::StructBodyBraces); + type StablePtr = StructBodyBracesPtr<'db>; + type Green = StructBodyBracesGreen<'db>; + fn missing(db: &'db dyn Database) -> Self::Green { + StructBodyBracesGreen( + GreenNode { + kind: SyntaxKind::StructBodyBraces, + details: GreenNodeDetails::Node { + children: [ + TerminalLBrace::missing(db).0, + MemberList::missing(db).0, + TerminalRBrace::missing(db).0, + ] + .into(), + width: TextWidth::default(), + }, + } + .intern(db), + ) + } + fn from_syntax_node(db: &'db dyn Database, node: SyntaxNode<'db>) -> Self { + let kind = node.kind(db); + assert_eq!( + kind, + SyntaxKind::StructBodyBraces, + "Unexpected SyntaxKind {:?}. Expected {:?}.", + kind, + SyntaxKind::StructBodyBraces + ); + Self { node } + } + fn cast(db: &'db dyn Database, node: SyntaxNode<'db>) -> Option { + let kind = node.kind(db); + if kind == SyntaxKind::StructBodyBraces { + Some(Self::from_syntax_node(db, node)) + } else { + None + } + } + fn as_syntax_node(&self) -> SyntaxNode<'db> { + self.node + } + fn stable_ptr(&self, db: &'db dyn Database) -> Self::StablePtr { + StructBodyBracesPtr(self.node.stable_ptr(db)) + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)] +pub struct StructBodyParens<'db> { + node: SyntaxNode<'db>, +} +impl<'db> StructBodyParens<'db> { + pub const INDEX_LPAREN: usize = 0; + pub const INDEX_MEMBERS: usize = 1; + pub const INDEX_RPAREN: usize = 2; + pub fn new_green( + db: &'db dyn Database, + lparen: TerminalLParenGreen<'db>, + members: StructArgMemberListGreen<'db>, + rparen: TerminalRParenGreen<'db>, + ) -> StructBodyParensGreen<'db> { + let children = [lparen.0, members.0, rparen.0]; + let width = children.into_iter().map(|id: GreenId<'_>| id.long(db).width(db)).sum(); + StructBodyParensGreen( + GreenNode { + kind: SyntaxKind::StructBodyParens, + details: GreenNodeDetails::Node { children: children.into(), width }, + } + .intern(db), + ) + } +} +impl<'db> StructBodyParens<'db> { + pub fn lparen(&self, db: &'db dyn Database) -> TerminalLParen<'db> { + TerminalLParen::from_syntax_node(db, self.node.get_children(db)[0]) + } + pub fn members(&self, db: &'db dyn Database) -> StructArgMemberList<'db> { + StructArgMemberList::from_syntax_node(db, self.node.get_children(db)[1]) + } + pub fn rparen(&self, db: &'db dyn Database) -> TerminalRParen<'db> { + TerminalRParen::from_syntax_node(db, self.node.get_children(db)[2]) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, salsa::Update, HeapSize)] +pub struct StructBodyParensPtr<'db>(pub SyntaxStablePtrId<'db>); +impl<'db> StructBodyParensPtr<'db> {} +impl<'db> TypedStablePtr<'db> for StructBodyParensPtr<'db> { + type SyntaxNode = StructBodyParens<'db>; + fn untyped(self) -> SyntaxStablePtrId<'db> { + self.0 + } + fn lookup(&self, db: &'db dyn Database) -> StructBodyParens<'db> { + StructBodyParens::from_syntax_node(db, self.0.lookup(db)) + } +} +impl<'db> From> for SyntaxStablePtrId<'db> { + fn from(ptr: StructBodyParensPtr<'db>) -> Self { + ptr.untyped() + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, salsa::Update)] +pub struct StructBodyParensGreen<'db>(pub GreenId<'db>); +impl<'db> TypedSyntaxNode<'db> for StructBodyParens<'db> { + const OPTIONAL_KIND: Option = Some(SyntaxKind::StructBodyParens); + type StablePtr = StructBodyParensPtr<'db>; + type Green = StructBodyParensGreen<'db>; + fn missing(db: &'db dyn Database) -> Self::Green { + StructBodyParensGreen( + GreenNode { + kind: SyntaxKind::StructBodyParens, + details: GreenNodeDetails::Node { + children: [ + TerminalLParen::missing(db).0, + StructArgMemberList::missing(db).0, + TerminalRParen::missing(db).0, + ] + .into(), + width: TextWidth::default(), + }, + } + .intern(db), + ) + } + fn from_syntax_node(db: &'db dyn Database, node: SyntaxNode<'db>) -> Self { + let kind = node.kind(db); + assert_eq!( + kind, + SyntaxKind::StructBodyParens, + "Unexpected SyntaxKind {:?}. Expected {:?}.", + kind, + SyntaxKind::StructBodyParens + ); + Self { node } + } + fn cast(db: &'db dyn Database, node: SyntaxNode<'db>) -> Option { + let kind = node.kind(db); + if kind == SyntaxKind::StructBodyParens { + Some(Self::from_syntax_node(db, node)) + } else { + None + } + } + fn as_syntax_node(&self) -> SyntaxNode<'db> { + self.node + } + fn stable_ptr(&self, db: &'db dyn Database) -> Self::StablePtr { + StructBodyParensPtr(self.node.stable_ptr(db)) + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)] +pub enum StructBody<'db> { + Braces(StructBodyBraces<'db>), + Parens(StructBodyParens<'db>), +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, salsa::Update, HeapSize)] +pub struct StructBodyPtr<'db>(pub SyntaxStablePtrId<'db>); +impl<'db> TypedStablePtr<'db> for StructBodyPtr<'db> { + type SyntaxNode = StructBody<'db>; + fn untyped(self) -> SyntaxStablePtrId<'db> { + self.0 + } + fn lookup(&self, db: &'db dyn Database) -> Self::SyntaxNode { + StructBody::from_syntax_node(db, self.0.lookup(db)) + } +} +impl<'db> From> for SyntaxStablePtrId<'db> { + fn from(ptr: StructBodyPtr<'db>) -> Self { + ptr.untyped() + } +} +impl<'db> From> for StructBodyPtr<'db> { + fn from(value: StructBodyBracesPtr<'db>) -> Self { + Self(value.0) + } +} +impl<'db> From> for StructBodyPtr<'db> { + fn from(value: StructBodyParensPtr<'db>) -> Self { + Self(value.0) + } +} +impl<'db> From> for StructBodyGreen<'db> { + fn from(value: StructBodyBracesGreen<'db>) -> Self { + Self(value.0) + } +} +impl<'db> From> for StructBodyGreen<'db> { + fn from(value: StructBodyParensGreen<'db>) -> Self { + Self(value.0) + } +} +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, salsa::Update)] +pub struct StructBodyGreen<'db>(pub GreenId<'db>); +impl<'db> TypedSyntaxNode<'db> for StructBody<'db> { + const OPTIONAL_KIND: Option = None; + type StablePtr = StructBodyPtr<'db>; + type Green = StructBodyGreen<'db>; + fn missing(db: &'db dyn Database) -> Self::Green { + panic!("No missing variant."); + } + fn from_syntax_node(db: &'db dyn Database, node: SyntaxNode<'db>) -> Self { + let kind = node.kind(db); + match kind { + SyntaxKind::StructBodyBraces => { + StructBody::Braces(StructBodyBraces::from_syntax_node(db, node)) + } + SyntaxKind::StructBodyParens => { + StructBody::Parens(StructBodyParens::from_syntax_node(db, node)) + } + _ => panic!("Unexpected syntax kind {:?} when constructing {}.", kind, "StructBody"), + } + } + fn cast(db: &'db dyn Database, node: SyntaxNode<'db>) -> Option { + let kind = node.kind(db); + match kind { + SyntaxKind::StructBodyBraces => { + Some(StructBody::Braces(StructBodyBraces::from_syntax_node(db, node))) + } + SyntaxKind::StructBodyParens => { + Some(StructBody::Parens(StructBodyParens::from_syntax_node(db, node))) + } + _ => None, + } + } + fn as_syntax_node(&self) -> SyntaxNode<'db> { + match self { + StructBody::Braces(x) => x.as_syntax_node(), + StructBody::Parens(x) => x.as_syntax_node(), + } + } + fn stable_ptr(&self, db: &'db dyn Database) -> Self::StablePtr { + StructBodyPtr(self.as_syntax_node().stable_ptr(db)) + } +} +impl<'db> StructBody<'db> { + /// Checks if a kind of a variant of [StructBody]. + pub fn is_variant(kind: SyntaxKind) -> bool { + matches!(kind, SyntaxKind::StructBodyBraces | SyntaxKind::StructBodyParens) + } +} +#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)] pub struct Variant<'db> { node: SyntaxNode<'db>, } @@ -17255,9 +17741,7 @@ impl<'db> ItemStruct<'db> { pub const INDEX_STRUCT_KW: usize = 2; pub const INDEX_NAME: usize = 3; pub const INDEX_GENERIC_PARAMS: usize = 4; - pub const INDEX_LBRACE: usize = 5; - pub const INDEX_MEMBERS: usize = 6; - pub const INDEX_RBRACE: usize = 7; + pub const INDEX_BODY: usize = 5; pub fn new_green( db: &'db dyn Database, attributes: AttributeListGreen<'db>, @@ -17265,20 +17749,9 @@ impl<'db> ItemStruct<'db> { struct_kw: TerminalStructGreen<'db>, name: TerminalIdentifierGreen<'db>, generic_params: OptionWrappedGenericParamListGreen<'db>, - lbrace: TerminalLBraceGreen<'db>, - members: MemberListGreen<'db>, - rbrace: TerminalRBraceGreen<'db>, + body: StructBodyGreen<'db>, ) -> ItemStructGreen<'db> { - let children = [ - attributes.0, - visibility.0, - struct_kw.0, - name.0, - generic_params.0, - lbrace.0, - members.0, - rbrace.0, - ]; + let children = [attributes.0, visibility.0, struct_kw.0, name.0, generic_params.0, body.0]; let width = children.into_iter().map(|id: GreenId<'_>| id.long(db).width(db)).sum(); ItemStructGreen( GreenNode { @@ -17305,14 +17778,8 @@ impl<'db> ItemStruct<'db> { pub fn generic_params(&self, db: &'db dyn Database) -> OptionWrappedGenericParamList<'db> { OptionWrappedGenericParamList::from_syntax_node(db, self.node.get_children(db)[4]) } - pub fn lbrace(&self, db: &'db dyn Database) -> TerminalLBrace<'db> { - TerminalLBrace::from_syntax_node(db, self.node.get_children(db)[5]) - } - pub fn members(&self, db: &'db dyn Database) -> MemberList<'db> { - MemberList::from_syntax_node(db, self.node.get_children(db)[6]) - } - pub fn rbrace(&self, db: &'db dyn Database) -> TerminalRBrace<'db> { - TerminalRBrace::from_syntax_node(db, self.node.get_children(db)[7]) + pub fn body(&self, db: &'db dyn Database) -> StructBody<'db> { + StructBody::from_syntax_node(db, self.node.get_children(db)[5]) } } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, salsa::Update, HeapSize)] @@ -17353,9 +17820,7 @@ impl<'db> TypedSyntaxNode<'db> for ItemStruct<'db> { TerminalStruct::missing(db).0, TerminalIdentifier::missing(db).0, OptionWrappedGenericParamList::missing(db).0, - TerminalLBrace::missing(db).0, - MemberList::missing(db).0, - TerminalRBrace::missing(db).0, + StructBody::missing(db).0, ] .into(), width: TextWidth::default(), diff --git a/crates/cairo-lang-syntax/src/node/key_fields.rs b/crates/cairo-lang-syntax/src/node/key_fields.rs index 32c3d8c4df2..eb509e457ea 100644 --- a/crates/cairo-lang-syntax/src/node/key_fields.rs +++ b/crates/cairo-lang-syntax/src/node/key_fields.rs @@ -104,6 +104,10 @@ pub fn key_fields_range(kind: SyntaxKind) -> core::ops::Range { // Key fields: name. SyntaxKind::Member => 2..3, SyntaxKind::MemberList => 0..0, + SyntaxKind::StructArgMember => 0..0, + SyntaxKind::StructArgMemberList => 0..0, + SyntaxKind::StructBodyBraces => 0..0, + SyntaxKind::StructBodyParens => 0..0, // Key fields: name. SyntaxKind::Variant => 1..2, SyntaxKind::VariantList => 0..0, diff --git a/crates/cairo-lang-syntax/src/node/kind.rs b/crates/cairo-lang-syntax/src/node/kind.rs index 6214c666c4b..a1c89d35957 100644 --- a/crates/cairo-lang-syntax/src/node/kind.rs +++ b/crates/cairo-lang-syntax/src/node/kind.rs @@ -107,6 +107,10 @@ pub enum SyntaxKind { FunctionSignature, Member, MemberList, + StructArgMember, + StructArgMemberList, + StructBodyBraces, + StructBodyParens, Variant, VariantList, ModuleItemList,