@@ -150,13 +150,14 @@ fn maybe_append(mut lhs: Vec<Attribute>, rhs: Option<Vec<Attribute>>)
150150 lhs
151151}
152152
153- #[ derive( Clone , PartialEq ) ]
153+ #[ derive( Clone , Copy , PartialEq ) ]
154154enum PrevTokenKind {
155155 DocComment ,
156156 Comma ,
157157 Plus ,
158158 Interpolated ,
159159 Eof ,
160+ Ident ,
160161 Other ,
161162}
162163
@@ -1040,6 +1041,7 @@ impl<'a> Parser<'a> {
10401041 token:: BinOp ( token:: Plus ) => PrevTokenKind :: Plus ,
10411042 token:: Interpolated ( ..) => PrevTokenKind :: Interpolated ,
10421043 token:: Eof => PrevTokenKind :: Eof ,
1044+ token:: Ident ( ..) => PrevTokenKind :: Ident ,
10431045 _ => PrevTokenKind :: Other ,
10441046 } ;
10451047
@@ -1079,6 +1081,16 @@ impl<'a> Parser<'a> {
10791081 None => token:: CloseDelim ( self . token_cursor . frame . delim ) ,
10801082 } )
10811083 }
1084+ fn look_ahead_span ( & self , dist : usize ) -> Span {
1085+ if dist == 0 {
1086+ return self . span
1087+ }
1088+
1089+ match self . token_cursor . frame . tree_cursor . look_ahead ( dist - 1 ) {
1090+ Some ( TokenTree :: Token ( span, _) ) | Some ( TokenTree :: Delimited ( span, _) ) => span,
1091+ None => self . look_ahead_span ( dist - 1 ) ,
1092+ }
1093+ }
10821094 pub fn fatal ( & self , m : & str ) -> DiagnosticBuilder < ' a > {
10831095 self . sess . span_diagnostic . struct_span_fatal ( self . span , m)
10841096 }
@@ -2777,10 +2789,15 @@ impl<'a> Parser<'a> {
27772789 self . expected_tokens . push ( TokenType :: Operator ) ;
27782790 while let Some ( op) = AssocOp :: from_token ( & self . token ) {
27792791
2780- let lhs_span = if self . prev_token_kind == PrevTokenKind :: Interpolated {
2781- self . prev_span
2782- } else {
2783- lhs. span
2792+ // Adjust the span for interpolated LHS to point to the `$lhs` token and not to what
2793+ // it refers to. Interpolated identifiers are unwrapped early and never show up here
2794+ // as `PrevTokenKind::Interpolated` so if LHS is a single identifier we always process
2795+ // it as "interpolated", it doesn't change the answer for non-interpolated idents.
2796+ let lhs_span = match ( self . prev_token_kind , & lhs. node ) {
2797+ ( PrevTokenKind :: Interpolated , _) => self . prev_span ,
2798+ ( PrevTokenKind :: Ident , & ExprKind :: Path ( None , ref path) )
2799+ if path. segments . len ( ) == 1 => self . prev_span ,
2800+ _ => lhs. span ,
27842801 } ;
27852802
27862803 let cur_op_span = self . span ;
@@ -2798,13 +2815,10 @@ impl<'a> Parser<'a> {
27982815 }
27992816 // Special cases:
28002817 if op == AssocOp :: As {
2801- // Save the state of the parser before parsing type normally, in case there is a
2802- // LessThan comparison after this cast.
2803- lhs = self . parse_assoc_op_as ( lhs, lhs_span) ?;
2818+ lhs = self . parse_assoc_op_cast ( lhs, lhs_span, ExprKind :: Cast ) ?;
28042819 continue
28052820 } else if op == AssocOp :: Colon {
2806- let rhs = self . parse_ty_no_plus ( ) ?;
2807- lhs = self . mk_expr ( lhs_span. to ( rhs. span ) , ExprKind :: Type ( lhs, rhs) , ThinVec :: new ( ) ) ;
2821+ lhs = self . parse_assoc_op_cast ( lhs, lhs_span, ExprKind :: Type ) ?;
28082822 continue
28092823 } else if op == AssocOp :: DotDot || op == AssocOp :: DotDotDot {
28102824 // If we didn’t have to handle `x..`/`x...`, it would be pretty easy to
@@ -2898,61 +2912,61 @@ impl<'a> Parser<'a> {
28982912 Ok ( lhs)
28992913 }
29002914
2901- fn parse_assoc_op_as ( & mut self , lhs : P < Expr > , lhs_span : Span ) -> PResult < ' a , P < Expr > > {
2902- let rp = self . clone ( ) ;
2915+ fn parse_assoc_op_cast ( & mut self , lhs : P < Expr > , lhs_span : Span ,
2916+ expr_kind : fn ( P < Expr > , P < Ty > ) -> ExprKind )
2917+ -> PResult < ' a , P < Expr > > {
2918+ let mk_expr = |this : & mut Self , rhs : P < Ty > | {
2919+ this. mk_expr ( lhs_span. to ( rhs. span ) , expr_kind ( lhs, rhs) , ThinVec :: new ( ) )
2920+ } ;
2921+
2922+ // Save the state of the parser before parsing type normally, in case there is a
2923+ // LessThan comparison after this cast.
2924+ let parser_snapshot_before_type = self . clone ( ) ;
29032925 match self . parse_ty_no_plus ( ) {
29042926 Ok ( rhs) => {
2905- Ok ( self . mk_expr ( lhs_span. to ( rhs. span ) ,
2906- ExprKind :: Cast ( lhs, rhs) ,
2907- ThinVec :: new ( ) ) )
2927+ Ok ( mk_expr ( self , rhs) )
29082928 }
2909- Err ( mut err) => {
2910- let rp_err = self . clone ( ) ;
2911- let sp = rp_err. span . clone ( ) ;
2929+ Err ( mut type_err) => {
2930+ // Rewind to before attempting to parse the type with generics, to recover
2931+ // from situations like `x as usize < y` in which we first tried to parse
2932+ // `usize < y` as a type with generic arguments.
2933+ let parser_snapshot_after_type = self . clone ( ) ;
2934+ mem:: replace ( self , parser_snapshot_before_type) ;
29122935
2913- // Rewind to before attempting to parse the type with generics, to get
2914- // arround #22644.
2915- mem:: replace ( self , rp) ;
2916- let lo = self . span ;
29172936 match self . parse_path_without_generics ( PathStyle :: Type ) {
29182937 Ok ( path) => {
2919- // Successfully parsed the type leaving a `<` yet to parse
2920- err. cancel ( ) ;
2921- let codemap = self . sess . codemap ( ) ;
2922- let suggestion_span = lhs_span. to ( self . prev_span ) ;
2923- let warn_message = match codemap. span_to_snippet ( self . prev_span ) {
2924- Ok ( lstring) => format ! ( "`{}`" , lstring) ,
2925- _ => "a type" . to_string ( ) ,
2926- } ;
2938+ // Successfully parsed the type path leaving a `<` yet to parse.
2939+ type_err. cancel ( ) ;
2940+
2941+ // Report non-fatal diagnostics, keep `x as usize` as an expression
2942+ // in AST and continue parsing.
29272943 let msg = format ! ( "`<` is interpreted as a start of generic \
2928- arguments for {} , not a comparison",
2929- warn_message ) ;
2930- let mut err = self . sess . span_diagnostic . struct_span_err ( sp , & msg ) ;
2931- err . span_label ( sp , "interpreted as generic argument " ) ;
2944+ arguments for `{}` , not a comparison", path ) ;
2945+ let mut err = self . sess . span_diagnostic . struct_span_err ( self . span , & msg ) ;
2946+ err. span_label ( self . look_ahead_span ( 1 ) . to ( parser_snapshot_after_type . span ) ,
2947+ "interpreted as generic arguments " ) ;
29322948 err. span_label ( self . span , "not interpreted as comparison" ) ;
2933- let suggestion = match codemap. span_to_snippet ( suggestion_span) {
2934- Ok ( lstring) => format ! ( "({})" , lstring) ,
2935- _ => format ! ( "(<expression> as <type>)" )
2936- } ;
2937- err. span_suggestion ( suggestion_span,
2949+
2950+ let expr = mk_expr ( self , P ( Ty {
2951+ span : path. span ,
2952+ node : TyKind :: Path ( None , path) ,
2953+ id : ast:: DUMMY_NODE_ID
2954+ } ) ) ;
2955+
2956+ let expr_str = self . sess . codemap ( ) . span_to_snippet ( expr. span )
2957+ . unwrap_or ( pprust:: expr_to_string ( & expr) ) ;
2958+ err. span_suggestion ( expr. span ,
29382959 "if you want to compare the casted value then write:" ,
2939- suggestion ) ;
2960+ format ! ( "({})" , expr_str ) ) ;
29402961 err. emit ( ) ;
29412962
2942- let path = TyKind :: Path ( None , path) ;
2943- let span = lo. to ( self . prev_span ) ;
2944- let rhs = P ( Ty { node : path, span : span, id : ast:: DUMMY_NODE_ID } ) ;
2945- // Letting the parser accept the recovered type to avoid further errors,
2946- // but the code will still not compile due to the error emitted above.
2947- Ok ( self . mk_expr ( lhs_span. to ( rhs. span ) ,
2948- ExprKind :: Cast ( lhs, rhs) ,
2949- ThinVec :: new ( ) ) )
2963+ Ok ( expr)
29502964 }
29512965 Err ( mut path_err) => {
2952- // Still couldn 't parse, return original error and parser state
2966+ // Couldn 't parse as a path , return original error and parser state.
29532967 path_err. cancel ( ) ;
2954- mem:: replace ( self , rp_err ) ;
2955- Err ( err )
2968+ mem:: replace ( self , parser_snapshot_after_type ) ;
2969+ Err ( type_err )
29562970 }
29572971 }
29582972 }
0 commit comments