about summary refs log tree commit diff
path: root/compiler/rustc_ast_pretty
AgeCommit message (Collapse)AuthorLines
2025-08-25add span to struct pattern rest (..)Valdemar Erk-1/+1
2025-08-22print raw lifetime idents with `r#`Deadbeef-9/+8
2025-08-11Extract ast TraitImplHeaderCameron Steffen-27/+29
2025-08-09remove `P`Deadbeef-12/+9
2025-07-28use let chains in ast, borrowck, codegen, const_evalKivooeo-4/+4
2025-07-17parse `const trait Trait`Deadbeef-0/+2
2025-07-10fix: Include frontmatter in -Zunpretty outputEd Page-1/+9
In the implementation (#140035), this was left as an open question for the tracking issue (#136889). My assumption is that this should be carried over. Thankfully, either way, `-Zunpretty` is unstable and we can always change it even if we stabilize frontmatter.
2025-07-09feat(lexer): Allow including frontmatter with 'tokenize'Ed Page-1/+1
2025-07-06compiler: rename {ast,hir}::BareFn* to FnPtr*Jubilee Young-1/+1
Fix some comments and related types and locals where it is obvious, e.g. - bare_fn -> fn_ptr - LifetimeBinderKind::BareFnType -> LifetimeBinderKind::FnPtrType Co-authored-by: León Orell Valerian Liehr <me@fmease.dev>
2025-07-01Remove support for dyn*Michael Goulet-1/+0
2025-06-30Introduce `ByteSymbol`.Nicholas Nethercote-2/+6
It's like `Symbol` but for byte strings. The interner is now used for both `Symbol` and `ByteSymbol`. E.g. if you intern `"dog"` and `b"dog"` you'll get a `Symbol` and a `ByteSymbol` with the same index and the characters will only be stored once. The motivation for this is to eliminate the `Arc`s in `ast::LitKind`, to make `ast::LitKind` impl `Copy`, and to avoid the need to arena-allocate `ast::LitKind` in HIR. The latter change reduces peak memory by a non-trivial amount on literal-heavy benchmarks such as `deep-vector` and `tuple-stress`. `Encoder`, `Decoder`, `SpanEncoder`, and `SpanDecoder` all get some changes so that they can handle normal strings and byte strings. This change does slow down compilation of programs that use `include_bytes!` on large files, because the contents of those files are now interned (hashed). This makes `include_bytes!` more similar to `include_str!`, though `include_bytes!` contents still aren't escaped, and hashing is still much cheaper than escaping.
2025-06-24Rollup merge of #135731 - frank-king:feature/pin-borrow, r=eholk,traviscrossJubilee-0/+4
Implement parsing of pinned borrows This PR implements part of #130494. EDIT: It introduces `&pin mut $place` and `&pin const $place` as sugars for `std::pin::pin!($place)` and its shared reference equivalent, except that `$place` will not be moved when borrowing. The borrow check will be in charge of enforcing places cannot be moved or mutably borrowed since being pinned till dropped. ### Implementation steps: - [x] parse the `&pin mut $place` and `&pin const $place` syntaxes - [ ] borrowck of `&pin mut|const` - [ ] support autoref of `&pin mut|const` when needed
2025-06-20Insert parentheses around binary operation with attributeDavid Tolnay-12/+38
2025-06-15Implement pinned borrows, part of `pin_ergonomics`Frank King-0/+4
2025-06-13Rollup merge of #142069 - nnethercote:Zmacro-stats, r=petrochenkovMatthias Krüger-2/+22
Introduce `-Zmacro-stats` Introduce `-Zmacro-stats`. It collects data about macro expansions and prints them in a table after expansion finishes. It's very useful for detecting macro bloat, especially for proc macros. r? `@petrochenkov`
2025-06-13Rollup merge of #134847 - dtolnay:asymmetrical, r=fmeaseMatthias Krüger-64/+134
Implement asymmetrical precedence for closures and jumps I have been through a series of asymmetrical precedence designs in Syn, and finally have one that I like and is worth backporting into rustc. It is based on just 2 bits of state: `next_operator_can_begin_expr` and `next_operator_can_continue_expr`. Asymmetrical precedence is the thing that enables `(return 1) + 1` to require parentheses while `1 + return 1` does not, despite `+` always having stronger precedence than `return` [according to the Rust Reference](https://doc.rust-lang.org/1.83.0/reference/expressions.html#expression-precedence). This is facilitated by `next_operator_can_continue_expr`. Relatedly, it is the thing that enables `(return) - 1` to require parentheses while `return + 1` does not, despite `+` and `-` having exactly the same precedence. This is facilitated by `next_operator_can_begin_expr`. **Example:** ```rust macro_rules! repro { ($e:expr) => { $e - $e; $e + $e; }; } fn main() { repro!{return} repro!{return 1} } ``` `-Zunpretty=expanded` **Before:** ```console fn main() { (return) - (return); (return) + (return); (return 1) - (return 1); (return 1) + (return 1); } ``` **After:** ```console fn main() { (return) - return; return + return; (return 1) - return 1; (return 1) + return 1; } ```
2025-06-12Introduce `-Zmacro-stats`.Nicholas Nethercote-2/+22
It collects data about macro expansions and prints them in a table after expansion finishes. It's very useful for detecting macro bloat, especially for proc macros. Details: - It measures code snippets by pretty-printing them and then measuring lines and bytes. This required a bunch of additional pretty-printing plumbing, in `rustc_ast_pretty` and `rustc_expand`. - The measurement is done in `MacroExpander::expand_invoc`. - The measurements are stored in `ExtCtxt::macro_stats`.
2025-05-28Reorder `ast::ItemKind::{Struct,Enum,Union}` fields.Nicholas Nethercote-4/+4
So they match the order of the parts in the source code, e.g.: ``` struct Foo<T, U> { t: T, u: U } <-><----> <------------> / | \ ident generics variant_data ```
2025-05-04Initial support for dynamically linked cratesBryanskiy-5/+45
2025-05-03Implement asymmetrical precedence for closures and jumpsDavid Tolnay-64/+134
2025-05-03Avoid an indent for labelled loops.Nicholas Nethercote-2/+2
2025-05-03Enable `BoxMarker` drop checking.Nicholas Nethercote-5/+1
All the box open/close issues have been fixed.
2025-05-03Remove fake `BoxMarker`s.Nicholas Nethercote-10/+1
They don't appear to do anything -- no test output is affected -- and no other pretty-printing code looks like this.
2025-05-02Rollup merge of #137474 - VlaDexa:shebang-placement, r=wesleywiserStuart Cook-0/+19
pretty-print: Print shebang at the top of the output Shebang should stay at the top of the file, even after pretty-printing. Closes #134643
2025-04-30Rollup merge of #140312 - nnethercote:DelimArgs-spacing, r=petrochenkovMatthias Krüger-9/+24
Improve pretty-printing of braces r? ````@petrochenkov````
2025-04-29Rollup merge of #139909 - oli-obk:or-patterns, r=BoxyUwUTrevor Gross-0/+11
implement or-patterns for pattern types These are necessary to represent `NonZeroI32`, as the range for that is `..0 | 1..`. The `rustc_scalar_layout_range_*` attributes avoided this by just implementing wraparound and having a single `1..=-1` range effectively. See https://rust-lang.zulipchat.com/#narrow/channel/481660-t-lang.2Fpattern-types/topic/.60or.20pattern.60.20representation.20in.20type.20system/with/504217694 for some background discussion cc https://github.com/rust-lang/rust/issues/123646 r? `@BoxyUwU`
2025-04-29Improve pretty-printing of braces.Nicholas Nethercote-9/+24
Most notably, the `FIXME` for suboptimal printing of `use` groups in `tests/ui/macros/stringify.rs` is fixed. And all other test output changes result in pretty printed output being closer to the original formatting in the source code.
2025-04-28Add or-patterns to pattern typesOli Scherer-0/+11
2025-04-28Inline and remove three pretty-printer methods.Nicholas Nethercote-42/+22
They all have a single call site, aren't that big, and removing them avoids having to pass some `BoxMarker`s.
2025-04-28Use `PrintState::head` in `PrintState::block_to_string`.Nicholas Nethercote-4/+1
2025-04-28Introduce `BoxMarker` to pretty-printing.Nicholas Nethercote-202/+257
The pretty-printers open and close "boxes" of text a lot. The open and close operations must be matched. The matching is currently all implicit and very easy to get wrong. (#140280 and #140246 are two recent pretty-printing fixes that both involved unclosed boxes.) This commit introduces `BoxMarker`, a marker type that represents an open box. It makes box opening/closing explicit, which makes it much easier to understand and harder to get wrong. The commit also removes many comments are on `end` calls saying things like "end outer head-block", "Close the outer-box". These demonstrate how confusing the implicit approach was, but aren't necessary any more.
2025-04-27Rollup merge of #140280 - nnethercote:improve-if-else-printing, r=UrgauMatthias Krüger-4/+6
Improve if/else pretty printing AST/HIR pretty printing of if/else is currently pretty bad. This PR improves it a lot. r? `@Nadrieril`
2025-04-27Rollup merge of #140246 - nnethercote:fix-never-pattern-printing, r=NadrierilMatthias Krüger-0/+1
Fix never pattern printing It's currently broken, but there's an easy fix. r? `@Nadrieril`
2025-04-26Improve HIR pretty-printing of if/else some more.Nicholas Nethercote-1/+1
In the AST the "then" block is represented as a `Block`. In HIR the "then" block is represented as an `Expr` that happens to always be. `ExprKind::Block`. By deconstructing the `ExprKind::Block` to extract the block within, things print properly. For `issue-82392.rs`, note that we no longer print a type after the "then" block. This is good, it now matches how we don't print a type for the "else" block. (Well, we do print a type after the "else" block, but it's for the whole if/else.) Also tighten up some of the pattern matching -- these block expressions within if/else will never have labels.
2025-04-25Improve pretty printing of if/else.Nicholas Nethercote-3/+5
By removing some of the over-indenting. AST pretty printing now looks correct. HIR pretty printing is better, but still over-indents some.
2025-04-25Fix some pretty printing indents.Nicholas Nethercote-2/+2
Indents for `cbox` and `ibox` are 0 or `INDENT_UNIT` (4) except for a couple of places which are `INDENT_UNIT - 1` for no clear reason. This commit changes the three space indents to four spaces.
2025-04-24Fix pretty printing of never pattern match arms.Nicholas Nethercote-0/+1
2025-04-24Rollup merge of #140232 - nnethercote:rm-unnecessary-clones, r=SparrowLiiMatthias Krüger-2/+2
Remove unnecessary clones r? `@SparrowLii`
2025-04-24Remove some unnecessary clones.Nicholas Nethercote-2/+2
I found these by grepping for `&[a-z_\.]*\.clone()`, i.e. expressions like `&a.b.clone()`, which are sometimes unnecessary clones, and also looking at clones nearby to cases like that.
2025-04-21Remove `token::{Open,Close}Delim`.Nicholas Nethercote-10/+9
By replacing them with `{Open,Close}{Param,Brace,Bracket,Invisible}`. PR #137902 made `ast::TokenKind` more like `lexer::TokenKind` by replacing the compound `BinOp{,Eq}(BinOpToken)` variants with fieldless variants `Plus`, `Minus`, `Star`, etc. This commit does a similar thing with delimiters. It also makes `ast::TokenKind` more similar to `parser::TokenType`. This requires a few new methods: - `TokenKind::is_{,open_,close_}delim()` replace various kinds of pattern matches. - `Delimiter::as_{open,close}_token_kind` are used to convert `Delimiter` values to `TokenKind`. Despite these additions, it's a net reduction in lines of code. This is because e.g. `token::OpenParen` is so much shorter than `token::OpenDelim(Delimiter::Parenthesis)` that many multi-line forms reduce to single line forms. And many places where the number of lines doesn't change are still easier to read, just because the names are shorter, e.g.: ``` - } else if self.token != token::CloseDelim(Delimiter::Brace) { + } else if self.token != token::CloseBrace { ```
2025-04-14Auto merge of #124141 - ↵bors-16/+2
nnethercote:rm-Nonterminal-and-TokenKind-Interpolated, r=petrochenkov Remove `Nonterminal` and `TokenKind::Interpolated` A third attempt at this; the first attempt was #96724 and the second was #114647. r? `@ghost`
2025-04-10Rename some `name` variables as `ident`.Nicholas Nethercote-2/+2
It bugs me when variables of type `Ident` are called `name`. It leads to silly things like `name.name`. `Ident` variables should be called `ident`, and `name` should be used for variables of type `Symbol`. This commit improves things by by doing `s/name/ident/` on a bunch of `Ident` variables. Not all of them, but a decent chunk.
2025-04-07Rollup merge of #139112 - m-ou-se:super-let, r=lcnrStuart Cook-0/+3
Implement `super let` Tracking issue: https://github.com/rust-lang/rust/issues/139076 This implements `super let` as proposed in #139080, based on the following two equivalence rules. 1. For all expressions `$expr` in any context, these are equivalent: - `& $expr` - `{ super let a = & $expr; a }` 2. And, additionally, these are equivalent in any context when `$expr` is a temporary (aka rvalue): - `& $expr` - `{ super let a = $expr; & a }` So far, this experiment has a few interesting results: ## Interesting result 1 In this snippet: ```rust super let a = f(&temp()); ``` I originally expected temporary `temp()` would be dropped at the end of the statement (`;`), just like in a regular `let`, because `temp()` is not subject to temporary lifetime extension. However, it turns out that that would break the fundamental equivalence rules. For example, in ```rust g(&f(&temp())); ``` the temporary `temp()` will be dropped at the `;`. The first equivalence rule tells us this must be equivalent: ```rust g({ super let a = &f(&temp()); a }); ``` But that means that `temp()` must live until the last `;` (after `g()`), not just the first `;` (after `f()`). While this was somewhat surprising to me at first, it does match the exact behavior we need for `pin!()`: The following _should work_. (See also https://github.com/rust-lang/rust/issues/138718) ```rust g(pin!(f(&mut temp()))); ``` Here, `temp()` lives until the end of the statement. This makes sense from the perspective of the user, as no other `;` or `{}` are visible. Whether `pin!()` uses a `{}` block internally or not should be irrelevant. This means that _nothing_ in a `super let` statement will be dropped at the end of that super let statement. It does not even need its own scope. This raises questions that are useful for later on: - Will this make temporaries live _too long_ in cases where `super let` is used not in a hidden block in a macro, but as a visible statement in code like the following? ```rust let writer = { super let file = File::create(&format!("/home/{user}/test")); Writer::new(&file) }; ``` - Is a `let` statement in a block still the right syntax for this? Considering it has _no_ scope of its own, maybe neither a block nor a statement should be involved This leads me to think that instead of `{ super let $pat = $init; $expr }`, we might want to consider something like `let $pat = $init in $expr` or `$expr where $pat = $init`. Although there are also issues with these, as it isn't obvious anymore if `$init` should be subject to temporary lifetime extension. (Do we want both `let _ = _ in ..` and `super let _ = _ in ..`?) ## Interesting result 2 What about `super let x;` without initializer? ```rust let a = { super let x; x = temp(); &x }; ``` This works fine with the implementation in this PR: `x` is extended to live as long as `a`. While it matches my expectations, a somewhat interesting thing to realize is that these are _not_ equivalent: - `super let x = $expr;` - `super let x; x = $expr;` In the first case, all temporaries in $expr will live at least as long as (the result of) the surrounding block. In the second case, temporaries will be dropped at the end of the assignment statement. (Because the assignment statement itself "is not `super`".) This difference in behavior might be confusing, but it _might_ be useful. One might want to extend the lifetime of a variable without extending all the temporaries in the initializer expression. On the other hand, that can also be expressed as: - `let x = $expr; super let x = x;` (w/o temporary lifetime extension), or - `super let x = { $expr };` (w/ temporary lifetime extension) So, this raises these questions: - Do we want to accept `super let x;` without initializer at all? - Does it make sense for statements other than let statements to be "super"? An expression statement also drops temporaries at its `;`, so now that we discovered that `super let` basically disables that `;` (see interesting result 1), is there a use to having other statements without their own scope? (I don't think that's ever useful?) ## Interesting result 3 This works now: ```rust super let Some(x) = a.get(i) else { return }; ``` I didn't put in any special cases for `super let else`. This is just the behavior that 'naturally' falls out when implementing `super let` without thinking of the `let else` case. - Should `super let else` work? ## Interesting result 4 This 'works': ```rust fn main() { super let a = 123; } ``` I didn't put in any special cases for `super let` at function scope. I had expected the code to cause an ICE or other weird failure when used at function body scope, because there's no way to let the variable live as long as the result of the function. This raises the question: - Does this mean that this behavior is the natural/expected behavior when `super let` is used at function scope? Or is this just a quirk and should we explicitly disallow `super let` in a function body? (Probably the latter.) --- The questions above do not need an answer to land this PR. These questions should be considered when redesigning/rfc'ing/stabilizing the feature.
2025-04-07Rollup merge of #139035 - nnethercote:PatKind-Missing, r=oli-obkStuart Cook-8/+3
Add new `PatKind::Missing` variants To avoid some ugly uses of `kw::Empty` when handling "missing" patterns, e.g. in bare fn tys. Helps with #137978. Details in the individual commits. r? ``@oli-obk``
2025-04-04Fix typo in pretty printing super let.Mara Bos-1/+1
Co-authored-by: lcnr <rust@lcnr.de>
2025-04-04Implement `super let`.Mara Bos-0/+3
2025-04-03Tighten up assignment operator representations.Nicholas Nethercote-2/+1
In the AST, currently we use `BinOpKind` within `ExprKind::AssignOp` and `AssocOp::AssignOp`, even though this allows some nonsensical combinations. E.g. there is no `&&=` operator. Likewise for HIR and THIR. This commit introduces `AssignOpKind` which only includes the ten assignable operators, and uses it in `ExprKind::AssignOp` and `AssocOp::AssignOp`. (And does similar things for `hir::ExprKind` and `thir::ExprKind`.) This avoids the possibility of nonsensical combinations, as seen by the removal of the `bug!` case in `lang_item_for_binop`. The commit is mostly plumbing, including: - Adds an `impl From<AssignOpKind> for BinOpKind` (AST) and `impl From<AssignOp> for BinOp` (MIR/THIR). - `BinOpCategory` can now be created from both `BinOpKind` and `AssignOpKind`. - Replaces the `IsAssign` type with `Op`, which has more information and a few methods. - `suggest_swapping_lhs_and_rhs`: moves the condition to the call site, it's easier that way. - `check_expr_inner`: had to factor out some code into a separate method. I'm on the fence about whether avoiding the nonsensical combinations is worth the extra code.
2025-04-03Use `BinOpKind` instead of `BinOp` for function args where possible.Nicholas Nethercote-6/+6
Because it's nice to avoid passing in unnecessary data.
2025-04-02Remove `NtBlock`, `Nonterminal`, and `TokenKind::Interpolated`.Nicholas Nethercote-16/+2
`NtBlock` is the last remaining variant of `Nonterminal`, so once it is gone then `Nonterminal` can be removed as well.
2025-04-01Move `ast::Item::ident` into `ast::ItemKind`.Nicholas Nethercote-41/+42
`ast::Item` has an `ident` field. - It's always non-empty for these item kinds: `ExternCrate`, `Static`, `Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`, `Trait`, `TraitAlias`, `MacroDef`, `Delegation`. - It's always empty for these item kinds: `Use`, `ForeignMod`, `GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`. There is a similar story for `AssocItemKind` and `ForeignItemKind`. Some sites that handle items check for an empty ident, some don't. This is a very C-like way of doing things, but this is Rust, we have sum types, we can do this properly and never forget to check for the exceptional case and never YOLO possibly empty identifiers (or possibly dummy spans) around and hope that things will work out. The commit is large but it's mostly obvious plumbing work. Some notable things. - `ast::Item` got 8 bytes bigger. This could be avoided by boxing the fields within some of the `ast::ItemKind` variants (specifically: `Struct`, `Union`, `Enum`). I might do that in a follow-up; this commit is big enough already. - For the visitors: `FnKind` no longer needs an `ident` field because the `Fn` within how has one. - In the parser, the `ItemInfo` typedef is no longer needed. It was used in various places to return an `Ident` alongside an `ItemKind`, but now the `Ident` (if present) is within the `ItemKind`. - In a few places I renamed identifier variables called `name` (or `foo_name`) as `ident` (or `foo_ident`), to better match the type, and because `name` is normally used for `Symbol`s. It's confusing to see something like `foo_name.name`.