@@ -608,3 +608,106 @@ pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
608608
609609 false
610610}
611+
612+ /// A parsed `panic!` expansion
613+ pub struct PanicExpn < ' tcx > {
614+ /// Span of `panic!(..)`
615+ pub call_site : Span ,
616+ /// Inner `format_args!` expansion
617+ pub format_args : PanicArgsExpn < ' tcx > ,
618+ }
619+
620+ impl PanicExpn < ' tcx > {
621+ /// Parses an expanded `panic!` invocation
622+ pub fn parse ( expr : & ' tcx Expr < ' tcx > ) -> Option < Self > {
623+ if_chain ! {
624+ if let ExprKind :: Block ( block, _) = expr. kind;
625+ if let Some ( init) = block. expr;
626+ if let ExprKind :: Call ( _, [ format_args] ) = init. kind;
627+ let expn_data = expr. span. ctxt( ) . outer_expn_data( ) ;
628+ if let Some ( format_args) = PanicArgsExpn :: parse( format_args) ;
629+ then {
630+ Some ( PanicExpn {
631+ call_site: expn_data. call_site,
632+ format_args,
633+ } )
634+ } else {
635+ None
636+ }
637+ }
638+ }
639+ }
640+
641+ /// A parsed `format_args!` expansion
642+ pub struct PanicArgsExpn < ' tcx > {
643+ /// Span of the first argument, the format string
644+ pub format_string_span : Span ,
645+ /// Values passed after the format string
646+ pub value_args : Vec < & ' tcx Expr < ' tcx > > ,
647+
648+ /// String literal expressions which represent the format string split by "{}"
649+ pub format_string_parts : & ' tcx [ Expr < ' tcx > ] ,
650+ /// Symbols corresponding to [`Self::format_string_parts`]
651+ pub format_string_symbols : Vec < Symbol > ,
652+ /// Expressions like `ArgumentV1::new(arg0, Debug::fmt)`
653+ pub args : & ' tcx [ Expr < ' tcx > ] ,
654+ /// The final argument passed to `Arguments::new_v1_formatted`, if applicable
655+ pub fmt_expr : Option < & ' tcx Expr < ' tcx > > ,
656+ }
657+
658+ impl PanicArgsExpn < ' tcx > {
659+ /// Parses an expanded `format_args!` or `format_args_nl!` invocation
660+ pub fn parse ( expr : & ' tcx Expr < ' tcx > ) -> Option < Self > {
661+ if_chain ! {
662+ if let ExprKind :: AddrOf ( _, _, matched) = expr. kind;
663+ if let ExprKind :: Match ( inner_match, [ arm] , _) = matched. kind;
664+
665+ // `match match`, if you will
666+ if let ExprKind :: Match ( args, [ inner_arm] , _) = inner_match. kind;
667+ if let ExprKind :: Tup ( value_args) = args. kind;
668+ if let Some ( value_args) = value_args
669+ . iter( )
670+ . map( |e| match e. kind {
671+ ExprKind :: AddrOf ( _, _, e) => Some ( e) ,
672+ _ => None ,
673+ } )
674+ . collect( ) ;
675+ if let ExprKind :: Array ( args) = inner_arm. body. kind;
676+
677+ if let ExprKind :: Block ( Block { stmts: [ ] , expr: Some ( expr) , .. } , _) = arm. body. kind;
678+ if let ExprKind :: Call ( _, call_args) = expr. kind;
679+ if let Some ( ( strs_ref, fmt_expr) ) = match call_args {
680+ // Arguments::new_v1
681+ [ strs_ref, _] => Some ( ( strs_ref, None ) ) ,
682+ // Arguments::new_v1_formatted
683+ [ strs_ref, _, fmt_expr] => Some ( ( strs_ref, Some ( fmt_expr) ) ) ,
684+ _ => None ,
685+ } ;
686+ if let ExprKind :: AddrOf ( BorrowKind :: Ref , _, strs_arr) = strs_ref. kind;
687+ if let ExprKind :: Array ( format_string_parts) = strs_arr. kind;
688+ if let Some ( format_string_symbols) = format_string_parts
689+ . iter( )
690+ . map( |e| {
691+ if let ExprKind :: Lit ( lit) = & e. kind {
692+ if let LitKind :: Str ( symbol, _style) = lit. node {
693+ return Some ( symbol) ;
694+ }
695+ }
696+ None
697+ } )
698+ . collect( ) ;
699+ then {
700+ Some ( PanicArgsExpn {
701+ format_string_span: strs_ref. span,
702+ value_args,
703+ format_string_parts,
704+ format_string_symbols,
705+ args,
706+ fmt_expr,
707+ } )
708+ } else {
709+ None
710+ }
711+ }
712+ }
713+ }
0 commit comments