Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
385dbfe
rustfmt: Discover modules via `cfg_select!`
mkroening Jun 24, 2026
2aeef85
Merge commit 'd427a7c1adc8afd86a7ebf3daec769a434cb2307' into rustfmt-…
jieyouxu Jul 21, 2026
a8307d5
unify the AST repr of type const and const RHS
khyperia Jul 22, 2026
0c393d1
Make FieldDef smaller
panstromek Jun 16, 2026
9f6060f
rustfmt fix: ignore file not found errors for external mods with cust…
ytmimi Jul 22, 2026
7ec361d
Rollup merge of #159737 - ytmimi:rustfmt_issue_6959, r=petrochenkov
jhpratt Jul 23, 2026
80e7dbc
Rollup merge of #158372 - mkroening:rustfmt-cfg_select-modules, r=ytm…
jhpratt Jul 24, 2026
9b49f71
feat: parse `cfg_select!` within rustfmt
ytmimi Mar 22, 2026
f6deac5
chore: make `rewrite_match_body` `pub(crate)` within rustfmt
ytmimi Mar 22, 2026
b764bd3
feat: implement `cfg_select!` formatting in rustfmt
ytmimi Mar 22, 2026
7005bdf
test: Add more `cfg_select!` test cases based on the PR review feedback
ytmimi Mar 22, 2026
a750a23
docs: Add doc comments based on PR feedback
ytmimi Jul 30, 2026
869e53c
feat: flatten `cfg_select!` arms if they're a single expression
ytmimi Jul 30, 2026
f31fc43
fix: rename `parse_cfg_select` -> `parse_cfg_select_arms`
ytmimi Jul 30, 2026
987e6b4
refactor: reorder `format_cfg_select` arguments
ytmimi Jul 30, 2026
d9ad5c7
fix: make sure we cancel diagnostic errors when parsing `cfg_select!`…
ytmimi Jul 30, 2026
02a4cf0
fix: No need to clone before calling `context.leave_macro`
ytmimi Jul 30, 2026
8534173
test: Add test case where `cfg_select!` falls back to default macro h…
ytmimi Jul 30, 2026
17efa09
Fix errors in tooling
JonathanBrouwer Aug 11, 2026
3c3e988
Improve `rewrite_generic_args` to take pattern into account
JonathanBrouwer Aug 11, 2026
7462d13
Add rustfmt regression test
JonathanBrouwer Aug 11, 2026
1568a31
rustfmt: only format `cfg_select!` macros on the `nightly` release ch…
ytmimi Aug 12, 2026
9ebbe58
rustfmt test: prevent some rustfmt system tests from running on a `ni…
ytmimi Aug 12, 2026
9419d20
rustfmt test: add `stable`-only tests for `cfg_select!` formatting
ytmimi Aug 12, 2026
903bc51
clippy: Remove `Cow` from `FnSig::coroutine_kind`
nnethercote Aug 14, 2026
27762fb
rustfmt: Avoid some code duplication
nnethercote Aug 14, 2026
a8226bb
Overhaul `CoroutineKind`
nnethercote Aug 14, 2026
43f0ea5
Auto merge of #161082 - nnethercote:overhaul-CoroutineMarker, r=mejrs
bors Aug 17, 2026
bec6bf5
Uplift rustfmt macro formatting fix
Manishearth Aug 19, 2026
6c7ecd0
add internal DSL for testing binders
khyperia Aug 24, 2026
e01c77e
Fix
CPunisher Jun 28, 2025
69af134
Add variadic test
CPunisher Jun 28, 2025
dbdd05b
Add trait fn tests
CPunisher Jun 28, 2025
86abbb8
Add test for issue 6607
CPunisher Jul 21, 2025
4461e6d
remove `box_patterns` feature
cyrgani May 19, 2026
c8b68ef
Auto merge of #161731 - jhpratt:rollup-cUfsCoq, r=jhpratt
bors Aug 25, 2026
530a4e5
Rollup merge of #161443 - khyperia:abbys-tests, r=BoxyUwU
JonathanBrouwer Aug 25, 2026
e2e9ce3
Merge remote-tracking branch 'upstream/main' into subtree-push-nightl…
ytmimi Aug 28, 2026
f533ffb
chore: bump rustfmt toolchain to nightly-2026-08-27
ytmimi Aug 28, 2026
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
2 changes: 1 addition & 1 deletion rust-toolchain
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2026-07-19"
channel = "nightly-2026-08-27"
components = ["llvm-tools", "rustc-dev"]
21 changes: 9 additions & 12 deletions src/closures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, Rew
use crate::shape::Shape;
use crate::source_map::SpanUtils;
use crate::types::rewrite_bound_params;
use crate::utils::{NodeIdExt, last_line_width, left_most_sub_expr, outer_attributes, stmt_expr};
use crate::utils::{
NodeIdExt, format_coro, last_line_width, left_most_sub_expr, outer_attributes, stmt_expr,
};

// This module is pretty messy because of the rules around closures and blocks:
// FIXME - the below is probably no longer true in full.
Expand All @@ -30,7 +32,7 @@ pub(crate) fn rewrite_closure(
binder: &ast::ClosureBinder,
constness: ast::Const,
capture: ast::CaptureBy,
coroutine_kind: &Option<ast::CoroutineKind>,
coroutine_marker: &Option<ast::CoroutineMarker>,
movability: ast::Movability,
fn_decl: &ast::FnDecl,
body: &ast::Expr,
Expand All @@ -44,7 +46,7 @@ pub(crate) fn rewrite_closure(
binder,
constness,
capture,
coroutine_kind,
coroutine_marker,
movability,
fn_decl,
body,
Expand Down Expand Up @@ -257,7 +259,7 @@ fn rewrite_closure_fn_decl(
binder: &ast::ClosureBinder,
constness: ast::Const,
capture: ast::CaptureBy,
coroutine_kind: &Option<ast::CoroutineKind>,
coroutine_marker: &Option<ast::CoroutineMarker>,
movability: ast::Movability,
fn_decl: &ast::FnDecl,
body: &ast::Expr,
Expand Down Expand Up @@ -288,12 +290,7 @@ fn rewrite_closure_fn_decl(
} else {
""
};
let coro = match coroutine_kind {
Some(ast::CoroutineKind::Async { .. }) => "async ",
Some(ast::CoroutineKind::Gen { .. }) => "gen ",
Some(ast::CoroutineKind::AsyncGen { .. }) => "async gen ",
None => "",
};
let coro = coroutine_marker.map_or_default(format_coro);
let capture_str = match capture {
ast::CaptureBy::Value { .. } => "move ",
ast::CaptureBy::Use { .. } => "use ",
Expand Down Expand Up @@ -370,7 +367,7 @@ pub(crate) fn rewrite_last_closure(
ref binder,
constness,
capture_clause,
ref coroutine_kind,
ref coroutine_marker,
movability,
ref fn_decl,
ref body,
Expand All @@ -392,7 +389,7 @@ pub(crate) fn rewrite_last_closure(
binder,
constness,
capture_clause,
coroutine_kind,
coroutine_marker,
movability,
fn_decl,
body,
Expand Down
2 changes: 1 addition & 1 deletion src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ pub(crate) fn format_expr(
&cl.binder,
cl.constness,
cl.capture_clause,
&cl.coroutine_kind,
&cl.coroutine_marker,
cl.movability,
&cl.fn_decl,
&cl.body,
Expand Down
30 changes: 15 additions & 15 deletions src/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ pub(crate) struct FnSig<'a> {
decl: &'a ast::FnDecl,
generics: &'a ast::Generics,
ext: ast::Extern,
coroutine_kind: Cow<'a, Option<ast::CoroutineKind>>,
coroutine_marker: &'a Option<ast::CoroutineMarker>,
constness: ast::Const,
defaultness: ast::Defaultness,
safety: ast::Safety,
Expand All @@ -313,7 +313,7 @@ impl<'a> FnSig<'a> {
) -> FnSig<'a> {
FnSig {
safety: method_sig.header.safety,
coroutine_kind: Cow::Borrowed(&method_sig.header.coroutine_kind),
coroutine_marker: &method_sig.header.coroutine_marker,
constness: method_sig.header.constness,
defaultness,
ext: method_sig.header.ext,
Expand All @@ -337,7 +337,7 @@ impl<'a> FnSig<'a> {
generics,
ext: sig.header.ext,
constness: sig.header.constness,
coroutine_kind: Cow::Borrowed(&sig.header.coroutine_kind),
coroutine_marker: &sig.header.coroutine_marker,
defaultness,
safety: sig.header.safety,
visibility: vis,
Expand All @@ -352,8 +352,8 @@ impl<'a> FnSig<'a> {
result.push_str(&*format_visibility(context, self.visibility));
result.push_str(format_defaultness(self.defaultness));
result.push_str(format_constness(self.constness));
self.coroutine_kind
.map(|coroutine_kind| result.push_str(format_coro(&coroutine_kind)));
self.coroutine_marker
.map(|coroutine_marker| result.push_str(format_coro(coroutine_marker)));
result.push_str(format_safety(self.safety));
result.push_str(&format_extern(
self.ext,
Expand Down Expand Up @@ -1525,7 +1525,7 @@ fn get_bytepos_after_visibility(vis: &ast::Visibility, default_span: Span) -> By

// Format tuple or struct without any fields. We need to make sure that the comments
// inside the delimiters are preserved.
fn format_empty_struct_or_tuple(
pub(crate) fn format_empty_struct_or_tuple(
context: &RewriteContext<'_>,
span: Span,
offset: Indent,
Expand Down Expand Up @@ -1887,8 +1887,8 @@ pub(crate) fn rewrite_struct_field_prefix(
field: &ast::FieldDef,
) -> RewriteResult {
let vis = format_visibility(context, &field.vis);
let mut_restriction = format_mut_restriction(context, &field.mut_restriction);
let safety = format_safety(field.safety);
let mut_restriction = format_mut_restriction(context, field.mut_restriction());
let safety = format_safety(field.safety());
let type_annotation_spacing = type_annotation_spacing(context.config);
Ok(match field.ident {
Some(name) => format!(
Expand Down Expand Up @@ -1917,7 +1917,7 @@ pub(crate) fn rewrite_struct_field(
lhs_max_width: usize,
) -> RewriteResult {
// FIXME(default_field_values): Implement formatting.
if field.default.is_some() {
if field.default_value().is_some() {
return Err(RewriteError::Unknown);
}

Expand Down Expand Up @@ -2010,7 +2010,7 @@ impl<'a> StaticParts<'a> {
),
ast::ItemKind::Const(c) => (
Some(c.defaultness),
if c.rhs_kind.is_type_const() {
if c.kind == ast::ConstItemKind::TypeConst {
"type const"
} else {
"const"
Expand All @@ -2019,7 +2019,7 @@ impl<'a> StaticParts<'a> {
c.ident,
&c.ty,
ast::Mutability::Not,
c.rhs_kind.expr(),
c.body.as_deref(),
Some(&c.generics),
),
_ => unreachable!(),
Expand All @@ -2041,15 +2041,15 @@ impl<'a> StaticParts<'a> {
pub(crate) fn from_trait_item(ti: &'a ast::AssocItem, ident: Ident) -> Self {
let (defaultness, ty, expr_opt, generics, prefix) = match &ti.kind {
ast::AssocItemKind::Const(c) => {
let prefix = if c.rhs_kind.is_type_const() {
let prefix = if c.kind == ast::ConstItemKind::TypeConst {
"type const"
} else {
"const"
};
(
c.defaultness,
&c.ty,
c.rhs_kind.expr(),
c.body.as_deref(),
Some(&c.generics),
prefix,
)
Expand All @@ -2073,15 +2073,15 @@ impl<'a> StaticParts<'a> {
pub(crate) fn from_impl_item(ii: &'a ast::AssocItem, ident: Ident) -> Self {
let (defaultness, ty, expr_opt, generics, prefix) = match &ii.kind {
ast::AssocItemKind::Const(c) => {
let prefix = if c.rhs_kind.is_type_const() {
let prefix = if c.kind == ast::ConstItemKind::TypeConst {
"type const"
} else {
"const"
};
(
c.defaultness,
&c.ty,
c.rhs_kind.expr(),
c.body.as_deref(),
Some(&c.generics),
prefix,
)
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ extern crate rustc_ast_pretty;
extern crate rustc_data_structures;
extern crate rustc_errors;
extern crate rustc_expand;
extern crate rustc_feature;
extern crate rustc_parse;
extern crate rustc_session;
extern crate rustc_span;
Expand Down
139 changes: 138 additions & 1 deletion src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
// List-like invocations with parentheses will be formatted as function calls,
// and those with brackets will be formatted as array literals.

use std::borrow::Cow;
use std::collections::HashMap;
use std::panic::{AssertUnwindSafe, catch_unwind};

use rustc_ast::ast;
use rustc_ast::token::{Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::{TokenStream, TokenStreamIter, TokenTree};
use rustc_ast_pretty::pprust;
use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol};
use rustc_span::{BytePos, DUMMY_SP, Ident, Pos, Span, Symbol};
use tracing::debug;

use crate::comment::{
Expand All @@ -26,8 +27,10 @@ use crate::config::StyleEdition;
use crate::config::lists::*;
use crate::expr::{RhsAssignKind, rewrite_array, rewrite_assign_rhs};
use crate::header::{HeaderPart, format_header};
use crate::is_nightly_channel;
use crate::lists::{ListFormatting, itemize_list, write_list};
use crate::overflow;
use crate::parse::macros::cfg_select::{CfgSelectFormatPredicate, parse_cfg_select_arms};
use crate::parse::macros::lazy_static::parse_lazy_static;
use crate::parse::macros::{ParsedMacroArgs, parse_expr, parse_macro_args};
use crate::rewrite::{
Expand Down Expand Up @@ -245,6 +248,26 @@ fn rewrite_macro_inner(
}
}

if is_nightly_channel!() && macro_name.ends_with("cfg_select!") {
match format_cfg_select(context, shape, mac.span(), &macro_name, style, ts.clone()) {
Ok(rw) => return Ok(rw),
Err(err) => match err {
// We will move on to parsing macro args just like other macros
// if we could not parse cfg_select! with known syntax
RewriteError::MacroFailure { kind, span: _ }
if kind == MacroErrorKind::ParseFailure => {}
// If formatting fails even though parsing succeeds, return the err early
other => return Err(other),
},
}
}

// If we're falling through to default macro handling check that the context is correct
debug_assert!(
context.inside_macro(),
"expect `context.inside_macro() == true`"
);

let ParsedMacroArgs {
args: arg_vec,
vec_with_semi,
Expand Down Expand Up @@ -1530,3 +1553,117 @@ fn rewrite_macro_with_items(
result.push_str(trailing_semicolon);
Ok(result)
}

fn format_cfg_select(
context: &RewriteContext<'_>,
shape: Shape,
span: Span,
name: &str,
delim_token: Delimiter,
ts: TokenStream,
) -> RewriteResult {
let mut rewrite = String::with_capacity((span.hi() - span.lo()).to_usize() * 2);
rewrite.push_str(name);

let (opening_delim, closing_delim) = match delim_token {
Delimiter::Brace => ("{", "}"),
Delimiter::Bracket => ("[", "]"),
Delimiter::Parenthesis => ("(", ")"),
Delimiter::Invisible(_) => {
unreachable!("cfg_select! macro will always have outer delimiters");
}
};

if matches!(delim_token, Delimiter::Brace) {
rewrite.push(' ');
};

let arms =
parse_cfg_select_arms(context.psess, ts).macro_error(MacroErrorKind::ParseFailure, span)?;

if arms.is_empty() {
let lo = context.snippet_provider.span_after(span, opening_delim);
let hi = context.snippet_provider.span_before(span, closing_delim);

// NOTE(ytmimi) reusing `format_empty_struct_or_tuple` since
// it handles proper indentation and recovering comments
crate::items::format_empty_struct_or_tuple(
context,
mk_sp(lo, hi),
shape.indent,
&mut rewrite,
opening_delim,
closing_delim,
);
return Ok(rewrite);
} else {
rewrite.push_str(opening_delim);
}

let nested_shape = shape.block_indent(context.config.tab_spaces());
rewrite.push_str(&nested_shape.indent.to_string_with_newline(context.config));

let last_arm = arms.last();

// We have to fib a little here and update the context to remove the `inside_macro` state.
// The code that flattens match arms will refuse to do so if it's inside a macro. Mostly
// this is done to prevent rustfmt from removing tokens in the context of a macro, but in
// this case it should be fine since we know that each `cfg_select!` arm must be a valid expr.
context.leave_macro();

let items = itemize_list(
context.snippet_provider,
arms.iter(),
closing_delim,
"}",
|arm| arm.span().lo(),
|arm| arm.span().hi(),
|arm| {
let predicate_str = match &arm.predicate {
CfgSelectFormatPredicate::Wildcard(_t) => Cow::Borrowed("_"),
CfgSelectFormatPredicate::Cfg(meta_item_inner) => {
Cow::Owned(meta_item_inner.rewrite_result(context, nested_shape)?)
}
};

crate::matches::rewrite_match_body(
context,
&arm.expr,
&predicate_str,
nested_shape,
false,
arm.arrow.span,
last_arm.is_some_and(|la| la == arm),
)
},
// Start Span after the opening delimiter. For example,
// ```
// cfg_select! {
// ^ start here
// }
// ```
context.snippet_provider.span_after(span, opening_delim),
// End on closing delimiter. For example,
// ```
// cfg_select! {
// }
// ^ end here
// ```
span.hi(),
false,
);
let arms_vec: Vec<_> = items.collect();

// We will add/remove commas inside `arm.rewrite()`, and hence no separator here.
let fmt = ListFormatting::new(nested_shape, context.config)
.separator("")
.align_comments(false)
.preserve_newline(true);

rewrite.push_str(&write_list(&arms_vec, &fmt)?);
rewrite.push('\n');
rewrite.push_str(&shape.indent.to_string(context.config));
rewrite.push_str(closing_delim);

Ok(rewrite)
}
2 changes: 1 addition & 1 deletion src/matches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ fn flatten_arm_body<'a>(
}
}

fn rewrite_match_body(
pub(crate) fn rewrite_match_body(
context: &RewriteContext<'_>,
body: &Box<ast::Expr>,
pats_str: &str,
Expand Down
Loading