diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 0bd72625f1509..f3e185c75c743 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -22,6 +22,8 @@ rustdoc-json-types = { path = "../rustdoc-json-types" } tracing = "0.1" tracing-tree = "0.2.0" once_cell = "1.10.0" +# @Note hmmmmm +# rustc_apfloat = { path = "../../compiler/rustc_apfloat" } [dependencies.tracing-subscriber] version = "0.3.3" diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 2d364f3402e96..e93bf588ce5c3 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -36,7 +36,7 @@ use rustc_typeck::check::intrinsic::intrinsic_operation_unsafety; use crate::clean::cfg::Cfg; use crate::clean::external_path; use crate::clean::inline::{self, print_inlined_const}; -use crate::clean::utils::{is_literal_expr, print_const_expr, print_evaluated_const}; +use crate::clean::utils::is_literal_expr; use crate::clean::Clean; use crate::core::DocContext; use crate::formats::cache::Cache; @@ -2338,6 +2338,10 @@ impl Constant { self.kind.value(tcx) } + pub(crate) fn value_html(&self, cx: &Context<'_>) -> Option { + self.kind.value_html(cx) + } + pub(crate) fn is_literal(&self, tcx: TyCtxt<'_>) -> bool { self.kind.is_literal(tcx) } @@ -2349,7 +2353,7 @@ impl ConstantKind { ConstantKind::TyConst { ref expr } => expr.clone(), ConstantKind::Extern { def_id } => print_inlined_const(tcx, def_id), ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => { - print_const_expr(tcx, body) + super::utils::print_const_expr(tcx, body) } } } @@ -2358,7 +2362,16 @@ impl ConstantKind { match *self { ConstantKind::TyConst { .. } | ConstantKind::Anonymous { .. } => None, ConstantKind::Extern { def_id } | ConstantKind::Local { def_id, .. } => { - print_evaluated_const(tcx, def_id) + super::utils::print_evaluated_const(tcx, def_id) + } + } + } + + pub(crate) fn value_html(&self, cx: &Context<'_>) -> Option { + match *self { + ConstantKind::TyConst { .. } | ConstantKind::Anonymous { .. } => None, + ConstantKind::Extern { def_id } | ConstantKind::Local { def_id, .. } => { + super::utils::print_evaluated_const_html(def_id, cx) } } } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index fa449a21b0c93..e9aebc6d4198e 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -276,6 +276,26 @@ pub(crate) fn print_evaluated_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option anymore +pub(crate) fn print_evaluated_const_html( + def_id: DefId, + cx: &crate::html::render::Context<'_>, +) -> Option { + cx.tcx().const_eval_poly(def_id).ok().and_then(|val| { + let mut buffer = String::new(); + // @Task don't call into that module! + crate::html::format::pretty_const::format_const_value( + &mut buffer, + val, + cx.tcx().type_of(def_id), + cx, + ) + .ok()?; + Some(buffer) + }) +} + fn format_integer_with_underscore_sep(num: &str) -> String { let num_chars: Vec<_> = num.chars().collect(); let mut num_start_index = if num_chars.get(0) == Some(&'-') { 1 } else { 0 }; @@ -302,6 +322,7 @@ fn format_integer_with_underscore_sep(num: &str) -> String { .collect() } +#[allow(dead_code)] // @Temporary fn print_const_with_custom_print_scalar(tcx: TyCtxt<'_>, ct: mir::ConstantKind<'_>) -> String { // Use a slightly different format for integer types which always shows the actual value. // For all other types, fallback to the original `pretty_print_const`. diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 36a47b05cb9d6..bd662e8bf8ced 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -654,6 +654,13 @@ pub(crate) fn href_with_root_path( // documented on their parent's page tcx.parent(did) } + DefKind::Field => { + let parent_id = tcx.parent(did); + match tcx.def_kind(parent_id) { + DefKind::Variant => tcx.parent(parent_id), + _ => parent_id, + } + } _ => did, }; let cache = cx.cache(); @@ -1670,6 +1677,558 @@ impl clean::types::Term { } } +pub(crate) mod pretty_const { + #![allow(dead_code, unused_variables, unused_imports)] // @Temporary + + use crate::{ + formats::item_type::ItemType, + html::{escape::Escape, render::Context}, + }; + use rustc_hir::{ + def::{CtorKind, DefKind}, + def_id::DefId, + }; + use rustc_middle::{ + mir::{ + interpret::{AllocRange, ConstValue, Pointer, Scalar}, + ConstantKind, + }, + ty::{ + self, subst::GenericArg, util::is_doc_hidden, Const, ConstInt, DefIdTree, ParamConst, + ScalarInt, Ty, TypeFoldable, TypeVisitable, Visibility, + }, + }; + use rustc_target::abi::Size; + use std::fmt::{Error, Write}; + + fn format_constant_kind<'tcx>( + buffer: &mut String, + ct: ConstantKind<'tcx>, + cx: &Context<'tcx>, + ) -> Result<(), Error> { + match ct { + ConstantKind::Ty(ct) => format_const(buffer, ct, cx), + ConstantKind::Val(ct, ty) => format_const_value(buffer, ct, ty, cx), + } + } + + pub(crate) fn format_const<'tcx>( + buffer: &mut String, + ct: Const<'tcx>, + cx: &Context<'tcx>, + ) -> Result<(), Error> { + match ct.kind() { + ty::ConstKind::Unevaluated(ty::Unevaluated { + def, + substs, + promoted: Some(promoted), + }) => { + // @Question do we want it to print like that? + format_path(buffer, def.did, substs, cx)?; + write!(buffer, "::{:?}", promoted) + } + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted: None }) => { + match cx.tcx().def_kind(def.did) { + DefKind::Static(..) | DefKind::Const | DefKind::AssocConst => { + format_path(buffer, def.did, substs, cx) + } + _ => { + if def.is_local() { + let span = cx.tcx().def_span(def.did); + if let Ok(snip) = cx.tcx().sess.source_map().span_to_snippet(span) { + write!(buffer, "{}", snip) + } else { + write!(buffer, "_") + } + } else { + write!(buffer, "_") + } + } + } + } + ty::ConstKind::Param(ParamConst { name, .. }) => write!(buffer, "{}", name), + // @Beacon @Beacon @Task + // ty::ConstKind::Value(value) => format_const_value(buffer, value, ct.ty(), cx), + ty::ConstKind::Value(_value) => write!(buffer, "$VALTREE$"), // @Temporary + ty::ConstKind::Infer(_) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Error(_) => write!(buffer, "_"), + } + } + + const ELLIPSIS: &str = r#""#; + + pub(crate) fn format_const_value<'tcx>( + buffer: &mut String, + ct: ConstValue<'tcx>, + ty: Ty<'tcx>, + cx: &Context<'tcx>, + ) -> Result<(), Error> { + let tcx = cx.tcx(); + // let ct = tcx.lift(ct).unwrap(); // @Question necessary? + // let ty = tcx.lift(ty).unwrap(); + let u8_type = tcx.types.u8; + + match (ct, ty.kind()) { + // Byte/string slices, printed as (byte) string literals. + (ConstValue::Slice { data, start, end }, ty::Ref(_, inner, _)) => { + match inner.kind() { + ty::Slice(t) => { + if *t == u8_type { + // The `inspect` here is okay since we checked the bounds, and there are + // no relocations (we have an active slice reference here). We don't use + // this result to affect interpreter execution. + let byte_str = data + .inner() + .inspect_with_uninit_and_ptr_outside_interpreter(start..end); + return format_byte_str(buffer, byte_str); + } + } + ty::Str => { + // The `inspect` here is okay since we checked the bounds, and there are no + // relocations (we have an active `str` reference here). We don't use this + // result to affect interpreter execution. + let slice = data + .inner() + .inspect_with_uninit_and_ptr_outside_interpreter(start..end); + + // @Task better name, better limit + const LIMIT: usize = 80; + + return if slice.len() > LIMIT { + write!(buffer, r#""{ELLIPSIS}""#) + } else { + // @Task improve perf + write!( + buffer, + "{}", + Escape(&format!("{:?}", String::from_utf8_lossy(slice))) + ) + }; + } + _ => {} + } + } + (ConstValue::ByRef { alloc, offset }, ty::Array(t, n)) if *t == u8_type => { + let n = n.kind().try_to_bits(tcx.data_layout.pointer_size).unwrap(); + // cast is ok because we already checked for pointer size (32 or 64 bit) above + let range = AllocRange { start: offset, size: Size::from_bytes(n) }; + let byte_str = alloc.inner().get_bytes(&tcx, range).unwrap(); + write!(buffer, "*")?; + return format_byte_str(buffer, byte_str); + } + + // Aggregates, printed as array/tuple/struct/variant construction syntax. + // + // NB: the `has_param_types_or_consts` check ensures that we can use + // the `destructure_const` query with an empty `ty::ParamEnv` without + // introducing ICEs (e.g. via `layout_of`) from missing bounds. + // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized` + // to be able to destructure the tuple into `(0u8, *mut T) + (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_param_types_or_consts() => { + // let ct = tcx.lift(ct).unwrap(); + // let ty = tcx.lift(ty).unwrap(); + let Some(contents) = tcx.try_destructure_mir_constant( + ty::ParamEnv::reveal_all() + .and(ConstantKind::Val(ct, ty)) + ) else { + // Fall back to debug pretty printing for invalid constants. + return write!(buffer, "{:?}", ct); + }; + + // @Task better name + const LIMIT: usize = 12; + + let mut fields = contents.fields.iter().copied(); + match *ty.kind() { + ty::Array(..) => { + write!(buffer, "[")?; + + if contents.fields.len() > LIMIT { + write!(buffer, "{ELLIPSIS}")?; + } else if let Some(first) = fields.next() { + format_constant_kind(buffer, first, cx)?; + for field in fields { + buffer.write_str(", ")?; + format_constant_kind(buffer, field, cx)?; + } + } + + write!(buffer, "]")?; + } + ty::Tuple(..) => { + write!(buffer, "(")?; + if let Some(first) = fields.next() { + format_constant_kind(buffer, first, cx)?; + for field in fields { + buffer.write_str(", ")?; + format_constant_kind(buffer, field, cx)?; + } + } + if contents.fields.len() == 1 { + write!(buffer, ",")?; + } + write!(buffer, ")")?; + } + // @Question should we get rid of this? when is this reachable? + ty::Adt(def, _) if def.variants().is_empty() => { + typed_value( + buffer, + |buffer| write!(buffer, "unreachable()"), + |buffer| format_type(buffer, ty), + ": ", + )?; + } + ty::Adt(def, substs) => { + let document_private = cx.shared.document_private; + let document_hidden = cx.shared.document_hidden; + + let variant_idx = + contents.variant.expect("destructed const of adt without variant idx"); + let variant_def = &def.variant(variant_idx); + format_path(buffer, variant_def.def_id, substs, cx)?; + + match variant_def.ctor_kind { + CtorKind::Const => {} + CtorKind::Fn => { + write!(buffer, "(")?; + let mut first = true; + for (field_def, field) in + std::iter::zip(&variant_def.fields, fields) + { + if !first { + write!(buffer, ", ")?; + } + first = false; + + // @Question should I use + // cache.access_levels.is_public(did) + // cache.document_private + // @Question is the visibility-check correct? + if is_doc_hidden(tcx, field_def.did) && !document_hidden + || field_def.vis != Visibility::Public && !document_private + { + write!(buffer, "_")?; + continue; + } + + format_constant_kind(buffer, field, cx)?; + } + // @Beacon @Beacon @Question should we also print sth if + // the thingy is `#[non_exhaustive]`? + write!(buffer, ")")?; + } + CtorKind::Fictive => { + write!(buffer, " {{ ")?; + let mut first = true; + let mut contains_private_or_hidden_fields = false; + for (field_def, field) in + std::iter::zip(&variant_def.fields, fields) + { + // @Question is the visibility-check correct? + // @Question should I use + // cache.access_levels.is_public(did) + // cache.document_private + if is_doc_hidden(tcx, field_def.did) && !document_hidden + || field_def.vis != Visibility::Public && !document_private + { + contains_private_or_hidden_fields = true; + continue; + } + + if !first { + write!(buffer, ", ")?; + } + first = false; + + match super::href(field_def.did, cx) { + Ok((mut url, ..)) => { + write!(url, "#")?; + let parent_id = tcx.parent(field_def.did); + if tcx.def_kind(parent_id) == DefKind::Variant { + write!( + url, + "{}.{}.field", + ItemType::Variant, + tcx.item_name(parent_id) + ) + } else { + write!(url, "{}", ItemType::StructField) + }?; + + write!(url, ".{}", field_def.name)?; + + write!( + buffer, + r#"{}"#, + ItemType::StructField, + url, + field_def.name, + field_def.name, + ) + } + Err(_) => write!(buffer, "{}", field_def.name), + }?; + + write!(buffer, ": ")?; + + format_constant_kind(buffer, field, cx)?; + } + + // @Beacon @Beacon @Question should we also print ellipses if + // the thingy is `#[non_exhaustive]`? + if contains_private_or_hidden_fields { + if !first { + write!(buffer, ", ")?; + } + write!(buffer, "..")?; + } + + write!(buffer, " }}")?; + } + } + } + _ => unreachable!(), + } + + return Ok(()); + } + + (ConstValue::Scalar(scalar), _) => { + return format_const_scalar(buffer, scalar, ty, cx); + } + (ConstValue::ZeroSized, ty::FnDef(d, s)) => { + return format_path(buffer, *d, s, cx); + } + + // FIXME(oli-obk, fmease): also pretty print arrays and other aggregate constants by + // reading their fields instead of just dumping the memory. + _ => {} + } + + // @Question should we get rid of this? + // fallback + write!(buffer, "{:?}", ct)?; + + Ok(()) + } + + fn format_path<'tcx>( + buffer: &mut String, + def_id: DefId, + substs: &'tcx [GenericArg<'tcx>], + cx: &Context<'tcx>, + ) -> Result<(), Error> { + let tcx = cx.tcx(); + + if let Ok((mut url, item_type, path)) = super::href(def_id, cx) { + let mut needs_fragment = true; + let item_type = match tcx.def_kind(def_id) { + DefKind::AssocFn => match tcx.associated_item(def_id).defaultness.has_value() { + true => ItemType::Method, + false => ItemType::TyMethod, + }, + DefKind::AssocTy => ItemType::AssocType, + DefKind::AssocConst => ItemType::AssocConst, + DefKind::Variant => ItemType::Variant, + _ => { + needs_fragment = false; + item_type + } + }; + + let name = tcx.item_name(def_id); + let mut path = super::join_with_double_colon(&path); + + if needs_fragment { + write!(url, "#{}.{}", item_type, name)?; + write!(path, "::{}", name)?; + } + + write!( + buffer, + r#"{}"#, + item_type, url, item_type, path, name, + ) + } else { + // do not use substitutions for a more concise output + write!(buffer, "{}", tcx.def_path_str(def_id)) + } + } + + fn format_byte_str(buffer: &mut String, byte_str: &[u8]) -> Result<(), Error> { + // @Task find better limit + const LIMIT: usize = 80; + + buffer.write_str("b\"")?; + + if byte_str.len() > LIMIT { + write!(buffer, "{ELLIPSIS}")?; + } else { + for &char in byte_str { + for char in std::ascii::escape_default(char) { + // @Task improve + write!(buffer, "{}", Escape(&char::from(char).to_string()))?; + } + } + } + + buffer.write_str("\"") + } + + fn format_const_scalar<'tcx>( + buffer: &mut String, + scalar: Scalar, + ty: Ty<'tcx>, + cx: &Context<'tcx>, + ) -> Result<(), Error> { + match scalar { + Scalar::Ptr(ptr, _size) => format_const_scalar_ptr(buffer, ptr, ty), + Scalar::Int(int) => format_const_scalar_int(buffer, int, ty, cx), + } + } + + // @Question necessary? + fn format_const_scalar_ptr(buffer: &mut String, ptr: Pointer, ty: Ty<'_>) -> Result<(), Error> { + // let (alloc_id, offset) = ptr.into_parts(); + // match ty.kind() { + // // Byte strings (&[u8; N]) + // ty::Ref(_, inner, _) => { + // if let ty::Array(elem, len) = inner.kind() { + // if let ty::Uint(ty::UintTy::U8) = elem.kind() { + // if let ty::ConstKind::Value(ConstValue::Scalar(int)) = len.val() { + // match self.tcx().get_global_alloc(alloc_id) { + // Some(GlobalAlloc::Memory(alloc)) => { + // let len = int.assert_bits(self.tcx().data_layout.pointer_size); + // let range = + // AllocRange { start: offset, size: Size::from_bytes(len) }; + // if let Ok(byte_str) = + // alloc.inner().get_bytes(&self.tcx(), range) + // { + // p!(pretty_print_byte_str(byte_str)) + // } else { + // p!("") + // } + // } + // // FIXME: for statics and functions, we could in principle print more detail. + // Some(GlobalAlloc::Static(def_id)) => { + // p!(write("", def_id)) + // } + // Some(GlobalAlloc::Function(_)) => p!(""), + // None => p!(""), + // } + // return Ok(self); + // } + // } + // } + // } + // ty::FnPtr(_) => { + // // FIXME: We should probably have a helper method to share code with the "Byte strings" + // // printing above (which also has to handle pointers to all sorts of things). + // if let Some(GlobalAlloc::Function(instance)) = tcx.get_global_alloc(alloc_id) + // { + // self = self.typed_value( + // |this| this.print_value_path(instance.def_id(), instance.substs), + // |this| this.print_type(ty), + // " as ", + // )?; + // return Ok(()); + // } + // } + // _ => {} + // } + // // Any pointer values not covered by a branch above + // self = self.pretty_print_const_pointer(ptr, ty, print_ty)?; + write!(buffer, "/*const_scalar_ptr*/")?; // @Temporary + Ok(()) + } + + fn format_const_scalar_int<'tcx>( + buffer: &mut String, + int: ScalarInt, + ty: Ty<'tcx>, + cx: &Context<'tcx>, + ) -> Result<(), Error> { + match ty.kind() { + // Bool + ty::Bool if int == ScalarInt::FALSE => write!(buffer, "false"), + ty::Bool if int == ScalarInt::TRUE => write!(buffer, "true"), + // Float + ty::Float(ty::FloatTy::F32) => { + // @Bug TryFrom impl doesn't exist for some reason + // write!(buffer, "{}f32", rustc_apfloat::ieee::Single::try_from(int).unwrap()) + // @Temporary + write!(buffer, "/*f32*/") + } + ty::Float(ty::FloatTy::F64) => { + // @Bug TryFrom impl doesn't exist for some reason + // write!(buffer, "{}f64", rustc_apfloat::ieee::Double::try_from(int).unwrap()) + // @Temporary + write!(buffer, "/*f64*/") + } + // Int + ty::Uint(_) | ty::Int(_) => { + let int = + ConstInt::new(int, matches!(ty.kind(), ty::Int(_)), ty.is_ptr_sized_integral()); + write!(buffer, "{:?}", int) + } + // Char + ty::Char if char::try_from(int).is_ok() => { + write!(buffer, "{:?}", char::try_from(int).unwrap()) + } + // Pointer types + ty::Ref(..) | ty::RawPtr(_) | ty::FnPtr(_) => { + let data = int.assert_bits(cx.tcx().data_layout.pointer_size); + typed_value( + buffer, + |buffer| write!(buffer, "0x{:x}", data), + |buffer| format_type(buffer, ty), + " as ", + ) + } + // Nontrivial types with scalar bit representation + _ => { + // @Task link to transmute + if int.size() == Size::ZERO { + write!(buffer, "transmute(())") + } else { + write!(buffer, "transmute(0x{:x})", int) + } + } + } + } + + // @Task get rid of this + fn format_type(buffer: &mut String, ty: Ty<'_>) -> Result<(), Error> { + // let type_length_limit = tcx.type_length_limit(); + // if type_length_limit.value_within_limit(self.printed_type_count) { + // self.printed_type_count += 1; + // self.pretty_print_type(ty) + // } else { + // write!(self, "...")?; + // Ok(self) + // } + // @Temporary + write!(buffer, "/*type*/") + } + + /// Prints `{f: t}` or `{f as t}` depending on the `conversion` argument + // @Task get rid of this! + fn typed_value( + buffer: &mut String, + f: impl FnOnce(&mut String) -> Result<(), Error>, + t: impl FnOnce(&mut String) -> Result<(), Error>, + conversion: &str, + ) -> Result<(), Error> { + buffer.write_str("{")?; + f(buffer)?; + buffer.write_str(conversion)?; + t(buffer)?; + buffer.write_str("}") + } +} + pub(crate) fn display_fn( f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result, ) -> impl fmt::Display { diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 2ed7a6f1bb144..234e121120271 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -112,6 +112,10 @@ pub(crate) struct SharedContext<'tcx> { /// Storage for the errors produced while generating documentation so they /// can be printed together at the end. errors: Receiver, + /// Document items that have lower than `pub` visibility. + pub(crate) document_private: bool, + /// Document items that have `doc(hidden)`. + pub(crate) document_hidden: bool, /// `None` by default, depends on the `generate-redirect-map` option flag. If this field is set /// to `Some(...)`, it'll store redirections and then generate a JSON file at the top level of /// the crate. @@ -404,6 +408,8 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { resource_suffix, static_root_path, unstable_features, + document_private, + document_hidden, generate_redirect_map, show_type_layout, generate_link_to_definition, @@ -488,6 +494,8 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { playground, all: RefCell::new(AllTypes::new()), errors: receiver, + document_private, + document_hidden, redirections: if generate_redirect_map { Some(Default::default()) } else { None }, show_type_layout, span_correspondance_map: matches, diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 89d372da32278..819ab51d9ed75 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -715,14 +715,7 @@ fn assoc_const( ty = ty.print(cx), ); if let Some(default) = default { - write!(w, " = "); - - // FIXME: `.value()` uses `clean::utils::format_integer_with_underscore_sep` under the - // hood which adds noisy underscores and a type suffix to number literals. - // This hurts readability in this context especially when more complex expressions - // are involved and it doesn't add much of value. - // Find a way to print constants here without all that jazz. - write!(w, "{}", Escape(&default.value(cx.tcx()).unwrap_or_else(|| default.expr(cx.tcx())))); + write!(w, " = {}", default.value_html(cx).unwrap_or_else(|| "_".into())); } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 81cc12c9d5596..fe2c3f0ab2ff6 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1374,36 +1374,11 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle typ = c.type_.print(cx), ); - // FIXME: The code below now prints - // ` = _; // 100i32` - // if the expression is - // `50 + 50` - // which looks just wrong. - // Should we print - // ` = 100i32;` - // instead? - - let value = c.value(cx.tcx()); - let is_literal = c.is_literal(cx.tcx()); - let expr = c.expr(cx.tcx()); - if value.is_some() || is_literal { - write!(w, " = {expr};", expr = Escape(&expr)); - } else { - w.write_str(";"); + if let Some(value) = c.value_html(cx) { + write!(w, " = {}", value); } - if !is_literal { - if let Some(value) = &value { - let value_lowercase = value.to_lowercase(); - let expr_lowercase = expr.to_lowercase(); - - if value_lowercase != expr_lowercase - && value_lowercase.trim_end_matches("i32") != expr_lowercase - { - write!(w, " // {value}", value = Escape(value)); - } - } - } + w.write_str(";"); }); }); diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 70b7a47bcd58b..340ad2fa97ca0 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -828,6 +828,13 @@ pre, .rustdoc.source .example-wrap { margin-left: 0; } +.content .ellipsis { + border: 1px solid var(--main-color); + border-radius: 4px; + padding: 0 5px; + opacity: 0.6; +} + nav.sub { flex-grow: 1; margin-bottom: 25px;