@@ -17,8 +17,8 @@ use rustc_hir::def_id::{
17
17
CrateNum , DefId , DefIndex , LocalDefId , CRATE_DEF_ID , CRATE_DEF_INDEX , LOCAL_CRATE ,
18
18
} ;
19
19
use rustc_hir:: definitions:: DefPathData ;
20
- use rustc_hir:: intravisit;
21
20
use rustc_hir:: lang_items:: LangItem ;
21
+ use rustc_hir_pretty:: id_to_string;
22
22
use rustc_middle:: middle:: debugger_visualizer:: DebuggerVisualizerFile ;
23
23
use rustc_middle:: middle:: dependency_format:: Linkage ;
24
24
use rustc_middle:: middle:: exported_symbols:: {
@@ -1615,7 +1615,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
1615
1615
record ! ( self . tables. mir_const_qualif[ def_id. to_def_id( ) ] <- qualifs) ;
1616
1616
let body_id = tcx. hir ( ) . maybe_body_owned_by ( def_id) ;
1617
1617
if let Some ( body_id) = body_id {
1618
- let const_data = self . encode_rendered_const_for_body ( body_id) ;
1618
+ let const_data = rendered_const ( self . tcx , body_id) ;
1619
1619
record ! ( self . tables. rendered_const[ def_id. to_def_id( ) ] <- const_data) ;
1620
1620
}
1621
1621
}
@@ -1683,14 +1683,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
1683
1683
}
1684
1684
}
1685
1685
1686
- fn encode_rendered_const_for_body ( & mut self , body_id : hir:: BodyId ) -> String {
1687
- let hir = self . tcx . hir ( ) ;
1688
- let body = hir. body ( body_id) ;
1689
- rustc_hir_pretty:: to_string ( & ( & hir as & dyn intravisit:: Map < ' _ > ) , |s| {
1690
- s. print_expr ( & body. value )
1691
- } )
1692
- }
1693
-
1694
1686
#[ instrument( level = "debug" , skip( self ) ) ]
1695
1687
fn encode_info_for_macro ( & mut self , def_id : LocalDefId ) {
1696
1688
let tcx = self . tcx ;
@@ -2292,3 +2284,97 @@ pub fn provide(providers: &mut Providers) {
2292
2284
..* providers
2293
2285
}
2294
2286
}
2287
+
2288
+ /// Build a textual representation of an unevaluated constant expression.
2289
+ ///
2290
+ /// If the const expression is too complex, an underscore `_` is returned.
2291
+ /// For const arguments, it's `{ _ }` to be precise.
2292
+ /// This means that the output is not necessarily valid Rust code.
2293
+ ///
2294
+ /// Currently, only
2295
+ ///
2296
+ /// * literals (optionally with a leading `-`)
2297
+ /// * unit `()`
2298
+ /// * blocks (`{ … }`) around simple expressions and
2299
+ /// * paths without arguments
2300
+ ///
2301
+ /// are considered simple enough. Simple blocks are included since they are
2302
+ /// necessary to disambiguate unit from the unit type.
2303
+ /// This list might get extended in the future.
2304
+ ///
2305
+ /// Without this censoring, in a lot of cases the output would get too large
2306
+ /// and verbose. Consider `match` expressions, blocks and deeply nested ADTs.
2307
+ /// Further, private and `doc(hidden)` fields of structs would get leaked
2308
+ /// since HIR datatypes like the `body` parameter do not contain enough
2309
+ /// semantic information for this function to be able to hide them –
2310
+ /// at least not without significant performance overhead.
2311
+ ///
2312
+ /// Whenever possible, prefer to evaluate the constant first and try to
2313
+ /// use a different method for pretty-printing. Ideally this function
2314
+ /// should only ever be used as a fallback.
2315
+ pub fn rendered_const < ' tcx > ( tcx : TyCtxt < ' tcx > , body : hir:: BodyId ) -> String {
2316
+ let hir = tcx. hir ( ) ;
2317
+ let value = & hir. body ( body) . value ;
2318
+
2319
+ #[ derive( PartialEq , Eq ) ]
2320
+ enum Classification {
2321
+ Literal ,
2322
+ Simple ,
2323
+ Complex ,
2324
+ }
2325
+
2326
+ use Classification :: * ;
2327
+
2328
+ fn classify ( expr : & hir:: Expr < ' _ > ) -> Classification {
2329
+ match & expr. kind {
2330
+ hir:: ExprKind :: Unary ( hir:: UnOp :: Neg , expr) => {
2331
+ if matches ! ( expr. kind, hir:: ExprKind :: Lit ( _) ) { Literal } else { Complex }
2332
+ }
2333
+ hir:: ExprKind :: Lit ( _) => Literal ,
2334
+ hir:: ExprKind :: Tup ( [ ] ) => Simple ,
2335
+ hir:: ExprKind :: Block ( hir:: Block { stmts : [ ] , expr : Some ( expr) , .. } , _) => {
2336
+ if classify ( expr) == Complex { Complex } else { Simple }
2337
+ }
2338
+ // Paths with a self-type or arguments are too “complex” following our measure since
2339
+ // they may leak private fields of structs (with feature `adt_const_params`).
2340
+ // Consider: `<Self as Trait<{ Struct { private: () } }>>::CONSTANT`.
2341
+ // Paths without arguments are definitely harmless though.
2342
+ hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( _, hir:: Path { segments, .. } ) ) => {
2343
+ if segments. iter ( ) . all ( |segment| segment. args . is_none ( ) ) { Simple } else { Complex }
2344
+ }
2345
+ // FIXME: Claiming that those kinds of QPaths are simple is probably not true if the Ty
2346
+ // contains const arguments. Is there a *concise* way to check for this?
2347
+ hir:: ExprKind :: Path ( hir:: QPath :: TypeRelative ( ..) ) => Simple ,
2348
+ // FIXME: Can they contain const arguments and thus leak private struct fields?
2349
+ hir:: ExprKind :: Path ( hir:: QPath :: LangItem ( ..) ) => Simple ,
2350
+ _ => Complex ,
2351
+ }
2352
+ }
2353
+
2354
+ let classification = classify ( value) ;
2355
+
2356
+ if classification == Literal
2357
+ && !value. span . from_expansion ( )
2358
+ && let Ok ( snippet) = tcx. sess . source_map ( ) . span_to_snippet ( value. span ) {
2359
+ // For literals, we avoid invoking the pretty-printer and use the source snippet instead to
2360
+ // preserve certain stylistic choices the user likely made for the sake legibility like
2361
+ //
2362
+ // * hexadecimal notation
2363
+ // * underscores
2364
+ // * character escapes
2365
+ //
2366
+ // FIXME: This passes through `-/*spacer*/0` verbatim.
2367
+ snippet
2368
+ } else if classification == Simple {
2369
+ // Otherwise we prefer pretty-printing to get rid of extraneous whitespace, comments and
2370
+ // other formatting artifacts.
2371
+ id_to_string ( & hir, body. hir_id )
2372
+ } else if tcx. def_kind ( hir. body_owner_def_id ( body) . to_def_id ( ) ) == DefKind :: AnonConst {
2373
+ // FIXME: Omit the curly braces if the enclosing expression is an array literal
2374
+ // with a repeated element (an `ExprKind::Repeat`) as in such case it
2375
+ // would not actually need any disambiguation.
2376
+ "{ _ }" . to_owned ( )
2377
+ } else {
2378
+ "_" . to_owned ( )
2379
+ }
2380
+ }
0 commit comments