Skip to content

Introduce -Zmacro-stats #142069

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ pub struct Path {
pub tokens: Option<LazyAttrTokenStream>,
}

// Succeeds if the path has a single segment that is arg-free and matches the given symbol.
impl PartialEq<Symbol> for Path {
#[inline]
fn eq(&self, name: &Symbol) -> bool {
if let [segment] = self.segments.as_ref()
&& segment.args.is_none()
&& segment.ident.name == *name
&& segment == name
{
true
} else {
Expand All @@ -111,6 +111,15 @@ impl PartialEq<Symbol> for Path {
}
}

// Succeeds if the path has segments that are arg-free and match the given symbols.
impl PartialEq<&[Symbol]> for Path {
#[inline]
fn eq(&self, names: &&[Symbol]) -> bool {
self.segments.len() == names.len()
&& self.segments.iter().zip(names.iter()).all(|(s1, s2)| s1 == s2)
}
}

impl<CTX: rustc_span::HashStableContext> HashStable<CTX> for Path {
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
self.segments.len().hash_stable(hcx, hasher);
Expand Down Expand Up @@ -166,6 +175,14 @@ pub struct PathSegment {
pub args: Option<P<GenericArgs>>,
}

// Succeeds if the path segment is arg-free and matches the given symbol.
impl PartialEq<Symbol> for PathSegment {
#[inline]
fn eq(&self, name: &Symbol) -> bool {
self.args.is_none() && self.ident.name == *name
}
}

impl PathSegment {
pub fn from_ident(ident: Ident) -> Self {
PathSegment { ident, id: DUMMY_NODE_ID, args: None }
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ pub fn item_to_string(i: &ast::Item) -> String {
State::new().item_to_string(i)
}

pub fn assoc_item_to_string(i: &ast::AssocItem) -> String {
State::new().assoc_item_to_string(i)
}

pub fn foreign_item_to_string(i: &ast::ForeignItem) -> String {
State::new().foreign_item_to_string(i)
}

pub fn stmt_to_string(s: &ast::Stmt) -> String {
State::new().stmt_to_string(s)
}

pub fn path_to_string(p: &ast::Path) -> String {
State::new().path_to_string(p)
}
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1063,6 +1063,14 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
Self::to_string(|s| s.print_item(i))
}

fn assoc_item_to_string(&self, i: &ast::AssocItem) -> String {
Self::to_string(|s| s.print_assoc_item(i))
}

fn foreign_item_to_string(&self, i: &ast::ForeignItem) -> String {
Self::to_string(|s| s.print_foreign_item(i))
}

fn path_to_string(&self, p: &ast::Path) -> String {
Self::to_string(|s| s.print_path(p, false, 0))
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast_pretty/src/pprust/state/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ impl<'a> State<'a> {
}
}

fn print_foreign_item(&mut self, item: &ast::ForeignItem) {
pub(crate) fn print_foreign_item(&mut self, item: &ast::ForeignItem) {
let ast::Item { id, span, ref attrs, ref kind, ref vis, tokens: _ } = *item;
self.ann.pre(self, AnnNode::SubItem(id));
self.hardbreak_if_not_bol();
Expand Down Expand Up @@ -548,7 +548,7 @@ impl<'a> State<'a> {
}
}

fn print_assoc_item(&mut self, item: &ast::AssocItem) {
pub(crate) fn print_assoc_item(&mut self, item: &ast::AssocItem) {
let ast::Item { id, span, ref attrs, ref kind, ref vis, tokens: _ } = *item;
self.ann.pre(self, AnnNode::SubItem(id));
self.hardbreak_if_not_bol();
Expand Down
35 changes: 28 additions & 7 deletions compiler/rustc_data_structures/src/thousands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
//! This is an extremely bare-bones alternative to the `thousands` crate on
//! crates.io, for printing large numbers in a readable fashion.
//! This is a bare-bones alternative to the `thousands` crate on crates.io, for
//! printing large numbers in a readable fashion.

#[cfg(test)]
mod tests;

// Converts the number to a string, with underscores as the thousands separator.
pub fn format_with_underscores(n: usize) -> String {
let mut s = n.to_string();
let mut i = s.len();
while i > 3 {
fn format_with_underscores(mut s: String) -> String {
// Ignore a leading '-'.
let start = if s.starts_with('-') { 1 } else { 0 };

// Stop after the first non-digit, e.g. '.' or 'e' for floats.
let non_digit = s[start..].find(|c: char| !c.is_digit(10));
let end = if let Some(non_digit) = non_digit { start + non_digit } else { s.len() };

// Insert underscores within `start..end`.
let mut i = end;
while i > start + 3 {
i -= 3;
s.insert(i, '_');
}
s
}

/// Print a `usize` with underscore separators.
pub fn usize_with_underscores(n: usize) -> String {
format_with_underscores(format!("{n}"))
}

/// Print an `isize` with underscore separators.
pub fn isize_with_underscores(n: isize) -> String {
format_with_underscores(format!("{n}"))
}

/// Print an `f64` with precision 1 (one decimal place) and underscore separators.
pub fn f64p1_with_underscores(n: f64) -> String {
format_with_underscores(format!("{n:.1}"))
}
56 changes: 47 additions & 9 deletions compiler/rustc_data_structures/src/thousands/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,51 @@ use super::*;

#[test]
fn test_format_with_underscores() {
assert_eq!("0", format_with_underscores(0));
assert_eq!("1", format_with_underscores(1));
assert_eq!("99", format_with_underscores(99));
assert_eq!("345", format_with_underscores(345));
assert_eq!("1_000", format_with_underscores(1_000));
assert_eq!("12_001", format_with_underscores(12_001));
assert_eq!("999_999", format_with_underscores(999_999));
assert_eq!("1_000_000", format_with_underscores(1_000_000));
assert_eq!("12_345_678", format_with_underscores(12_345_678));
assert_eq!("", format_with_underscores("".to_string()));
assert_eq!("0", format_with_underscores("0".to_string()));
assert_eq!("12_345.67e14", format_with_underscores("12345.67e14".to_string()));
assert_eq!("-1_234.5678e10", format_with_underscores("-1234.5678e10".to_string()));
assert_eq!("------", format_with_underscores("------".to_string()));
assert_eq!("abcdefgh", format_with_underscores("abcdefgh".to_string()));
assert_eq!("-1b", format_with_underscores("-1b".to_string()));
assert_eq!("-3_456xyz", format_with_underscores("-3456xyz".to_string()));
}

#[test]
fn test_usize_with_underscores() {
assert_eq!("0", usize_with_underscores(0));
assert_eq!("1", usize_with_underscores(1));
assert_eq!("99", usize_with_underscores(99));
assert_eq!("345", usize_with_underscores(345));
assert_eq!("1_000", usize_with_underscores(1_000));
assert_eq!("12_001", usize_with_underscores(12_001));
assert_eq!("999_999", usize_with_underscores(999_999));
assert_eq!("1_000_000", usize_with_underscores(1_000_000));
assert_eq!("12_345_678", usize_with_underscores(12_345_678));
}

#[test]
fn test_isize_with_underscores() {
assert_eq!("0", isize_with_underscores(0));
assert_eq!("-1", isize_with_underscores(-1));
assert_eq!("99", isize_with_underscores(99));
assert_eq!("345", isize_with_underscores(345));
assert_eq!("-1_000", isize_with_underscores(-1_000));
assert_eq!("12_001", isize_with_underscores(12_001));
assert_eq!("-999_999", isize_with_underscores(-999_999));
assert_eq!("1_000_000", isize_with_underscores(1_000_000));
assert_eq!("-12_345_678", isize_with_underscores(-12_345_678));
}

#[test]
fn test_f64p1_with_underscores() {
assert_eq!("0.0", f64p1_with_underscores(0f64));
assert_eq!("0.0", f64p1_with_underscores(0.00000001));
assert_eq!("-0.0", f64p1_with_underscores(-0.00000001));
assert_eq!("1.0", f64p1_with_underscores(0.9999999));
assert_eq!("-1.0", f64p1_with_underscores(-0.9999999));
assert_eq!("345.5", f64p1_with_underscores(345.4999999));
assert_eq!("-100_000.0", f64p1_with_underscores(-100_000f64));
assert_eq!("123_456_789.1", f64p1_with_underscores(123456789.123456789));
assert_eq!("-123_456_789.1", f64p1_with_underscores(-123456789.123456789));
}
22 changes: 21 additions & 1 deletion compiler/rustc_expand/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{AssocCtxt, Visitor};
use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind};
use rustc_attr_data_structures::{AttributeKind, Deprecation, Stability, find_attr};
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_data_structures::sync;
use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult};
use rustc_feature::Features;
Expand Down Expand Up @@ -727,6 +727,7 @@ pub enum SyntaxExtensionKind {
/// A trivial attribute "macro" that does nothing,
/// only keeps the attribute and marks it as inert,
/// thus making it ineligible for further expansion.
/// E.g. `#[default]`, `#[rustfmt::skip]`.
NonMacroAttr,

/// A token-based derive macro.
Expand Down Expand Up @@ -1166,6 +1167,22 @@ pub struct ExpansionData {
pub is_trailing_mac: bool,
}

#[derive(Default)]
pub struct MacroStat {
/// Number of invocations of the macro.
pub count: usize,

/// Net increase in number of lines of code (when pretty-printed), i.e.
/// `lines(output) - lines(invocation)`. Can be negative because a macro
/// output may be smaller than the invocation.
pub lines: isize,

/// Net increase in number of lines of code (when pretty-printed), i.e.
/// `bytes(output) - bytes(invocation)`. Can be negative because a macro
/// output may be smaller than the invocation.
pub bytes: isize,
}

/// One of these is made during expansion and incrementally updated as we go;
/// when a macro expansion occurs, the resulting nodes have the `backtrace()
/// -> expn_data` of their expansion context stored into their span.
Expand All @@ -1189,6 +1206,8 @@ pub struct ExtCtxt<'a> {
/// in the AST, but insert it here so that we know
/// not to expand it again.
pub(super) expanded_inert_attrs: MarkedAttrs,
/// `-Zmacro-stats` data.
pub macro_stats: FxHashMap<(String, MacroKind), MacroStat>,
}

impl<'a> ExtCtxt<'a> {
Expand Down Expand Up @@ -1218,6 +1237,7 @@ impl<'a> ExtCtxt<'a> {
expansions: FxIndexMap::default(),
expanded_inert_attrs: MarkedAttrs::new(),
buffered_early_lint: vec![],
macro_stats: Default::default(),
}
}

Expand Down
Loading
Loading