@@ -1023,7 +1023,7 @@ impl<'a> Parser<'a> {
10231023 // we should break everything including floats into more basic proc-macro style
10241024 // tokens in the lexer (probably preferable).
10251025 // See also `TokenKind::break_two_token_op` which does similar splitting of `>>` into `>`.
1026- fn break_up_float ( & mut self , float : Symbol ) -> DestructuredFloat {
1026+ fn break_up_float ( & self , float : Symbol , span : Span ) -> DestructuredFloat {
10271027 #[ derive( Debug ) ]
10281028 enum FloatComponent {
10291029 IdentLike ( String ) ,
@@ -1053,7 +1053,6 @@ impl<'a> Parser<'a> {
10531053 // With proc macros the span can refer to anything, the source may be too short,
10541054 // or too long, or non-ASCII. It only makes sense to break our span into components
10551055 // if its underlying text is identical to our float literal.
1056- let span = self . token . span ;
10571056 let can_take_span_apart =
10581057 || self . span_to_snippet ( span) . as_deref ( ) == Ok ( float_str) . as_deref ( ) ;
10591058
@@ -1115,7 +1114,7 @@ impl<'a> Parser<'a> {
11151114 float : Symbol ,
11161115 suffix : Option < Symbol > ,
11171116 ) -> P < Expr > {
1118- match self . break_up_float ( float) {
1117+ match self . break_up_float ( float, self . token . span ) {
11191118 // 1e2
11201119 DestructuredFloat :: Single ( sym, _sp) => {
11211120 self . parse_expr_tuple_field_access ( lo, base, sym, suffix, None )
@@ -1143,40 +1142,105 @@ impl<'a> Parser<'a> {
11431142 }
11441143 }
11451144
1146- fn parse_field_name_maybe_tuple ( & mut self ) -> PResult < ' a , ThinVec < Ident > > {
1147- let token:: Literal ( token:: Lit { kind : token:: Float , symbol, suffix } ) = self . token . kind
1148- else {
1149- return Ok ( thin_vec ! [ self . parse_field_name( ) ?] ) ;
1150- } ;
1151- Ok ( match self . break_up_float ( symbol) {
1152- // 1e2
1153- DestructuredFloat :: Single ( sym, sp) => {
1154- self . bump ( ) ;
1155- thin_vec ! [ Ident :: new( sym, sp) ]
1156- }
1157- // 1.
1158- DestructuredFloat :: TrailingDot ( sym, sym_span, dot_span) => {
1159- assert ! ( suffix. is_none( ) ) ;
1160- // Analogous to `Self::break_and_eat`
1161- self . break_last_token = true ;
1162- // This might work, in cases like `1. 2`, and might not,
1163- // in cases like `offset_of!(Ty, 1.)`. It depends on what comes
1164- // after the float-like token, and therefore we have to make
1165- // the other parts of the parser think that there is a dot literal.
1166- self . token = Token :: new ( token:: Ident ( sym, false ) , sym_span) ;
1167- self . bump_with ( ( Token :: new ( token:: Dot , dot_span) , self . token_spacing ) ) ;
1168- thin_vec ! [ Ident :: new( sym, sym_span) ]
1169- }
1170- // 1.2 | 1.2e3
1171- DestructuredFloat :: MiddleDot ( symbol1, ident1_span, _dot_span, symbol2, ident2_span) => {
1172- self . bump ( ) ;
1173- thin_vec ! [ Ident :: new( symbol1, ident1_span) , Ident :: new( symbol2, ident2_span) ]
1145+ /// Parse the field access used in offset_of, matched by `$(e:expr)+`.
1146+ /// Currently returns a list of idents. However, it should be possible in
1147+ /// future to also do array indices, which might be arbitrary expressions.
1148+ fn parse_floating_field_access ( & mut self ) -> PResult < ' a , P < [ Ident ] > > {
1149+ let mut fields = Vec :: new ( ) ;
1150+ let mut trailing_dot = None ;
1151+
1152+ loop {
1153+ // This is expected to use a metavariable $(args:expr)+, but the builtin syntax
1154+ // could be called directly. Calling `parse_expr` allows this function to only
1155+ // consider `Expr`s.
1156+ let expr = self . parse_expr ( ) ?;
1157+ let mut current = & expr;
1158+ let start_idx = fields. len ( ) ;
1159+ loop {
1160+ match current. kind {
1161+ ExprKind :: Field ( ref left, right) => {
1162+ // Field access is read right-to-left.
1163+ fields. insert ( start_idx, right) ;
1164+ trailing_dot = None ;
1165+ current = left;
1166+ }
1167+ // Parse this both to give helpful error messages and to
1168+ // verify it can be done with this parser setup.
1169+ ExprKind :: Index ( ref left, ref _right, span) => {
1170+ self . dcx ( ) . emit_err ( errors:: ArrayIndexInOffsetOf ( span) ) ;
1171+ current = left;
1172+ }
1173+ ExprKind :: Lit ( token:: Lit {
1174+ kind : token:: Float | token:: Integer ,
1175+ symbol,
1176+ suffix,
1177+ } ) => {
1178+ if let Some ( suffix) = suffix {
1179+ self . expect_no_tuple_index_suffix ( current. span , suffix) ;
1180+ }
1181+ match self . break_up_float ( symbol, current. span ) {
1182+ // 1e2
1183+ DestructuredFloat :: Single ( sym, sp) => {
1184+ trailing_dot = None ;
1185+ fields. insert ( start_idx, Ident :: new ( sym, sp) ) ;
1186+ }
1187+ // 1.
1188+ DestructuredFloat :: TrailingDot ( sym, sym_span, dot_span) => {
1189+ assert ! ( suffix. is_none( ) ) ;
1190+ trailing_dot = Some ( dot_span) ;
1191+ fields. insert ( start_idx, Ident :: new ( sym, sym_span) ) ;
1192+ }
1193+ // 1.2 | 1.2e3
1194+ DestructuredFloat :: MiddleDot (
1195+ symbol1,
1196+ span1,
1197+ _dot_span,
1198+ symbol2,
1199+ span2,
1200+ ) => {
1201+ trailing_dot = None ;
1202+ fields. insert ( start_idx, Ident :: new ( symbol2, span2) ) ;
1203+ fields. insert ( start_idx, Ident :: new ( symbol1, span1) ) ;
1204+ }
1205+ DestructuredFloat :: Error => {
1206+ trailing_dot = None ;
1207+ fields. insert ( start_idx, Ident :: new ( symbol, self . prev_token . span ) ) ;
1208+ }
1209+ }
1210+ break ;
1211+ }
1212+ ExprKind :: Path ( None , Path { ref segments, .. } ) => {
1213+ match & segments[ ..] {
1214+ [ PathSegment { ident, args : None , .. } ] => {
1215+ trailing_dot = None ;
1216+ fields. insert ( start_idx, * ident)
1217+ }
1218+ _ => {
1219+ self . dcx ( ) . emit_err ( errors:: InvalidOffsetOf ( current. span ) ) ;
1220+ break ;
1221+ }
1222+ }
1223+ break ;
1224+ }
1225+ _ => {
1226+ self . dcx ( ) . emit_err ( errors:: InvalidOffsetOf ( current. span ) ) ;
1227+ break ;
1228+ }
1229+ }
11741230 }
1175- DestructuredFloat :: Error => {
1176- self . bump ( ) ;
1177- thin_vec ! [ Ident :: new( symbol, self . prev_token. span) ]
1231+
1232+ if matches ! ( self . token. kind, token:: CloseDelim ( ..) | token:: Comma ) {
1233+ break ;
1234+ } else if trailing_dot. is_none ( ) {
1235+ // This loop should only repeat if there is a trailing dot.
1236+ self . dcx ( ) . emit_err ( errors:: InvalidOffsetOf ( self . token . span ) ) ;
1237+ break ;
11781238 }
1179- } )
1239+ }
1240+ if let Some ( dot) = trailing_dot {
1241+ self . dcx ( ) . emit_err ( errors:: InvalidOffsetOf ( dot) ) ;
1242+ }
1243+ Ok ( fields. into_iter ( ) . collect ( ) )
11801244 }
11811245
11821246 fn parse_expr_tuple_field_access (
@@ -1907,15 +1971,29 @@ impl<'a> Parser<'a> {
19071971 let container = self . parse_ty ( ) ?;
19081972 self . expect ( & TokenKind :: Comma ) ?;
19091973
1910- let seq_sep = SeqSep { sep : Some ( token:: Dot ) , trailing_sep_allowed : false } ;
1911- let ( fields, _trailing, _recovered) = self . parse_seq_to_before_end (
1912- & TokenKind :: CloseDelim ( Delimiter :: Parenthesis ) ,
1913- seq_sep,
1914- Parser :: parse_field_name_maybe_tuple,
1915- ) ?;
1916- let fields = fields. into_iter ( ) . flatten ( ) . collect :: < Vec < _ > > ( ) ;
1974+ let fields = self . parse_floating_field_access ( ) ?;
1975+ let trailing_comma = self . eat_noexpect ( & TokenKind :: Comma ) ;
1976+
1977+ if let Err ( mut e) =
1978+ self . expect_one_of ( & [ ] , & [ TokenKind :: CloseDelim ( Delimiter :: Parenthesis ) ] )
1979+ {
1980+ if trailing_comma {
1981+ e. note ( "unexpected third argument to offset_of" ) ;
1982+ } else {
1983+ e. note ( "offset_of expects dot-separated field and variant names" ) ;
1984+ }
1985+ e. emit ( ) ;
1986+ }
1987+
1988+ // Eat tokens until the macro call ends.
1989+ if self . may_recover ( ) {
1990+ while !matches ! ( self . token. kind, token:: CloseDelim ( ..) | token:: Eof ) {
1991+ self . bump ( ) ;
1992+ }
1993+ }
1994+
19171995 let span = lo. to ( self . token . span ) ;
1918- Ok ( self . mk_expr ( span, ExprKind :: OffsetOf ( container, fields. into ( ) ) ) )
1996+ Ok ( self . mk_expr ( span, ExprKind :: OffsetOf ( container, fields) ) )
19191997 }
19201998
19211999 /// Returns a string literal if the next token is a string literal.
0 commit comments