@@ -21,6 +21,7 @@ use crate::errors::{
2121
2222use crate :: fluent_generated as fluent;
2323use crate :: parser;
24+ use crate :: parser:: attr:: InnerAttrPolicy ;
2425use rustc_ast as ast;
2526use rustc_ast:: ptr:: P ;
2627use rustc_ast:: token:: { self , Delimiter , Lit , LitKind , TokenKind } ;
@@ -722,6 +723,101 @@ impl<'a> Parser<'a> {
722723 Err ( err)
723724 }
724725
726+ pub ( super ) fn attr_on_non_tail_expr ( & self , expr : & Expr ) {
727+ // Missing semicolon typo error.
728+ let span = self . prev_token . span . shrink_to_hi ( ) ;
729+ let mut err = self . sess . create_err ( ExpectedSemi {
730+ span,
731+ token : self . token . clone ( ) ,
732+ unexpected_token_label : Some ( self . token . span ) ,
733+ sugg : ExpectedSemiSugg :: AddSemi ( span) ,
734+ } ) ;
735+ let attr_span = match & expr. attrs [ ..] {
736+ [ ] => unreachable ! ( ) ,
737+ [ only] => only. span ,
738+ [ first, rest @ ..] => {
739+ for attr in rest {
740+ err. span_label ( attr. span , "" ) ;
741+ }
742+ first. span
743+ }
744+ } ;
745+ err. span_label (
746+ attr_span,
747+ format ! (
748+ "only `;` terminated statements or tail expressions are allowed after {}" ,
749+ if expr. attrs. len( ) == 1 { "this attribute" } else { "these attributes" } ,
750+ ) ,
751+ ) ;
752+ if self . token == token:: Pound
753+ && self . look_ahead ( 1 , |t| t. kind == token:: OpenDelim ( Delimiter :: Bracket ) )
754+ {
755+ // We have
756+ // #[attr]
757+ // expr
758+ // #[not_attr]
759+ // other_expr
760+ err. span_label ( span, "expected `;` here" ) ;
761+ err. multipart_suggestion (
762+ "alternatively, consider surrounding the expression with a block" ,
763+ vec ! [
764+ ( expr. span. shrink_to_lo( ) , "{ " . to_string( ) ) ,
765+ ( expr. span. shrink_to_hi( ) , " }" . to_string( ) ) ,
766+ ] ,
767+ Applicability :: MachineApplicable ,
768+ ) ;
769+ let mut snapshot = self . create_snapshot_for_diagnostic ( ) ;
770+ if let [ attr] = & expr. attrs [ ..]
771+ && let ast:: AttrKind :: Normal ( attr_kind) = & attr. kind
772+ && let [ segment] = & attr_kind. item . path . segments [ ..]
773+ && segment. ident . name == sym:: cfg
774+ && let Ok ( next_attr) = snapshot. parse_attribute ( InnerAttrPolicy :: Forbidden ( None ) )
775+ && let ast:: AttrKind :: Normal ( next_attr_kind) = next_attr. kind
776+ && let [ next_segment] = & next_attr_kind. item . path . segments [ ..]
777+ && segment. ident . name == sym:: cfg
778+ && let Ok ( next_expr) = snapshot. parse_expr ( )
779+ {
780+ // We have for sure
781+ // #[cfg(..)]
782+ // expr
783+ // #[cfg(..)]
784+ // other_expr
785+ // So we suggest using `if cfg!(..) { expr } else if cfg!(..) { other_expr }`.
786+ let margin = self . sess . source_map ( ) . span_to_margin ( next_expr. span ) . unwrap_or ( 0 ) ;
787+ let sugg = vec ! [
788+ ( attr. span. with_hi( segment. span( ) . hi( ) ) , "if cfg!" . to_string( ) ) ,
789+ (
790+ attr_kind. item. args. span( ) . unwrap( ) . shrink_to_hi( ) . with_hi( attr. span. hi( ) ) ,
791+ " {" . to_string( ) ,
792+ ) ,
793+ ( expr. span. shrink_to_lo( ) , " " . to_string( ) ) ,
794+ (
795+ next_attr. span. with_hi( next_segment. span( ) . hi( ) ) ,
796+ "} else if cfg!" . to_string( ) ,
797+ ) ,
798+ (
799+ next_attr_kind
800+ . item
801+ . args
802+ . span( )
803+ . unwrap( )
804+ . shrink_to_hi( )
805+ . with_hi( next_attr. span. hi( ) ) ,
806+ " {" . to_string( ) ,
807+ ) ,
808+ ( next_expr. span. shrink_to_lo( ) , " " . to_string( ) ) ,
809+ ( next_expr. span. shrink_to_hi( ) , format!( "\n {}}}" , " " . repeat( margin) ) ) ,
810+ ] ;
811+ err. multipart_suggestion (
812+ "it seems like you are trying to provide different expressions depending on \
813+ `cfg`, consider using `if cfg!(..)`",
814+ sugg,
815+ Applicability :: MachineApplicable ,
816+ ) ;
817+ }
818+ }
819+ err. emit ( ) ;
820+ }
725821 fn check_too_many_raw_str_terminators ( & mut self , err : & mut Diagnostic ) -> bool {
726822 let sm = self . sess . source_map ( ) ;
727823 match ( & self . prev_token . kind , & self . token . kind ) {
0 commit comments