
Improve `-Zunpretty=hir` for parsed attrs 0. Rename `print_something` to `should_render` to make it distinct from `print_attribute` in that it doesn't print anything, it's just a way to probe if a type renders anything. 1. Fixes a few bugs in the `PrintAttribute` derive. Namely, the `__printed_anything` variable was entangled with the `should_render` call, leading us to always render field names but never render commas. 2. Remove the outermost `""` from the attr. 3. Debug print `Symbol`s. I know that this is redundant for some parsed attributes, but there's no good way to distinguish symbols that are ident-like and symbols which are cooked string literals. We could perhaps *conditionally* to fall back to a debug printing if the symbol doesn't match an ident? But seems like overkill. Based on #138060, only review the commits not in that one.
203 lines
6 KiB
Rust
203 lines
6 KiB
Rust
// tidy-alphabetical-start
|
|
#![allow(internal_features)]
|
|
#![doc(rust_logo)]
|
|
#![feature(let_chains)]
|
|
#![feature(rustdoc_internals)]
|
|
#![warn(unreachable_pub)]
|
|
// tidy-alphabetical-end
|
|
|
|
mod attributes;
|
|
mod stability;
|
|
mod version;
|
|
|
|
use std::num::NonZero;
|
|
|
|
pub use attributes::*;
|
|
use rustc_abi::Align;
|
|
use rustc_ast::token::CommentKind;
|
|
use rustc_ast::{AttrStyle, IntTy, UintTy};
|
|
use rustc_ast_pretty::pp::Printer;
|
|
use rustc_span::hygiene::Transparency;
|
|
use rustc_span::{Span, Symbol};
|
|
pub use stability::*;
|
|
use thin_vec::ThinVec;
|
|
pub use version::*;
|
|
|
|
/// Requirements for a `StableHashingContext` to be used in this crate.
|
|
/// This is a hack to allow using the `HashStable_Generic` derive macro
|
|
/// instead of implementing everything in `rustc_middle`.
|
|
pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext {}
|
|
|
|
/// This trait is used to print attributes in `rustc_hir_pretty`.
|
|
///
|
|
/// For structs and enums it can be derived using [`rustc_macros::PrintAttribute`].
|
|
/// The output will look a lot like a `Debug` implementation, but fields of several types
|
|
/// like [`Span`]s and empty tuples, are gracefully skipped so they don't clutter the
|
|
/// representation much.
|
|
pub trait PrintAttribute {
|
|
/// Whether or not this will render as something meaningful, or if it's skipped
|
|
/// (which will force the containing struct to also skip printing a comma
|
|
/// and the field name).
|
|
fn should_render(&self) -> bool;
|
|
|
|
fn print_attribute(&self, p: &mut Printer);
|
|
}
|
|
|
|
impl<T: PrintAttribute> PrintAttribute for &T {
|
|
fn should_render(&self) -> bool {
|
|
T::should_render(self)
|
|
}
|
|
|
|
fn print_attribute(&self, p: &mut Printer) {
|
|
T::print_attribute(self, p)
|
|
}
|
|
}
|
|
impl<T: PrintAttribute> PrintAttribute for Option<T> {
|
|
fn should_render(&self) -> bool {
|
|
self.as_ref().is_some_and(|x| x.should_render())
|
|
}
|
|
|
|
fn print_attribute(&self, p: &mut Printer) {
|
|
if let Some(i) = self {
|
|
T::print_attribute(i, p)
|
|
}
|
|
}
|
|
}
|
|
impl<T: PrintAttribute> PrintAttribute for ThinVec<T> {
|
|
fn should_render(&self) -> bool {
|
|
self.is_empty() || self[0].should_render()
|
|
}
|
|
|
|
fn print_attribute(&self, p: &mut Printer) {
|
|
let mut last_printed = false;
|
|
p.word("[");
|
|
for i in self {
|
|
if last_printed {
|
|
p.word_space(",");
|
|
}
|
|
i.print_attribute(p);
|
|
last_printed = i.should_render();
|
|
}
|
|
p.word("]");
|
|
}
|
|
}
|
|
macro_rules! print_skip {
|
|
($($t: ty),* $(,)?) => {$(
|
|
impl PrintAttribute for $t {
|
|
fn should_render(&self) -> bool { false }
|
|
fn print_attribute(&self, _: &mut Printer) { }
|
|
})*
|
|
};
|
|
}
|
|
|
|
macro_rules! print_disp {
|
|
($($t: ty),* $(,)?) => {$(
|
|
impl PrintAttribute for $t {
|
|
fn should_render(&self) -> bool { true }
|
|
fn print_attribute(&self, p: &mut Printer) {
|
|
p.word(format!("{}", self));
|
|
}
|
|
}
|
|
)*};
|
|
}
|
|
macro_rules! print_debug {
|
|
($($t: ty),* $(,)?) => {$(
|
|
impl PrintAttribute for $t {
|
|
fn should_render(&self) -> bool { true }
|
|
fn print_attribute(&self, p: &mut Printer) {
|
|
p.word(format!("{:?}", self));
|
|
}
|
|
}
|
|
)*};
|
|
}
|
|
|
|
macro_rules! print_tup {
|
|
(num_should_render $($ts: ident)*) => { 0 $(+ $ts.should_render() as usize)* };
|
|
() => {};
|
|
($t: ident $($ts: ident)*) => {
|
|
#[allow(non_snake_case, unused)]
|
|
impl<$t: PrintAttribute, $($ts: PrintAttribute),*> PrintAttribute for ($t, $($ts),*) {
|
|
fn should_render(&self) -> bool {
|
|
let ($t, $($ts),*) = self;
|
|
print_tup!(num_should_render $t $($ts)*) != 0
|
|
}
|
|
|
|
fn print_attribute(&self, p: &mut Printer) {
|
|
let ($t, $($ts),*) = self;
|
|
let parens = print_tup!(num_should_render $t $($ts)*) > 1;
|
|
if parens {
|
|
p.popen();
|
|
}
|
|
|
|
let mut printed_anything = $t.should_render();
|
|
|
|
$t.print_attribute(p);
|
|
|
|
$(
|
|
if $ts.should_render() {
|
|
if printed_anything {
|
|
p.word_space(",");
|
|
}
|
|
printed_anything = true;
|
|
}
|
|
$ts.print_attribute(p);
|
|
)*
|
|
|
|
if parens {
|
|
p.pclose();
|
|
}
|
|
}
|
|
}
|
|
|
|
print_tup!($($ts)*);
|
|
};
|
|
}
|
|
|
|
print_tup!(A B C D E F G H);
|
|
print_skip!(Span, ());
|
|
print_disp!(u16, bool, NonZero<u32>);
|
|
print_debug!(Symbol, UintTy, IntTy, Align, AttrStyle, CommentKind, Transparency);
|
|
|
|
/// Finds attributes in sequences of attributes by pattern matching.
|
|
///
|
|
/// A little like `matches` but for attributes.
|
|
///
|
|
/// ```rust,ignore (illustrative)
|
|
/// // finds the repr attribute
|
|
/// if let Some(r) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
|
|
///
|
|
/// }
|
|
///
|
|
/// // checks if one has matched
|
|
/// if find_attr!(attrs, AttributeKind::Repr(_)) {
|
|
///
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// Often this requires you to first end up with a list of attributes.
|
|
/// A common way to get those is through `tcx.get_all_attrs(did)`
|
|
#[macro_export]
|
|
macro_rules! find_attr {
|
|
($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{
|
|
$crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some()
|
|
}};
|
|
|
|
($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{
|
|
fn check_attribute_iterator<'a>(_: &'_ impl IntoIterator<Item = &'a rustc_hir::Attribute>) {}
|
|
check_attribute_iterator(&$attributes_list);
|
|
|
|
let find_attribute = |iter| {
|
|
for i in $attributes_list {
|
|
match i {
|
|
rustc_hir::Attribute::Parsed($pattern) $(if $guard)? => {
|
|
return Some($e);
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
None
|
|
};
|
|
find_attribute($attributes_list)
|
|
}};
|
|
}
|