@@ -340,17 +340,98 @@ pub(crate) fn is_literal_expr(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
340
340
false
341
341
}
342
342
343
+ /// Build a textual representation of an unevaluated constant expression.
344
+ ///
345
+ /// If the const expression is too complex, an underscore `_` is returned.
346
+ /// For const arguments, it's `{ _ }` to be precise.
347
+ /// This means that the output is not necessarily valid Rust code.
348
+ ///
349
+ /// Currently, only
350
+ ///
351
+ /// * literals (optionally with a leading `-`)
352
+ /// * unit `()`
353
+ /// * blocks (`{ … }`) around simple expressions and
354
+ /// * paths without arguments
355
+ ///
356
+ /// are considered simple enough. Simple blocks are included since they are
357
+ /// necessary to disambiguate unit from the unit type.
358
+ /// This list might get extended in the future.
359
+ ///
360
+ /// Without this censoring, in a lot of cases the output would get too large
361
+ /// and verbose. Consider `match` expressions, blocks and deeply nested ADTs.
362
+ /// Further, private and `doc(hidden)` fields of structs would get leaked
363
+ /// since HIR datatypes like the `body` parameter do not contain enough
364
+ /// semantic information for this function to be able to hide them –
365
+ /// at least not without significant performance overhead.
366
+ ///
367
+ /// Whenever possible, prefer to evaluate the constant first and try to
368
+ /// use a different method for pretty-printing. Ideally this function
369
+ /// should only ever be used as a fallback.
343
370
pub ( crate ) fn print_const_expr ( tcx : TyCtxt < ' _ > , body : hir:: BodyId ) -> String {
344
371
let hir = tcx. hir ( ) ;
345
372
let value = & hir. body ( body) . value ;
346
373
347
- let snippet = if !value. span . from_expansion ( ) {
348
- tcx. sess . source_map ( ) . span_to_snippet ( value. span ) . ok ( )
349
- } else {
350
- None
351
- } ;
374
+ #[ derive( PartialEq , Eq ) ]
375
+ enum Classification {
376
+ Literal ,
377
+ Simple ,
378
+ Complex ,
379
+ }
352
380
353
- snippet. unwrap_or_else ( || rustc_hir_pretty:: id_to_string ( & hir, body. hir_id ) )
381
+ use Classification :: * ;
382
+
383
+ fn classify ( expr : & hir:: Expr < ' _ > ) -> Classification {
384
+ match & expr. kind {
385
+ hir:: ExprKind :: Unary ( hir:: UnOp :: Neg , expr) => {
386
+ if matches ! ( expr. kind, hir:: ExprKind :: Lit ( _) ) { Literal } else { Complex }
387
+ }
388
+ hir:: ExprKind :: Lit ( _) => Literal ,
389
+ hir:: ExprKind :: Tup ( [ ] ) => Simple ,
390
+ hir:: ExprKind :: Block ( hir:: Block { stmts : [ ] , expr : Some ( expr) , .. } , _) => {
391
+ if classify ( expr) == Complex { Complex } else { Simple }
392
+ }
393
+ // Paths with a self-type or arguments are too “complex” following our measure since
394
+ // they may leak private fields of structs (with feature `adt_const_params`).
395
+ // Consider: `<Self as Trait<{ Struct { private: () } }>>::CONSTANT`.
396
+ // Paths without arguments are definitely harmless though.
397
+ hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( _, hir:: Path { segments, .. } ) ) => {
398
+ if segments. iter ( ) . all ( |segment| segment. args . is_none ( ) ) { Simple } else { Complex }
399
+ }
400
+ // FIXME: Claiming that those kinds of QPaths are simple is probably not true if the Ty
401
+ // contains const arguments. Is there a *concise* way to check for this?
402
+ hir:: ExprKind :: Path ( hir:: QPath :: TypeRelative ( ..) ) => Simple ,
403
+ // FIXME: Can they contain const arguments and thus leak private struct fields?
404
+ hir:: ExprKind :: Path ( hir:: QPath :: LangItem ( ..) ) => Simple ,
405
+ _ => Complex ,
406
+ }
407
+ }
408
+
409
+ let classification = classify ( value) ;
410
+
411
+ if classification == Literal
412
+ && !value. span . from_expansion ( )
413
+ && let Ok ( snippet) = tcx. sess . source_map ( ) . span_to_snippet ( value. span ) {
414
+ // For literals, we avoid invoking the pretty-printer and use the source snippet instead to
415
+ // preserve certain stylistic choices the user likely made for the sake legibility like
416
+ //
417
+ // * hexadecimal notation
418
+ // * underscores
419
+ // * character escapes
420
+ //
421
+ // FIXME: This passes through `-/*spacer*/0` verbatim.
422
+ snippet
423
+ } else if classification == Simple {
424
+ // Otherwise we prefer pretty-printing to get rid of extraneous whitespace, comments and
425
+ // other formatting artifacts.
426
+ rustc_hir_pretty:: id_to_string ( & hir, body. hir_id )
427
+ } else if tcx. def_kind ( hir. body_owner_def_id ( body) . to_def_id ( ) ) == DefKind :: AnonConst {
428
+ // FIXME: Omit the curly braces if the enclosing expression is an array literal
429
+ // with a repeated element (an `ExprKind::Repeat`) as in such case it
430
+ // would not actually need any disambiguation.
431
+ "{ _ }" . to_owned ( )
432
+ } else {
433
+ "_" . to_owned ( )
434
+ }
354
435
}
355
436
356
437
/// Given a type Path, resolve it to a Type using the TyCtxt
0 commit comments