Skip to content

Commit 17d516f

Browse files
committed
add rustfmt support for cfg_select
1 parent d242a8b commit 17d516f

File tree

5 files changed

+417
-50
lines changed

5 files changed

+417
-50
lines changed

src/tools/rustfmt/src/macros.rs

Lines changed: 187 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use rustc_ast_pretty::pprust;
1919
use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol};
2020
use tracing::debug;
2121

22+
use crate::Config;
2223
use crate::comment::{
2324
CharClasses, FindUncommented, FullCodeCharKind, LineClasses, contains_comment,
2425
};
@@ -27,6 +28,7 @@ use crate::config::lists::*;
2728
use crate::expr::{RhsAssignKind, rewrite_array, rewrite_assign_rhs};
2829
use crate::lists::{ListFormatting, itemize_list, write_list};
2930
use crate::overflow;
31+
use crate::parse::macros::cfg_select::parse_cfg_select;
3032
use crate::parse::macros::lazy_static::parse_lazy_static;
3133
use crate::parse::macros::{ParsedMacroArgs, parse_expr, parse_macro_args};
3234
use crate::rewrite::{
@@ -240,6 +242,20 @@ fn rewrite_macro_inner(
240242
}
241243
}
242244

245+
if macro_name.ends_with("cfg_select!") {
246+
match format_cfg_select(context, shape, ts.clone(), mac.span()) {
247+
Ok(rw) => return Ok(rw),
248+
Err(err) => match err {
249+
// We will move on to parsing macro args just like other macros
250+
// if we could not parse cfg_select! with known syntax
251+
RewriteError::MacroFailure { kind, span: _ }
252+
if kind == MacroErrorKind::ParseFailure => {}
253+
// If formatting fails even though parsing succeeds, return the err early
254+
other => return Err(other),
255+
},
256+
}
257+
}
258+
243259
let ParsedMacroArgs {
244260
args: arg_vec,
245261
vec_with_semi,
@@ -1288,17 +1304,19 @@ impl MacroBranch {
12881304

12891305
let old_body = context.snippet(self.body).trim();
12901306
let has_block_body = old_body.starts_with('{');
1291-
let mut prefix_width = 5; // 5 = " => {"
1292-
if context.config.style_edition() >= StyleEdition::Edition2024 {
1293-
if has_block_body {
1294-
prefix_width = 6; // 6 = " => {{"
1295-
}
1296-
}
1307+
1308+
let prefix =
1309+
if context.config.style_edition() >= StyleEdition::Edition2024 && has_block_body {
1310+
" => {{"
1311+
} else {
1312+
" => {"
1313+
};
1314+
12971315
let mut result = format_macro_args(
12981316
context,
12991317
self.args.clone(),
13001318
shape
1301-
.sub_width(prefix_width)
1319+
.sub_width(prefix.len())
13021320
.max_width_error(shape.width, self.span)?,
13031321
)?;
13041322

@@ -1321,10 +1339,63 @@ impl MacroBranch {
13211339
let (body_str, substs) =
13221340
replace_names(old_body).macro_error(MacroErrorKind::ReplaceMacroVariable, self.span)?;
13231341

1324-
let mut config = context.config.clone();
1325-
config.set().show_parse_errors(false);
1342+
let mut new_body =
1343+
Self::try_format_rhs(&context.config, shape, has_block_body, &body_str, self.span)?;
13261344

1327-
result += " {";
1345+
// Undo our replacement of macro variables.
1346+
// FIXME: this could be *much* more efficient.
1347+
for (old, new) in &substs {
1348+
if old_body.contains(new) {
1349+
debug!("rewrite_macro_def: bailing matching variable: `{}`", new);
1350+
return Err(RewriteError::MacroFailure {
1351+
kind: MacroErrorKind::ReplaceMacroVariable,
1352+
span: self.span,
1353+
});
1354+
}
1355+
new_body = new_body.replace(new, old);
1356+
}
1357+
1358+
Self::emit_formatted_body(
1359+
&context.config,
1360+
&shape,
1361+
&mut result,
1362+
has_block_body,
1363+
&new_body,
1364+
);
1365+
1366+
Ok(result)
1367+
}
1368+
1369+
fn emit_formatted_body(
1370+
config: &Config,
1371+
shape: &Shape,
1372+
result: &mut String,
1373+
has_block_body: bool,
1374+
body: &str,
1375+
) {
1376+
*result += " {";
1377+
1378+
if has_block_body {
1379+
*result += body.trim();
1380+
} else if !body.is_empty() {
1381+
*result += "\n";
1382+
*result += &body;
1383+
*result += &shape.indent.to_string(&config);
1384+
}
1385+
1386+
*result += "}";
1387+
}
1388+
1389+
fn try_format_rhs(
1390+
config: &Config,
1391+
shape: Shape,
1392+
has_block_body: bool,
1393+
body_str: &str,
1394+
span: Span,
1395+
) -> RewriteResult {
1396+
// This is a best-effort, reporting parse errors is not helpful.
1397+
let mut config = config.clone();
1398+
config.set().show_parse_errors(false);
13281399

13291400
let body_indent = if has_block_body {
13301401
shape.indent
@@ -1335,33 +1406,34 @@ impl MacroBranch {
13351406
config.set().max_width(new_width);
13361407

13371408
// First try to format as items, then as statements.
1338-
let new_body_snippet = match crate::format_snippet(&body_str, &config, true) {
1339-
Some(new_body) => new_body,
1340-
None => {
1341-
let new_width = new_width + config.tab_spaces();
1342-
config.set().max_width(new_width);
1343-
match crate::format_code_block(&body_str, &config, true) {
1344-
Some(new_body) => new_body,
1345-
None => {
1346-
return Err(RewriteError::MacroFailure {
1347-
kind: MacroErrorKind::Unknown,
1348-
span: self.span,
1349-
});
1350-
}
1351-
}
1409+
let new_body_snippet = 'blk: {
1410+
if let Some(new_body) = crate::format_snippet(&body_str, &config, true) {
1411+
break 'blk new_body;
13521412
}
1413+
1414+
let new_width = config.max_width() + config.tab_spaces();
1415+
config.set().max_width(new_width);
1416+
1417+
if let Some(new_body) = crate::format_code_block(&body_str, &config, true) {
1418+
break 'blk new_body;
1419+
}
1420+
1421+
return Err(RewriteError::MacroFailure {
1422+
kind: MacroErrorKind::Unknown,
1423+
span,
1424+
});
13531425
};
13541426

13551427
if !filtered_str_fits(&new_body_snippet.snippet, config.max_width(), shape) {
13561428
return Err(RewriteError::ExceedsMaxWidth {
13571429
configured_width: shape.width,
1358-
span: self.span,
1430+
span,
13591431
});
13601432
}
13611433

13621434
// Indent the body since it is in a block.
13631435
let indent_str = body_indent.to_string(&config);
1364-
let mut new_body = LineClasses::new(new_body_snippet.snippet.trim_end())
1436+
let new_body = LineClasses::new(new_body_snippet.snippet.trim_end())
13651437
.enumerate()
13661438
.fold(
13671439
(String::new(), true),
@@ -1377,30 +1449,7 @@ impl MacroBranch {
13771449
)
13781450
.0;
13791451

1380-
// Undo our replacement of macro variables.
1381-
// FIXME: this could be *much* more efficient.
1382-
for (old, new) in &substs {
1383-
if old_body.contains(new) {
1384-
debug!("rewrite_macro_def: bailing matching variable: `{}`", new);
1385-
return Err(RewriteError::MacroFailure {
1386-
kind: MacroErrorKind::ReplaceMacroVariable,
1387-
span: self.span,
1388-
});
1389-
}
1390-
new_body = new_body.replace(new, old);
1391-
}
1392-
1393-
if has_block_body {
1394-
result += new_body.trim();
1395-
} else if !new_body.is_empty() {
1396-
result += "\n";
1397-
result += &new_body;
1398-
result += &shape.indent.to_string(&config);
1399-
}
1400-
1401-
result += "}";
1402-
1403-
Ok(result)
1452+
Ok(new_body)
14041453
}
14051454
}
14061455

@@ -1464,6 +1513,94 @@ fn format_lazy_static(
14641513
Ok(result)
14651514
}
14661515

1516+
fn format_cfg_select(
1517+
context: &RewriteContext<'_>,
1518+
shape: Shape,
1519+
ts: TokenStream,
1520+
span: Span,
1521+
) -> RewriteResult {
1522+
let mut result = String::with_capacity(1024);
1523+
1524+
result.push_str("cfg_select! {");
1525+
1526+
let branches = parse_cfg_select(context, ts).macro_error(MacroErrorKind::ParseFailure, span)?;
1527+
1528+
let shape = shape
1529+
.block_indent(context.config.tab_spaces())
1530+
.with_max_width(context.config);
1531+
1532+
// The cfg plus ` => {` should stay within the line length.
1533+
let rule_shape = shape
1534+
.sub_width(" => {".len())
1535+
.max_width_error(shape.width, span)?;
1536+
1537+
result.push_str(&shape.indent.to_string_with_newline(context.config));
1538+
1539+
for (rule, rhs, _) in branches.reachable {
1540+
result.push_str(&rule.rewrite_result(context, rule_shape)?);
1541+
result.push_str(" =>");
1542+
result.push_str(&format_cfg_select_rhs(context, shape, rhs)?);
1543+
}
1544+
1545+
if let Some((_, rhs, _)) = branches.wildcard {
1546+
result.push_str("_ =>");
1547+
result.push_str(&format_cfg_select_rhs(context, shape, rhs)?);
1548+
}
1549+
1550+
for (lhs, rhs, _) in branches.unreachable {
1551+
use rustc_parse::parser::cfg_select::CfgSelectPredicate;
1552+
1553+
match lhs {
1554+
CfgSelectPredicate::Cfg(rule) => {
1555+
result.push_str(&rule.rewrite_result(context, rule_shape)?);
1556+
}
1557+
CfgSelectPredicate::Wildcard(_) => {
1558+
result.push('_');
1559+
}
1560+
}
1561+
1562+
result.push_str(" =>");
1563+
result.push_str(&format_cfg_select_rhs(context, shape, rhs)?);
1564+
}
1565+
1566+
// Emit the final `}` at the correct indentation level.
1567+
result.truncate(result.len() - context.config.tab_spaces());
1568+
result.push('}');
1569+
1570+
Ok(result)
1571+
}
1572+
1573+
fn format_cfg_select_rhs(
1574+
context: &RewriteContext<'_>,
1575+
shape: Shape,
1576+
ts: TokenStream,
1577+
) -> RewriteResult {
1578+
let has_block_body = false;
1579+
1580+
let span = ts
1581+
.iter()
1582+
.map(|tt| tt.span())
1583+
.reduce(Span::to)
1584+
.unwrap_or_default();
1585+
1586+
let old_body = context.snippet(span).trim();
1587+
let new_body =
1588+
MacroBranch::try_format_rhs(&context.config, shape, has_block_body, old_body, span)?;
1589+
1590+
let mut result = String::new();
1591+
MacroBranch::emit_formatted_body(
1592+
&context.config,
1593+
&shape,
1594+
&mut result,
1595+
has_block_body,
1596+
&new_body,
1597+
);
1598+
1599+
result.push_str(&shape.indent.to_string_with_newline(context.config));
1600+
1601+
Ok(result)
1602+
}
1603+
14671604
fn rewrite_macro_with_items(
14681605
context: &RewriteContext<'_>,
14691606
items: &[MacroArg],
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use rustc_ast::tokenstream::TokenStream;
2+
use rustc_parse::parser::{self, cfg_select::CfgSelectBranches};
3+
4+
use crate::rewrite::RewriteContext;
5+
6+
pub(crate) fn parse_cfg_select(
7+
context: &RewriteContext<'_>,
8+
ts: TokenStream,
9+
) -> Option<CfgSelectBranches> {
10+
let mut parser = super::build_parser(context, ts);
11+
parser::cfg_select::parse_cfg_select(&mut parser).ok()
12+
}

src/tools/rustfmt/src/parse/macros/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::rewrite::RewriteContext;
1111

1212
pub(crate) mod asm;
1313
pub(crate) mod cfg_if;
14+
pub(crate) mod cfg_select;
1415
pub(crate) mod lazy_static;
1516

1617
fn build_stream_parser<'a>(psess: &'a ParseSess, tokens: TokenStream) -> Parser<'a> {

0 commit comments

Comments
 (0)