1- use rustc_ast:: token:: Delimiter ;
2- use rustc_ast:: tokenstream:: { DelimSpan , TokenStream } ;
1+ use rustc_ast:: token:: { Delimiter , Lit , LitKind , TokenKind } ;
2+ use rustc_ast:: tokenstream:: { DelimSpan , TokenStream , TokenTree } ;
33use rustc_ast:: * ;
44use rustc_expand:: base:: * ;
55use rustc_span:: edition:: Edition ;
6- use rustc_span:: { Span , sym} ;
6+ use rustc_span:: { Ident , Span , Symbol , sym} ;
7+
8+ // Use an enum to ensure that no new macro calls are added without also updating the message in the
9+ // optimized path below.
10+ enum InnerCall {
11+ Panic2015 ,
12+ Panic2021 ,
13+ Unreachable2015 ,
14+ Unreachable2021 ,
15+ }
16+
17+ impl InnerCall {
18+ fn symbol ( & self ) -> Symbol {
19+ match self {
20+ Self :: Panic2015 => sym:: panic_2015,
21+ Self :: Panic2021 => sym:: panic_2021,
22+ Self :: Unreachable2015 => sym:: unreachable_2015,
23+ Self :: Unreachable2021 => sym:: unreachable_2021,
24+ }
25+ }
26+ }
727
828/// This expands to either
929/// - `$crate::panic::panic_2015!(...)` or
@@ -19,7 +39,7 @@ pub(crate) fn expand_panic<'cx>(
1939 sp : Span ,
2040 tts : TokenStream ,
2141) -> MacroExpanderResult < ' cx > {
22- let mac = if use_panic_2021 ( sp) { sym :: panic_2021 } else { sym :: panic_2015 } ;
42+ let mac = if use_panic_2021 ( sp) { InnerCall :: Panic2021 } else { InnerCall :: Panic2015 } ;
2343 expand ( mac, cx, sp, tts)
2444}
2545
@@ -32,26 +52,77 @@ pub(crate) fn expand_unreachable<'cx>(
3252 sp : Span ,
3353 tts : TokenStream ,
3454) -> MacroExpanderResult < ' cx > {
35- let mac = if use_panic_2021 ( sp) { sym:: unreachable_2021 } else { sym:: unreachable_2015 } ;
55+ let mac =
56+ if use_panic_2021 ( sp) { InnerCall :: Unreachable2021 } else { InnerCall :: Unreachable2015 } ;
3657 expand ( mac, cx, sp, tts)
3758}
3859
3960fn expand < ' cx > (
40- mac : rustc_span :: Symbol ,
61+ mac : InnerCall ,
4162 cx : & ' cx ExtCtxt < ' _ > ,
4263 sp : Span ,
4364 tts : TokenStream ,
4465) -> MacroExpanderResult < ' cx > {
4566 let sp = cx. with_call_site_ctxt ( sp) ;
4667
68+ // If the call is of the form `panic!(<string literal>)` and there are no formatting arguments
69+ // in the string literal, we can call `core::panicking::panic` to centralize the panic logic.
70+ if tts. len ( ) == 1
71+ && let Some ( TokenTree :: Token ( token, _) ) = tts. get ( 0 )
72+ && let TokenKind :: Literal ( lit) = & token. kind
73+ && let Lit { kind : LitKind :: Str | LitKind :: StrRaw ( _) , symbol, .. } = lit
74+ {
75+ let msg = symbol. as_str ( ) ;
76+ if !msg. contains ( |c| c == '{' || c == '}' ) {
77+ let msg = match mac {
78+ InnerCall :: Panic2015 | InnerCall :: Panic2021 => cx. expr ( sp, ExprKind :: Lit ( * lit) ) ,
79+ InnerCall :: Unreachable2015 | InnerCall :: Unreachable2021 => {
80+ let msg = if msg. contains ( '\\' ) {
81+ let mut buf = String :: with_capacity ( msg. len ( ) ) ;
82+ // Force-inlining here is aggressive but the closure is
83+ // called on every char in the string, so it can be hot in
84+ // programs with many long strings containing escapes.
85+ rustc_literal_escaper:: unescape_str (
86+ msg,
87+ #[ inline( always) ]
88+ |_, res| match res {
89+ Ok ( c) => buf. push ( c) ,
90+ Err ( err) => {
91+ assert ! ( !err. is_fatal( ) , "failed to unescape string literal" )
92+ }
93+ } ,
94+ ) ;
95+ buf
96+ } else {
97+ msg. to_owned ( )
98+ } ;
99+
100+ cx. expr_str (
101+ sp,
102+ Symbol :: intern ( & format ! ( "internal error: entered unreachable code: {msg}" ) ) ,
103+ )
104+ }
105+ } ;
106+
107+ return ExpandResult :: Ready ( MacEager :: expr ( cx. expr_call (
108+ sp,
109+ cx. expr_path ( cx. path_global (
110+ sp,
111+ [ sym:: core, sym:: panicking, sym:: panic] . map ( |sym| Ident :: new ( sym, sp) ) . to_vec ( ) ,
112+ ) ) ,
113+ [ msg] . into ( ) ,
114+ ) ) ) ;
115+ }
116+ }
117+
47118 ExpandResult :: Ready ( MacEager :: expr (
48119 cx. expr (
49120 sp,
50121 ExprKind :: MacCall ( Box :: new ( MacCall {
51122 path : Path {
52123 span : sp,
53124 segments : cx
54- . std_path ( & [ sym:: panic, mac] )
125+ . std_path ( & [ sym:: panic, mac. symbol ( ) ] )
55126 . into_iter ( )
56127 . map ( |ident| PathSegment :: from_ident ( ident) )
57128 . collect ( ) ,
0 commit comments