diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index 1c5d804c6d465..c6d367a0fddd0 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -9,6 +9,7 @@ // 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}; @@ -16,7 +17,7 @@ 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::{ @@ -27,6 +28,7 @@ use crate::config::lists::*; use crate::expr::{RhsAssignKind, rewrite_array, rewrite_assign_rhs}; use crate::lists::{ListFormatting, itemize_list, write_list}; use crate::overflow; +use crate::parse::macros::cfg_select::{CfgSelectFormatPredicate, parse_cfg_select}; use crate::parse::macros::lazy_static::parse_lazy_static; use crate::parse::macros::{ParsedMacroArgs, parse_expr, parse_macro_args}; use crate::rewrite::{ @@ -244,6 +246,20 @@ fn rewrite_macro_inner( } } + if macro_name.ends_with("cfg_select!") { + match format_cfg_select(¯o_name, style, context, shape, ts.clone(), mac.span()) { + 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), + }, + } + } + let ParsedMacroArgs { args: arg_vec, vec_with_semi, @@ -397,16 +413,22 @@ fn rewrite_empty_macro_def_body( context: &RewriteContext<'_>, span: Span, shape: Shape, + delim_token: Delimiter, ) -> RewriteResult { - // Create an empty, dummy `ast::Block` representing an empty macro body - let block = ast::Block { - stmts: vec![].into(), - id: rustc_ast::node_id::DUMMY_NODE_ID, - rules: ast::BlockCheckMode::Default, - span, - tokens: None, - }; - block.rewrite_result(context, shape) + match delim_token { + Delimiter::Brace => { + // Create an empty, dummy `ast::Block` representing an empty macro body + let block = ast::Block { + stmts: vec![].into(), + id: rustc_ast::node_id::DUMMY_NODE_ID, + rules: ast::BlockCheckMode::Default, + span, + tokens: None, + }; + block.rewrite_result(context, shape) + } + _ => Ok(context.snippet(span).to_owned()), + } } pub(crate) fn rewrite_macro_def( @@ -452,7 +474,8 @@ pub(crate) fn rewrite_macro_def( if parsed_def.branches.len() == 0 { let lo = context.snippet_provider.span_before(span, "{"); result += " "; - result += &rewrite_empty_macro_def_body(context, span.with_lo(lo), shape)?; + result += + &rewrite_empty_macro_def_body(context, span.with_lo(lo), shape, Delimiter::Brace)?; return Ok(result); } @@ -1523,3 +1546,114 @@ fn rewrite_macro_with_items( result.push_str(trailing_semicolon); Ok(result) } + +fn format_cfg_select( + name: &str, + delim_token: Delimiter, + context: &RewriteContext<'_>, + shape: Shape, + ts: TokenStream, + span: Span, +) -> RewriteResult { + let mut rewrite = String::with_capacity((span.hi() - span.lo()).to_usize() * 2); + rewrite.push_str(name); + + let opening_delim = match delim_token { + Delimiter::Brace => "{", + Delimiter::Bracket => "[", + Delimiter::Parenthesis => "(", + Delimiter::Invisible(_) => { + unreachable!("cfg_selec! macro will always have outer delimiters"); + } + }; + + if matches!(delim_token, Delimiter::Brace) { + rewrite.push(' '); + }; + + let cfg_select_body_start = context.snippet_provider.span_before(span, opening_delim); + let arms = + parse_cfg_select(context.psess, ts).macro_error(MacroErrorKind::ParseFailure, span)?; + + if arms.is_empty() { + rewrite.push_str(&rewrite_empty_macro_def_body( + context, + span.with_lo(cfg_select_body_start), + shape, + delim_token, + )?); + 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(); + + let closing_delim = match delim_token { + Delimiter::Brace => "}", + Delimiter::Bracket => "]", + Delimiter::Parenthesis => ")", + Delimiter::Invisible(_) => { + unreachable!("cfg_selec! macro will always have outer delimiters"); + } + }; + + 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) +} diff --git a/src/tools/rustfmt/src/matches.rs b/src/tools/rustfmt/src/matches.rs index 4741abbe46583..a1e7a528daba8 100644 --- a/src/tools/rustfmt/src/matches.rs +++ b/src/tools/rustfmt/src/matches.rs @@ -394,7 +394,7 @@ fn flatten_arm_body<'a>( } } -fn rewrite_match_body( +pub(crate) fn rewrite_match_body( context: &RewriteContext<'_>, body: &Box, pats_str: &str, diff --git a/src/tools/rustfmt/src/parse/macros/cfg_select.rs b/src/tools/rustfmt/src/parse/macros/cfg_select.rs new file mode 100644 index 0000000000000..12a3fb7a9c253 --- /dev/null +++ b/src/tools/rustfmt/src/parse/macros/cfg_select.rs @@ -0,0 +1,108 @@ +use rustc_ast::tokenstream::TokenStream; +use rustc_ast::{ + ast, + token::{self, Token}, +}; +use rustc_parse::exp; +use rustc_span::Span; +use tracing::debug; + +use crate::parse::session::ParseSess; +use crate::spanned::Spanned; + +pub(crate) enum CfgSelectFormatPredicate { + Cfg(ast::MetaItemInner), + Wildcard(Span), +} + +impl Spanned for CfgSelectFormatPredicate { + fn span(&self) -> rustc_span::Span { + match self { + Self::Cfg(meta_item_inner) => meta_item_inner.span(), + Self::Wildcard(span) => *span, + } + } +} + +pub(crate) struct CfgSelectArm { + pub(crate) predicate: CfgSelectFormatPredicate, + pub(crate) arrow: Token, + pub(crate) expr: Box, + pub(crate) trailing_comma: Option, +} + +impl PartialEq for &CfgSelectArm { + fn eq(&self, other: &Self) -> bool { + // consider the arms equal if they have the same span + self.span() == other.span() + } +} + +impl Spanned for CfgSelectArm { + fn span(&self) -> Span { + self.predicate + .span() + .with_hi(if let Some(comma) = self.trailing_comma { + comma.hi() + } else { + self.expr.span.hi() + }) + } +} + +impl std::fmt::Debug for CfgSelectArm { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.predicate { + CfgSelectFormatPredicate::Cfg(cfg_entry) => cfg_entry.fmt(f)?, + CfgSelectFormatPredicate::Wildcard(t) => t.fmt(f)?, + }; + write!(f, "=> {:?}", self.expr) + } +} + +// FIXME(ytmimi) would be nice if rustfmt didn't need to implement parsing logic on its own +// and could instead just call rustc_attr_parsing::parse_cfg_select, but this is fine for now. +pub(crate) fn parse_cfg_select(psess: &ParseSess, ts: TokenStream) -> Option> { + let mut cfg_select_predicates = vec![]; + let mut parser = super::build_stream_parser(psess.inner(), ts); + + while parser.token != token::Eof { + let predicate = if parser.eat_keyword(exp!(Underscore)) { + CfgSelectFormatPredicate::Wildcard(parser.prev_token.span) + } else { + let Ok(meta_item) = parser.parse_meta_item_inner().map_err(|e| e.cancel()) else { + debug!("Failed to parse cfg entry in cfg_select! predicate"); + return None; + }; + CfgSelectFormatPredicate::Cfg(meta_item) + }; + + if let Err(_) = parser.expect(exp!(FatArrow)) { + debug!("Expected to find a `=>` after cfg_selec! predicate."); + return None; + }; + + let arrow = parser.prev_token; + + let Ok(expr) = parser.parse_expr().map_err(|e| e.cancel()) else { + debug!("Couldn't parse cfg_select! arm body after `=>`."); + return None; + }; + + let trailing_comma = if parser.eat(exp!(Comma)) { + Some(parser.prev_token.span) + } else { + None + }; + + let arm = CfgSelectArm { + predicate, + arrow, + expr, + trailing_comma, + }; + + cfg_select_predicates.push(arm); + } + Some(cfg_select_predicates) +} diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs index 00e0f6f58bd37..f9d18a275e220 100644 --- a/src/tools/rustfmt/src/parse/macros/mod.rs +++ b/src/tools/rustfmt/src/parse/macros/mod.rs @@ -11,6 +11,7 @@ use crate::rewrite::RewriteContext; pub(crate) mod cfg_if; pub(crate) mod cfg_match; +pub(crate) mod cfg_select; pub(crate) mod lazy_static; fn build_stream_parser<'a>(psess: &'a ParseSess, tokens: TokenStream) -> Parser<'a> { diff --git a/src/tools/rustfmt/tests/source/cfg_select.rs b/src/tools/rustfmt/tests/source/cfg_select.rs new file mode 100644 index 0000000000000..d3df3a5a7737e --- /dev/null +++ b/src/tools/rustfmt/tests/source/cfg_select.rs @@ -0,0 +1,1041 @@ +// rustfmt-style_edition: 2024 + +// empty cfg_select! +// Original `{}` delimiters +cfg_select! { +} +std::cfg_select! { +} +core::cfg_select! { +} + +// empty with other delimiters +// Original `()` delimiters +cfg_select! ( +); +std::cfg_select! ( +); +core::cfg_select! ( +); + +// Original `[]` delimiters +cfg_select! [ +]; +std::cfg_select! [ +]; +core::cfg_select! [ +]; + + +// Original `{}` delimiters +cfg_select! { /* inline comment */ +} +std::cfg_select! { /* inline comment */ +} +core::cfg_select! { /* inline comment */ +} + + +// Original `()` delimiters +cfg_select! ( /* inline comment */ +); +std::cfg_select! ( /* inline comment */ +); +core::cfg_select! ( /* inline comment */ +); + + +// Original `[]` delimiters +cfg_select! [ /* inline comment */ +]; +std::cfg_select! [ /* inline comment */ +]; +core::cfg_select! [ /* inline comment */ +]; + + +// Original `{}` delimiters +cfg_select! { // opening brace comment + +} +std::cfg_select! { // opening brace comment + +} +core::cfg_select! { // opening brace comment + +} + +// Original `()` delimiters +cfg_select! ( // opening brace comment + +); +std::cfg_select! ( // opening brace comment + +); +core::cfg_select! ( // opening brace comment + +); + +// Original `[]` delimiters +cfg_select! [ // opening brace comment + +]; +std::cfg_select! [ // opening brace comment + +]; +core::cfg_select! [ // opening brace comment + +]; + + +// Original `{}` delimiters +cfg_select! { + // nested inner comment +} +std::cfg_select! { + // nested inner comment +} +core::cfg_select! { + // nested inner comment +} + +// Original `()` delimiters +cfg_select! ( + // nested inner comment +); +std::cfg_select! ( + // nested inner comment +); +core::cfg_select! ( + // nested inner comment +); + +// Original `[]` delimiters +cfg_select! [ + // nested inner comment +]; +std::cfg_select! [ + // nested inner comment +]; +core::cfg_select! [ + // nested inner comment +]; + + +fn expression_position() { + // cfg_select arms with block + println!(cfg_select! { + unix => { "unix" } + windows => { "windows" } + _ => { "not " + "windows" + "or" + "unix" } + }); + + println!(std::cfg_select! { + unix => { "unix" } + windows => { "windows" } + _ => { "not " + "windows" + "or" + "unix" } + }); + + println!(core::cfg_select! { + unix => { "unix" } + windows => { "windows" } + _ => { "not " + "windows" + "or" + "unix" } + }); + + // cfg_select arms with block and trailing commas + println!(cfg_select! { + unix => { "unix" }, + windows => { "windows" }, + _ => { "not " + "windows" + "or" + "unix" }, + }); + + println!(std::cfg_select! { + unix => { "unix" }, + windows => { "windows" }, + _ => { "not " + "windows" + "or" + "unix" }, + }); + + println!(core::cfg_select! { + unix => { "unix" }, + windows => { "windows" }, + _ => { "not " + "windows" + "or" + "unix" }, + }); + + // cfg_select arms without block + println!(cfg_select! { + unix => "unix", + windows => "windows", + _ => "not windows or unix", + }); + + println!(std::cfg_select! { + unix => "unix", + windows => "windows", + _ => "not windows or unix", + }); + + println!(core::cfg_select! { + unix => "unix", + windows => "windows", + _ => "not windows or unix", + }); + + // cfg_select arms with and without blocks + println!(cfg_select! { + unix => {"unix"} + windows => {"windows"}, + _ => "not windows or unix", + }); + + println!(std::cfg_select! { + unix => {"unix"} + windows => {"windows"}, + _ => "not windows or unix", + }); + + println!(core::cfg_select! { + unix => {"unix"} + windows => {"windows"}, + _ => "not windows or unix", + }); +} + +// user specified newlines between arms are preserved +core::cfg_select! { + windows => {} + + + unix => { } + + + _ => {} +} + +core::cfg_select! ( + windows => {} + + + unix => { } + + + _ => {} +); + +core::cfg_select! [ + windows => {} + + + unix => { } + + + _ => {} +]; + + +// Leading comments are also preserved +core::cfg_select! { + // windows-Pre Comment + + windows => {} + // windows-Post comment + + + // unix-Pre Comment + + unix => { } + // unix-Post comment + + + // wildcard Comment + + _ => {} + // wildcard-Post comment +} + +// trailing comments work +cfg_select! { + windows => {} + // windows-Post comment + + + unix => { } + // unix-Post comment + + + + _ => {} + // wildcard-Post comment +} + +core::cfg_select! { + windows => {}// windows-Post comment + + + unix => { }// unix-Post comment + + _ => {}// wildcard-Post comment +} + +// trailing comments on the last line are a little buggy and always wrap back up +cfg_select! { + windows => {"windows"} + unix => {"unix"} + _ => {"none"} + // FIXME. Prevent wrapping back up to the next line +} + +cfg_select! { + windows => "windows", + unix => "unix", + _ => "none", + // FIXME. Prevent wrapping back up to the next line +} + + +// comments within the predicate are fine with style_edition=2024+ +cfg_select! { + any(true, /* comment */ + true, true, // true, + true, ) + // comment before arrow + => {} + + not(false // comment + ) => { + + } + + any(false // comment + ) => "any" +} + +// comments before and after the `=>` get dropped right now +cfg_select! { + any(true, + true, true, + true, ) + // commetn before arrow + => {} + + not(false + ) => /* comment before opening brace */ { + + } + + any(false + ) => // comment before brace + "any" +} + + +// A bunch of mixed predicates +cfg_select! { + // When all predicates are simple uses mixed list formatting + any(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true) => {} + + // more "complex" predicates will wrap using vertical formatting + all(target_arch = "x86_64", true, target_endian = "little", debug_assertions, panic = "unwind", target_env = "gnu") => {} + all(any(target_arch = "x86_64", true, target_endian = "little"), debug_assertions, panic = "unwind", all(target_env = "gnu", true)) => {} + + // nested "complex" predicates + all(target_arch = "x86_64", true, target_endian = "little", debug_assertions, panic = "unwind", target_env = "gnu", not(all(target_arch = "x86_64", true, target_endian = "little", debug_assertions, panic = "unwind", target_env = "gnu"))) => {} + + any(true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true) => {} + all( + any(target_arch = "x86_64", true, target_endian = "little"), debug_assertions, + panic = "unwind", all(target_env = "gnu", true) + ) => {} + + all( + any(target_arch = "x86_64", true, target_endian = "little"), + debug_assertions, + panic = "unwind", + all(target_env = "gnu", true) + ) => {} + + // This line is under 80 characters, no reason to break. + any(feature = "acdefg", true, true, true, true, true, true, true, true) => { + compile_error!("foo") + } + // The cfg is under 80 characters, but the line as a whole is over 80 characters. + any(feature = "acdefgh123", true, true, true, true, true, true, true, true) => { + compile_error!("foo") + } + // The cfg is over 80 characters. + not(any(feature = "acdefgh1234", true, true, true, true, true, true, true, true)) => { + compile_error!("foo") + } + // make sure that #![feature(cfg_version)] works + version("1.44.0") => { + } + + _ => {} +} + +// Can't format cfg_select! at all with style_edition <= 2021. +// Things can be formatted with style_edition >= 2024 +cfg_select! { + feature = "debug-with-rustfmt-long-long-long-long-loooooooonnnnnnnnnnnnnnnggggggffffffffffffffff" + => { + // abc + println!( + + ); + } + feature = "debug-with-rustfmt-long-long-long-long-loooooooonnnnnnnnnnnnnnnggggggffffffffffffffff" + => { + // abc + } + all(anything("some other long long long long long thing long long long long long long long long long long long", feature = "debug-with-rustfmt-long-long-long-long-loooooooonnnnnnnnnnnnnnnggggggffffffffffffffff")) => { + + let x = 7; + } +} + +std::cfg_select! { + target_arch = "aarch64" => { + use std::sync::OnceCell; + + fn foo() { + return 3; + } + + } + _ => { + compile_error!("mal", "formed") + } + false => { + compile_error!("also", "mal", "formed") + } +} + + +mod nested { + +// empty cfg_select! +// Original `{}` delimiters +cfg_select! { +} +std::cfg_select! { +} +core::cfg_select! { +} + +// empty with other delimiters +// Original `()` delimiters +cfg_select! ( +); +std::cfg_select! ( +); +core::cfg_select! ( +); + +// Original `[]` delimiters +cfg_select! [ +]; +std::cfg_select! [ +]; +core::cfg_select! [ +]; + + +// Original `{}` delimiters +cfg_select! { /* inline comment */ +} +std::cfg_select! { /* inline comment */ +} +core::cfg_select! { /* inline comment */ +} + + +// Original `()` delimiters +cfg_select! ( /* inline comment */ +); +std::cfg_select! ( /* inline comment */ +); +core::cfg_select! ( /* inline comment */ +); + + +// Original `[]` delimiters +cfg_select! [ /* inline comment */ +]; +std::cfg_select! [ /* inline comment */ +]; +core::cfg_select! [ /* inline comment */ +]; + + +// Original `{}` delimiters +cfg_select! { // opening brace comment + +} +std::cfg_select! { // opening brace comment + +} +core::cfg_select! { // opening brace comment + +} + +// Original `()` delimiters +cfg_select! ( // opening brace comment + +); +std::cfg_select! ( // opening brace comment + +); +core::cfg_select! ( // opening brace comment + +); + +// Original `[]` delimiters +cfg_select! [ // opening brace comment + +]; +std::cfg_select! [ // opening brace comment + +]; +core::cfg_select! [ // opening brace comment + +]; + + +// Original `{}` delimiters +cfg_select! { + // nested inner comment +} +std::cfg_select! { + // nested inner comment +} +core::cfg_select! { + // nested inner comment +} + +// Original `()` delimiters +cfg_select! ( + // nested inner comment +); +std::cfg_select! ( + // nested inner comment +); +core::cfg_select! ( + // nested inner comment +); + +// Original `[]` delimiters +cfg_select! [ + // nested inner comment +]; +std::cfg_select! [ + // nested inner comment +]; +core::cfg_select! [ + // nested inner comment +]; + + +fn expression_position() { + // cfg_select arms with block + println!(cfg_select! { + unix => { "unix" } + windows => { "windows" } + _ => { "not " + "windows" + "or" + "unix" } + }); + + println!(std::cfg_select! { + unix => { "unix" } + windows => { "windows" } + _ => { "not " + "windows" + "or" + "unix" } + }); + + println!(core::cfg_select! { + unix => { "unix" } + windows => { "windows" } + _ => { "not " + "windows" + "or" + "unix" } + }); + + // cfg_select arms with block and trailing commas + println!(cfg_select! { + unix => { "unix" }, + windows => { "windows" }, + _ => { "not " + "windows" + "or" + "unix" }, + }); + + println!(std::cfg_select! { + unix => { "unix" }, + windows => { "windows" }, + _ => { "not " + "windows" + "or" + "unix" }, + }); + + println!(core::cfg_select! { + unix => { "unix" }, + windows => { "windows" }, + _ => { "not " + "windows" + "or" + "unix" }, + }); + + // cfg_select arms without block + println!(cfg_select! { + unix => "unix", + windows => "windows", + _ => "not windows or unix", + }); + + println!(std::cfg_select! { + unix => "unix", + windows => "windows", + _ => "not windows or unix", + }); + + println!(core::cfg_select! { + unix => "unix", + windows => "windows", + _ => "not windows or unix", + }); + + // cfg_select arms with and without blocks + println!(cfg_select! { + unix => {"unix"} + windows => {"windows"}, + _ => "not windows or unix", + }); + + println!(std::cfg_select! { + unix => {"unix"} + windows => {"windows"}, + _ => "not windows or unix", + }); + + println!(core::cfg_select! { + unix => {"unix"} + windows => {"windows"}, + _ => "not windows or unix", + }); +} + +// user specified newlines between arms are preserved +core::cfg_select! { + windows => {} + + + unix => { } + + + _ => {} +} + +core::cfg_select! ( + windows => {} + + + unix => { } + + + _ => {} +); + +core::cfg_select! [ + windows => {} + + + unix => { } + + + _ => {} +]; + + +// Leading comments are also preserved +core::cfg_select! { + // windows-Pre Comment + + windows => {} + // windows-Post comment + + + // unix-Pre Comment + + unix => { } + // unix-Post comment + + + // wildcard Comment + + _ => {} + // wildcard-Post comment +} + +// trailing comments work +cfg_select! { + windows => {} + // windows-Post comment + + + unix => { } + // unix-Post comment + + + + _ => {} + // wildcard-Post comment +} + +core::cfg_select! { + windows => {}// windows-Post comment + + + unix => { }// unix-Post comment + + _ => {}// wildcard-Post comment +} + +// trailing comments on the last line are a little buggy and always wrap back up +cfg_select! { + windows => {"windows"} + unix => {"unix"} + _ => {"none"} + // FIXME. Prevent wrapping back up to the next line +} + +cfg_select! { + windows => "windows", + unix => "unix", + _ => "none", + // FIXME. Prevent wrapping back up to the next line +} + + +// comments within the predicate are fine with style_edition=2024+ +cfg_select! { + any(true, /* comment */ + true, true, // true, + true, ) + // comment before arrow + => {} + + not(false // comment + ) => { + + } + + any(false // comment + ) => "any" +} + +// comments before and after the `=>` get dropped right now +cfg_select! { + any(true, + true, true, + true, ) + // commetn before arrow + => {} + + not(false + ) => /* comment before opening brace */ { + + } + + any(false + ) => // comment before brace + "any" +} + + +// A bunch of mixed predicates +cfg_select! { + // When all predicates are simple uses mixed list formatting + any(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true) => {} + + // more "complex" predicates will wrap using vertical formatting + all(target_arch = "x86_64", true, target_endian = "little", debug_assertions, panic = "unwind", target_env = "gnu") => {} + all(any(target_arch = "x86_64", true, target_endian = "little"), debug_assertions, panic = "unwind", all(target_env = "gnu", true)) => {} + + // nested "complex" predicates + all(target_arch = "x86_64", true, target_endian = "little", debug_assertions, panic = "unwind", target_env = "gnu", not(all(target_arch = "x86_64", true, target_endian = "little", debug_assertions, panic = "unwind", target_env = "gnu"))) => {} + + any(true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true) => {} + all( + any(target_arch = "x86_64", true, target_endian = "little"), debug_assertions, + panic = "unwind", all(target_env = "gnu", true) + ) => {} + + all( + any(target_arch = "x86_64", true, target_endian = "little"), + debug_assertions, + panic = "unwind", + all(target_env = "gnu", true) + ) => {} + + // This line is under 80 characters, no reason to break. + any(feature = "acdefg", true, true, true, true, true, true, true, true) => { + compile_error!("foo") + } + // The cfg is under 80 characters, but the line as a whole is over 80 characters. + any(feature = "acdefgh123", true, true, true, true, true, true, true, true) => { + compile_error!("foo") + } + // The cfg is over 80 characters. + not(any(feature = "acdefgh1234", true, true, true, true, true, true, true, true)) => { + compile_error!("foo") + } + // make sure that #![feature(cfg_version)] works + version("1.44.0") => { + } + + _ => {} +} + +// Can't format cfg_select! at all with style_edition <= 2021. +// Things can be formatted with style_edition >= 2024 +cfg_select! { + feature = "debug-with-rustfmt-long-long-long-long-loooooooonnnnnnnnnnnnnnnggggggffffffffffffffff" + => { + // abc + println!( + + ); + } + feature = "debug-with-rustfmt-long-long-long-long-loooooooonnnnnnnnnnnnnnnggggggffffffffffffffff" + => { + // abc + } + all(anything("some other long long long long long thing long long long long long long long long long long long", feature = "debug-with-rustfmt-long-long-long-long-loooooooonnnnnnnnnnnnnnnggggggffffffffffffffff")) => { + + let x = 7; + } +} + +std::cfg_select! { + target_arch = "aarch64" => { + use std::sync::OnceCell; + + fn foo() { + return 3; + } + + } + _ => { + compile_error!("mal", "formed") + } + false => { + compile_error!("also", "mal", "formed") + } +} + +} + + +// Some examples I pulled from rust-lang/rust +#[cfg(target_env = "musl")] +cfg_select! { + all(feature = "llvm-libunwind", feature = "system-llvm-libunwind") => { + compile_error!("`llvm-libunwind` and `system-llvm-libunwind` cannot be enabled at the same time"); + } + feature = "llvm-libunwind" => { + #[link(name = "unwind", kind = "static", modifiers = "-bundle")] + unsafe extern "C" {} + } + feature = "system-llvm-libunwind" => { + #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] + #[link(name = "unwind", cfg(not(target_feature = "crt-static")))] + unsafe extern "C" {} + } + _ => { + #[link(name = "unwind", kind = "static", modifiers = "-bundle", cfg(target_feature = "crt-static"))] + #[link(name = "gcc_s", cfg(all(not(target_feature = "crt-static"), not(target_arch = "hexagon"))))] + unsafe extern "C" {} + } +} + +pub const fn midpoint(self, other: f32) -> f32 { + cfg_select! { + // Allow faster implementation that have known good 64-bit float + // implementations. Falling back to the branchy code on targets that don't + // have 64-bit hardware floats or buggy implementations. + // https://github.com/rust-lang/rust/pull/121062#issuecomment-2123408114 + any( + target_arch = "x86_64", + target_arch = "aarch64", + all(any(target_arch = "riscv32", target_arch = "riscv64"), target_feature = "d"), + all(target_arch = "loongarch64", target_feature = "d"), + all(target_arch = "arm", target_feature = "vfp2"), + target_arch = "wasm32", + target_arch = "wasm64", + ) => { + ((self as f64 + other as f64) / 2.0) as f32 + } + _ => { + const HI: f32 = f32::MAX / 2.; + + let (a, b) = (self, other); + let abs_a = a.abs(); + let abs_b = b.abs(); + + if abs_a <= HI && abs_b <= HI { + // Overflow is impossible + (a + b) / 2. + } else { + (a / 2.) + (b / 2.) + } + } + } +} + +mod c_int_definition { + crate::cfg_select! { + any(target_arch = "avr", target_arch = "msp430") => { + pub(super) type c_int = i16; + pub(super) type c_uint = u16; + } + _ => { + pub(super) type c_int = i32; + pub(super) type c_uint = u32; + } + } +} + +cfg_select! { + any( + target_family = "unix", + target_os = "wasi", + target_os = "teeos", + target_os = "trusty", + ) => { + mod unix; + } + target_os = "windows" => { + mod windows; + } + target_os = "hermit" => { + mod hermit; + } + target_os = "motor" => { + mod motor; + } + all(target_vendor = "fortanix", target_env = "sgx") => { + mod sgx; + } + target_os = "solid_asp3" => { + mod solid; + } + target_os = "uefi" => { + mod uefi; + } + target_os = "vexos" => { + mod vexos; + } + target_family = "wasm" => { + mod wasm; + } + target_os = "xous" => { + mod xous; + } + target_os = "zkvm" => { + mod zkvm; + } +} + +// Other rust-lang/rust ui tests to cover other expansion sites +fn arm_rhs_expr_3() -> i32 { + cfg_select! { + any(true) => 1, + any(false) => 2, + any(true) => { 42 } + any(true) => { 42 }, + any(false) => -1 as i32, + any(true) => 2 + 2, + any(false) => "", + any(true) => if true { 42 } else { 84 } + any(false) => if true { 42 } else { 84 }, + any(true) => return 42, + any(false) => loop {} + any(true) => (1, 2), + any(false) => (1, 2,), + any(true) => todo!(), + any(false) => println!("hello"), + } +} + +fn expand_to_statements() -> i32 { + cfg_select! { + false => { + let b = 2; + b + 1 + } + true => { + let a = 1; + a + 1 + } + } +} + +type ExpandToType = cfg_select! { + unix => { u32 }, + _ => i32, +}; + +fn expand_to_pattern(x: Option) -> bool { + match x { + (cfg_select! { + unix => Some(n), + _ => None, + }) => true, + _ => false, + } +} + +cfg_select! { + false => { + fn foo() {} + } + _ => { + fn bar() {} + } +} + +struct S; + +impl S { + cfg_select! { + false => { + fn foo() {} + } + _ => { + fn bar() {} + } + } +} + +trait T { + cfg_select! { + false => { + fn a(); + } + _ => { + fn b(); + } + } +} + +impl T for S { + cfg_select! { + false => { + fn a() {} + }, + _ => { + fn b() {} + } + } +} + +extern "C" { + cfg_select! { + false => { + fn puts(s: *const i8) -> i32; + } + _ => { + fn printf(fmt: *const i8, ...) -> i32; + } + } +} diff --git a/src/tools/rustfmt/tests/target/cfg_select.rs b/src/tools/rustfmt/tests/target/cfg_select.rs new file mode 100644 index 0000000000000..ed6f531ab0f41 --- /dev/null +++ b/src/tools/rustfmt/tests/target/cfg_select.rs @@ -0,0 +1,1156 @@ +// rustfmt-style_edition: 2024 + +// empty cfg_select! +// Original `{}` delimiters +cfg_select! {} +std::cfg_select! {} +core::cfg_select! {} + +// empty with other delimiters +// Original `()` delimiters +cfg_select!(); +std::cfg_select!(); +core::cfg_select!(); + +// Original `[]` delimiters +cfg_select![]; +std::cfg_select![]; +core::cfg_select![]; + +// Original `{}` delimiters +cfg_select! { /* inline comment */ } +std::cfg_select! { /* inline comment */ } +core::cfg_select! { /* inline comment */ } + +// Original `()` delimiters +cfg_select!( /* inline comment */ +); +std::cfg_select!( /* inline comment */ +); +core::cfg_select!( /* inline comment */ +); + +// Original `[]` delimiters +cfg_select![ /* inline comment */ +]; +std::cfg_select![ /* inline comment */ +]; +core::cfg_select![ /* inline comment */ +]; + +// Original `{}` delimiters +cfg_select! { // opening brace comment +} +std::cfg_select! { // opening brace comment +} +core::cfg_select! { // opening brace comment +} + +// Original `()` delimiters +cfg_select!( // opening brace comment + +); +std::cfg_select!( // opening brace comment + +); +core::cfg_select!( // opening brace comment + +); + +// Original `[]` delimiters +cfg_select![ // opening brace comment + +]; +std::cfg_select![ // opening brace comment + +]; +core::cfg_select![ // opening brace comment + +]; + +// Original `{}` delimiters +cfg_select! { + // nested inner comment +} +std::cfg_select! { + // nested inner comment +} +core::cfg_select! { + // nested inner comment +} + +// Original `()` delimiters +cfg_select!( + // nested inner comment +); +std::cfg_select!( + // nested inner comment +); +core::cfg_select!( + // nested inner comment +); + +// Original `[]` delimiters +cfg_select![ + // nested inner comment +]; +std::cfg_select![ + // nested inner comment +]; +core::cfg_select![ + // nested inner comment +]; + +fn expression_position() { + // cfg_select arms with block + println!(cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => { + "not " + "windows" + "or" + "unix" + } + }); + + println!(std::cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => { + "not " + "windows" + "or" + "unix" + } + }); + + println!(core::cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => { + "not " + "windows" + "or" + "unix" + } + }); + + // cfg_select arms with block and trailing commas + println!(cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => { + "not " + "windows" + "or" + "unix" + } + }); + + println!(std::cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => { + "not " + "windows" + "or" + "unix" + } + }); + + println!(core::cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => { + "not " + "windows" + "or" + "unix" + } + }); + + // cfg_select arms without block + println!(cfg_select! { + unix => "unix", + windows => "windows", + _ => "not windows or unix", + }); + + println!(std::cfg_select! { + unix => "unix", + windows => "windows", + _ => "not windows or unix", + }); + + println!(core::cfg_select! { + unix => "unix", + windows => "windows", + _ => "not windows or unix", + }); + + // cfg_select arms with and without blocks + println!(cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => "not windows or unix", + }); + + println!(std::cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => "not windows or unix", + }); + + println!(core::cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => "not windows or unix", + }); +} + +// user specified newlines between arms are preserved +core::cfg_select! { + windows => {} + + unix => {} + + _ => {} +} + +core::cfg_select!( + windows => {} + + unix => {} + + _ => {} +); + +core::cfg_select![ + windows => {} + + unix => {} + + _ => {} +]; + +// Leading comments are also preserved +core::cfg_select! { + // windows-Pre Comment + windows => {} + // windows-Post comment + + // unix-Pre Comment + unix => {} + // unix-Post comment + + // wildcard Comment + _ => {} // wildcard-Post comment +} + +// trailing comments work +cfg_select! { + windows => {} + // windows-Post comment + unix => {} + // unix-Post comment + _ => {} // wildcard-Post comment +} + +core::cfg_select! { + windows => {} // windows-Post comment + + unix => {} // unix-Post comment + + _ => {} // wildcard-Post comment +} + +// trailing comments on the last line are a little buggy and always wrap back up +cfg_select! { + windows => { + "windows" + } + unix => { + "unix" + } + _ => { + "none" + } // FIXME. Prevent wrapping back up to the next line +} + +cfg_select! { + windows => "windows", + unix => "unix", + _ => "none", // FIXME. Prevent wrapping back up to the next line +} + +// comments within the predicate are fine with style_edition=2024+ +cfg_select! { + any( + true, /* comment */ + true, true, // true, + true, + ) => {} + + not( + false // comment + ) => {} + + any( + false // comment + ) => "any", +} + +// comments before and after the `=>` get dropped right now +cfg_select! { + any(true, true, true, true,) => {} + + not(false) => {} + + any(false) => "any", +} + +// A bunch of mixed predicates +cfg_select! { + // When all predicates are simple uses mixed list formatting + any( + true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true + ) => {} + + // more "complex" predicates will wrap using vertical formatting + all( + target_arch = "x86_64", + true, + target_endian = "little", + debug_assertions, + panic = "unwind", + target_env = "gnu" + ) => {} + all( + any(target_arch = "x86_64", true, target_endian = "little"), + debug_assertions, + panic = "unwind", + all(target_env = "gnu", true) + ) => {} + + // nested "complex" predicates + all( + target_arch = "x86_64", + true, + target_endian = "little", + debug_assertions, + panic = "unwind", + target_env = "gnu", + not(all( + target_arch = "x86_64", + true, + target_endian = "little", + debug_assertions, + panic = "unwind", + target_env = "gnu" + )) + ) => {} + + any( + true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true + ) => {} + all( + any(target_arch = "x86_64", true, target_endian = "little"), + debug_assertions, + panic = "unwind", + all(target_env = "gnu", true) + ) => {} + + all( + any(target_arch = "x86_64", true, target_endian = "little"), + debug_assertions, + panic = "unwind", + all(target_env = "gnu", true) + ) => {} + + // This line is under 80 characters, no reason to break. + any(feature = "acdefg", true, true, true, true, true, true, true, true) => { + compile_error!("foo") + } + // The cfg is under 80 characters, but the line as a whole is over 80 characters. + any(feature = "acdefgh123", true, true, true, true, true, true, true, true) => { + compile_error!("foo") + } + // The cfg is over 80 characters. + not(any( + feature = "acdefgh1234", + true, + true, + true, + true, + true, + true, + true, + true + )) => { + compile_error!("foo") + } + // make sure that #![feature(cfg_version)] works + version("1.44.0") => {} + + _ => {} +} + +// Can't format cfg_select! at all with style_edition <= 2021. +// Things can be formatted with style_edition >= 2024 +cfg_select! { + feature = "debug-with-rustfmt-long-long-long-long-loooooooonnnnnnnnnnnnnnnggggggffffffffffffffff" => + { + // abc + println!(); + } + feature = "debug-with-rustfmt-long-long-long-long-loooooooonnnnnnnnnnnnnnnggggggffffffffffffffff" => + { + // abc + } + all(anything( + "some other long long long long long thing long long long long long long long long long long long", + feature = "debug-with-rustfmt-long-long-long-long-loooooooonnnnnnnnnnnnnnnggggggffffffffffffffff" + )) => { + let x = 7; + } +} + +std::cfg_select! { + target_arch = "aarch64" => { + use std::sync::OnceCell; + + fn foo() { + return 3; + } + } + _ => { + compile_error!("mal", "formed") + } + false => { + compile_error!("also", "mal", "formed") + } +} + +mod nested { + + // empty cfg_select! + // Original `{}` delimiters + cfg_select! {} + std::cfg_select! {} + core::cfg_select! {} + + // empty with other delimiters + // Original `()` delimiters + cfg_select!(); + std::cfg_select!(); + core::cfg_select!(); + + // Original `[]` delimiters + cfg_select![]; + std::cfg_select![]; + core::cfg_select![]; + + // Original `{}` delimiters + cfg_select! { /* inline comment */ } + std::cfg_select! { /* inline comment */ } + core::cfg_select! { /* inline comment */ } + + // Original `()` delimiters + cfg_select!( /* inline comment */ +); + std::cfg_select!( /* inline comment */ +); + core::cfg_select!( /* inline comment */ +); + + // Original `[]` delimiters + cfg_select![ /* inline comment */ +]; + std::cfg_select![ /* inline comment */ +]; + core::cfg_select![ /* inline comment */ +]; + + // Original `{}` delimiters + cfg_select! { // opening brace comment + } + std::cfg_select! { // opening brace comment + } + core::cfg_select! { // opening brace comment + } + + // Original `()` delimiters + cfg_select!( // opening brace comment + +); + std::cfg_select!( // opening brace comment + +); + core::cfg_select!( // opening brace comment + +); + + // Original `[]` delimiters + cfg_select![ // opening brace comment + +]; + std::cfg_select![ // opening brace comment + +]; + core::cfg_select![ // opening brace comment + +]; + + // Original `{}` delimiters + cfg_select! { + // nested inner comment + } + std::cfg_select! { + // nested inner comment + } + core::cfg_select! { + // nested inner comment + } + + // Original `()` delimiters + cfg_select!( + // nested inner comment +); + std::cfg_select!( + // nested inner comment +); + core::cfg_select!( + // nested inner comment +); + + // Original `[]` delimiters + cfg_select![ + // nested inner comment +]; + std::cfg_select![ + // nested inner comment +]; + core::cfg_select![ + // nested inner comment +]; + + fn expression_position() { + // cfg_select arms with block + println!(cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => { + "not " + "windows" + "or" + "unix" + } + }); + + println!(std::cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => { + "not " + "windows" + "or" + "unix" + } + }); + + println!(core::cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => { + "not " + "windows" + "or" + "unix" + } + }); + + // cfg_select arms with block and trailing commas + println!(cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => { + "not " + "windows" + "or" + "unix" + } + }); + + println!(std::cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => { + "not " + "windows" + "or" + "unix" + } + }); + + println!(core::cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => { + "not " + "windows" + "or" + "unix" + } + }); + + // cfg_select arms without block + println!(cfg_select! { + unix => "unix", + windows => "windows", + _ => "not windows or unix", + }); + + println!(std::cfg_select! { + unix => "unix", + windows => "windows", + _ => "not windows or unix", + }); + + println!(core::cfg_select! { + unix => "unix", + windows => "windows", + _ => "not windows or unix", + }); + + // cfg_select arms with and without blocks + println!(cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => "not windows or unix", + }); + + println!(std::cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => "not windows or unix", + }); + + println!(core::cfg_select! { + unix => { + "unix" + } + windows => { + "windows" + } + _ => "not windows or unix", + }); + } + + // user specified newlines between arms are preserved + core::cfg_select! { + windows => {} + + unix => {} + + _ => {} + } + + core::cfg_select!( + windows => {} + + unix => {} + + _ => {} + ); + + core::cfg_select![ + windows => {} + + unix => {} + + _ => {} + ]; + + // Leading comments are also preserved + core::cfg_select! { + // windows-Pre Comment + windows => {} + // windows-Post comment + + // unix-Pre Comment + unix => {} + // unix-Post comment + + // wildcard Comment + _ => {} // wildcard-Post comment + } + + // trailing comments work + cfg_select! { + windows => {} + // windows-Post comment + unix => {} + // unix-Post comment + _ => {} // wildcard-Post comment + } + + core::cfg_select! { + windows => {} // windows-Post comment + + unix => {} // unix-Post comment + + _ => {} // wildcard-Post comment + } + + // trailing comments on the last line are a little buggy and always wrap back up + cfg_select! { + windows => { + "windows" + } + unix => { + "unix" + } + _ => { + "none" + } // FIXME. Prevent wrapping back up to the next line + } + + cfg_select! { + windows => "windows", + unix => "unix", + _ => "none", // FIXME. Prevent wrapping back up to the next line + } + + // comments within the predicate are fine with style_edition=2024+ + cfg_select! { + any( + true, /* comment */ + true, true, // true, + true, + ) => {} + + not( + false // comment + ) => {} + + any( + false // comment + ) => "any", + } + + // comments before and after the `=>` get dropped right now + cfg_select! { + any(true, true, true, true,) => {} + + not(false) => {} + + any(false) => "any", + } + + // A bunch of mixed predicates + cfg_select! { + // When all predicates are simple uses mixed list formatting + any( + true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true + ) => {} + + // more "complex" predicates will wrap using vertical formatting + all( + target_arch = "x86_64", + true, + target_endian = "little", + debug_assertions, + panic = "unwind", + target_env = "gnu" + ) => {} + all( + any(target_arch = "x86_64", true, target_endian = "little"), + debug_assertions, + panic = "unwind", + all(target_env = "gnu", true) + ) => {} + + // nested "complex" predicates + all( + target_arch = "x86_64", + true, + target_endian = "little", + debug_assertions, + panic = "unwind", + target_env = "gnu", + not(all( + target_arch = "x86_64", + true, + target_endian = "little", + debug_assertions, + panic = "unwind", + target_env = "gnu" + )) + ) => {} + + any( + true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true + ) => {} + all( + any(target_arch = "x86_64", true, target_endian = "little"), + debug_assertions, + panic = "unwind", + all(target_env = "gnu", true) + ) => {} + + all( + any(target_arch = "x86_64", true, target_endian = "little"), + debug_assertions, + panic = "unwind", + all(target_env = "gnu", true) + ) => {} + + // This line is under 80 characters, no reason to break. + any(feature = "acdefg", true, true, true, true, true, true, true, true) => { + compile_error!("foo") + } + // The cfg is under 80 characters, but the line as a whole is over 80 characters. + any(feature = "acdefgh123", true, true, true, true, true, true, true, true) => { + compile_error!("foo") + } + // The cfg is over 80 characters. + not(any( + feature = "acdefgh1234", + true, + true, + true, + true, + true, + true, + true, + true + )) => { + compile_error!("foo") + } + // make sure that #![feature(cfg_version)] works + version("1.44.0") => {} + + _ => {} + } + + // Can't format cfg_select! at all with style_edition <= 2021. + // Things can be formatted with style_edition >= 2024 + cfg_select! { + feature = "debug-with-rustfmt-long-long-long-long-loooooooonnnnnnnnnnnnnnnggggggffffffffffffffff" => + { + // abc + println!(); + } + feature = "debug-with-rustfmt-long-long-long-long-loooooooonnnnnnnnnnnnnnnggggggffffffffffffffff" => + { + // abc + } + all(anything( + "some other long long long long long thing long long long long long long long long long long long", + feature = "debug-with-rustfmt-long-long-long-long-loooooooonnnnnnnnnnnnnnnggggggffffffffffffffff" + )) => { + let x = 7; + } + } + + std::cfg_select! { + target_arch = "aarch64" => { + use std::sync::OnceCell; + + fn foo() { + return 3; + } + } + _ => { + compile_error!("mal", "formed") + } + false => { + compile_error!("also", "mal", "formed") + } + } +} + +// Some examples I pulled from rust-lang/rust +#[cfg(target_env = "musl")] +cfg_select! { + all(feature = "llvm-libunwind", feature = "system-llvm-libunwind") => { + compile_error!( + "`llvm-libunwind` and `system-llvm-libunwind` cannot be enabled at the same time" + ); + } + feature = "llvm-libunwind" => { + #[link(name = "unwind", kind = "static", modifiers = "-bundle")] + unsafe extern "C" {} + } + feature = "system-llvm-libunwind" => { + #[link( + name = "unwind", + kind = "static", + modifiers = "-bundle", + cfg(target_feature = "crt-static") + )] + #[link(name = "unwind", cfg(not(target_feature = "crt-static")))] + unsafe extern "C" {} + } + _ => { + #[link( + name = "unwind", + kind = "static", + modifiers = "-bundle", + cfg(target_feature = "crt-static") + )] + #[link( + name = "gcc_s", + cfg(all(not(target_feature = "crt-static"), not(target_arch = "hexagon"))) + )] + unsafe extern "C" {} + } +} + +pub const fn midpoint(self, other: f32) -> f32 { + cfg_select! { + // Allow faster implementation that have known good 64-bit float + // implementations. Falling back to the branchy code on targets that don't + // have 64-bit hardware floats or buggy implementations. + // https://github.com/rust-lang/rust/pull/121062#issuecomment-2123408114 + any( + target_arch = "x86_64", + target_arch = "aarch64", + all( + any(target_arch = "riscv32", target_arch = "riscv64"), + target_feature = "d" + ), + all(target_arch = "loongarch64", target_feature = "d"), + all(target_arch = "arm", target_feature = "vfp2"), + target_arch = "wasm32", + target_arch = "wasm64", + ) => { + ((self as f64 + other as f64) / 2.0) as f32 + } + _ => { + const HI: f32 = f32::MAX / 2.; + + let (a, b) = (self, other); + let abs_a = a.abs(); + let abs_b = b.abs(); + + if abs_a <= HI && abs_b <= HI { + // Overflow is impossible + (a + b) / 2. + } else { + (a / 2.) + (b / 2.) + } + } + } +} + +mod c_int_definition { + crate::cfg_select! { + any(target_arch = "avr", target_arch = "msp430") => { + pub(super) type c_int = i16; + pub(super) type c_uint = u16; + } + _ => { + pub(super) type c_int = i32; + pub(super) type c_uint = u32; + } + } +} + +cfg_select! { + any( + target_family = "unix", + target_os = "wasi", + target_os = "teeos", + target_os = "trusty", + ) => { + mod unix; + } + target_os = "windows" => { + mod windows; + } + target_os = "hermit" => { + mod hermit; + } + target_os = "motor" => { + mod motor; + } + all(target_vendor = "fortanix", target_env = "sgx") => { + mod sgx; + } + target_os = "solid_asp3" => { + mod solid; + } + target_os = "uefi" => { + mod uefi; + } + target_os = "vexos" => { + mod vexos; + } + target_family = "wasm" => { + mod wasm; + } + target_os = "xous" => { + mod xous; + } + target_os = "zkvm" => { + mod zkvm; + } +} + +// Other rust-lang/rust ui tests to cover other expansion sites +fn arm_rhs_expr_3() -> i32 { + cfg_select! { + any(true) => 1, + any(false) => 2, + any(true) => { + 42 + } + any(true) => { + 42 + } + any(false) => -1 as i32, + any(true) => 2 + 2, + any(false) => "", + any(true) => + if true { + 42 + } else { + 84 + }, + any(false) => + if true { + 42 + } else { + 84 + }, + any(true) => return 42, + any(false) => loop {}, + any(true) => (1, 2), + any(false) => (1, 2,), + any(true) => todo!(), + any(false) => println!("hello"), + } +} + +fn expand_to_statements() -> i32 { + cfg_select! { + false => { + let b = 2; + b + 1 + } + true => { + let a = 1; + a + 1 + } + } +} + +type ExpandToType = cfg_select! { + unix => { + u32 + } + _ => i32, +}; + +fn expand_to_pattern(x: Option) -> bool { + match x { + (cfg_select! { + unix => Some(n), + _ => None, + }) => true, + _ => false, + } +} + +cfg_select! { + false => { + fn foo() {} + } + _ => { + fn bar() {} + } +} + +struct S; + +impl S { + cfg_select! { + false => { + fn foo() {} + } + _ => { + fn bar() {} + } + } +} + +trait T { + cfg_select! { + false => { + fn a(); + } + _ => { + fn b(); + } + } +} + +impl T for S { + cfg_select! { + false => { + fn a() {} + } + _ => { + fn b() {} + } + } +} + +extern "C" { + cfg_select! { + false => { + fn puts(s: *const i8) -> i32; + } + _ => { + fn printf(fmt: *const i8, ...) -> i32; + } + } +}