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