From 6a709e7195b819c2baef9cba2c889d5408346b48 Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 11 Oct 2025 18:52:58 +0800 Subject: [PATCH 01/16] Suppress the error for private fields with non_exhaustive attribute --- compiler/rustc_hir_typeck/src/expr.rs | 6 ++++- .../auxiliary/non_exhaustive_with_private.rs | 13 +++++++++++ ...n-exhaustive-with-private-fields-147513.rs | 17 ++++++++++++++ ...haustive-with-private-fields-147513.stderr | 23 +++++++++++++++++++ 4 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 tests/ui/privacy/auxiliary/non_exhaustive_with_private.rs create mode 100644 tests/ui/privacy/non-exhaustive-with-private-fields-147513.rs create mode 100644 tests/ui/privacy/non-exhaustive-with-private-fields-147513.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 1aaf02646c794..9aaf34a0fd1c6 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2331,7 +2331,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr.hir_id, fru_tys); - } else if adt_kind != AdtKind::Union && !remaining_fields.is_empty() { + } else if adt_kind != AdtKind::Union + && !remaining_fields.is_empty() + //~ non_exhaustive already reported, which will only happen for extern modules + && !variant.field_list_has_applicable_non_exhaustive() + { debug!(?remaining_fields); let private_fields: Vec<&ty::FieldDef> = variant .fields diff --git a/tests/ui/privacy/auxiliary/non_exhaustive_with_private.rs b/tests/ui/privacy/auxiliary/non_exhaustive_with_private.rs new file mode 100644 index 0000000000000..18e63db0cdd11 --- /dev/null +++ b/tests/ui/privacy/auxiliary/non_exhaustive_with_private.rs @@ -0,0 +1,13 @@ +// Auxiliary crate for testing non-exhaustive struct with private fields + +#[non_exhaustive] +pub struct Foo { + pub my_field: u32, + private_field: i32, +} + +#[non_exhaustive] +pub struct Bar { + pub my_field: u32, + pub missing_field: i32, +} diff --git a/tests/ui/privacy/non-exhaustive-with-private-fields-147513.rs b/tests/ui/privacy/non-exhaustive-with-private-fields-147513.rs new file mode 100644 index 0000000000000..0553ff6c781e9 --- /dev/null +++ b/tests/ui/privacy/non-exhaustive-with-private-fields-147513.rs @@ -0,0 +1,17 @@ +//@ aux-build:non_exhaustive_with_private.rs + +extern crate non_exhaustive_with_private; + +use non_exhaustive_with_private::{Bar, Foo}; + +fn main() { + let foo = Foo { + //~^ ERROR cannot create non-exhaustive struct using struct expression + my_field: 10, + }; + + let bar = Bar { + //~^ ERROR cannot create non-exhaustive struct using struct expression + my_field: 10, + }; +} diff --git a/tests/ui/privacy/non-exhaustive-with-private-fields-147513.stderr b/tests/ui/privacy/non-exhaustive-with-private-fields-147513.stderr new file mode 100644 index 0000000000000..85bf7e4847d73 --- /dev/null +++ b/tests/ui/privacy/non-exhaustive-with-private-fields-147513.stderr @@ -0,0 +1,23 @@ +error[E0639]: cannot create non-exhaustive struct using struct expression + --> $DIR/non-exhaustive-with-private-fields-147513.rs:8:15 + | +LL | let foo = Foo { + | _______________^ +LL | | +LL | | my_field: 10, +LL | | }; + | |_____^ + +error[E0639]: cannot create non-exhaustive struct using struct expression + --> $DIR/non-exhaustive-with-private-fields-147513.rs:13:15 + | +LL | let bar = Bar { + | _______________^ +LL | | +LL | | my_field: 10, +LL | | }; + | |_____^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0639`. From 71b093fd2c79a4de1354f8b00adba64d0bce25b6 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 5 Dec 2025 15:06:07 +0100 Subject: [PATCH 02/16] Emit `check-cfg` lints during attribute parsing rather than evaluation#149215 --- .../rustc_attr_parsing/src/attributes/cfg.rs | 82 +++++++------------ .../src/attributes/cfg_old.rs | 9 +- compiler/rustc_builtin_macros/src/cfg.rs | 8 +- .../rustc_builtin_macros/src/cfg_select.rs | 16 +--- compiler/rustc_codegen_ssa/src/back/link.rs | 7 +- compiler/rustc_expand/src/config.rs | 32 ++------ compiler/rustc_expand/src/expand.rs | 2 +- .../rustc_hir/src/attrs/data_structures.rs | 2 +- compiler/rustc_lint/src/early/diagnostics.rs | 16 ++-- compiler/rustc_lint_defs/src/lib.rs | 4 +- compiler/rustc_metadata/src/native_libs.rs | 7 +- compiler/rustc_resolve/src/diagnostics.rs | 2 +- 12 files changed, 64 insertions(+), 123 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index bd228315b2c95..490e28ed64c5d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -2,16 +2,16 @@ use std::convert::identity; use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; -use rustc_ast::{AttrItem, Attribute, CRATE_NODE_ID, LitKind, NodeId, ast, token}; +use rustc_ast::{AttrItem, Attribute, CRATE_NODE_ID, LitKind, ast, token}; use rustc_errors::{Applicability, PResult}; use rustc_feature::{AttrSuggestionStyle, AttributeTemplate, Features, template}; use rustc_hir::attrs::CfgEntry; +use rustc_hir::lints::AttributeLintKind; use rustc_hir::{AttrPath, RustcVersion}; use rustc_parse::parser::{ForceCollect, Parser}; use rustc_parse::{exp, parse_in}; use rustc_session::Session; use rustc_session::config::ExpectedValues; -use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::UNEXPECTED_CFGS; use rustc_session::parse::{ParseSess, feature_err}; use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; @@ -23,10 +23,7 @@ use crate::session_diagnostics::{ AttributeParseError, AttributeParseErrorReason, CfgAttrBadDelim, MetaBadDelimSugg, ParsedDescription, }; -use crate::{ - AttributeParser, CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics, - try_gate_cfg, -}; +use crate::{AttributeParser, fluent_generated, parse_version, session_diagnostics, try_gate_cfg}; pub const CFG_TEMPLATE: AttributeTemplate = template!( List: &["predicate"], @@ -195,43 +192,46 @@ fn parse_name_value( } }; - Ok(CfgEntry::NameValue { name, name_span, value, span }) + match cx.sess.psess.check_config.expecteds.get(&name) { + Some(ExpectedValues::Some(values)) if !values.contains(&value.map(|(v, _)| v)) => cx + .emit_lint( + UNEXPECTED_CFGS, + AttributeLintKind::UnexpectedCfgValue((name, name_span), value), + span, + ), + None if cx.sess.psess.check_config.exhaustive_names => cx.emit_lint( + UNEXPECTED_CFGS, + AttributeLintKind::UnexpectedCfgName((name, name_span), value), + span, + ), + _ => { /* not unexpected */ } + } + + Ok(CfgEntry::NameValue { name, value: value.map(|(v, _)| v), span }) } -pub fn eval_config_entry( - sess: &Session, - cfg_entry: &CfgEntry, - id: NodeId, - emit_lints: ShouldEmit, -) -> EvalConfigResult { +pub fn eval_config_entry(sess: &Session, cfg_entry: &CfgEntry) -> EvalConfigResult { match cfg_entry { CfgEntry::All(subs, ..) => { - let mut all = None; for sub in subs { - let res = eval_config_entry(sess, sub, id, emit_lints); - // We cannot short-circuit because `eval_config_entry` emits some lints + let res = eval_config_entry(sess, sub); if !res.as_bool() { - all.get_or_insert(res); + return res; } } - all.unwrap_or_else(|| EvalConfigResult::True) + EvalConfigResult::True } CfgEntry::Any(subs, span) => { - let mut any = None; for sub in subs { - let res = eval_config_entry(sess, sub, id, emit_lints); - // We cannot short-circuit because `eval_config_entry` emits some lints + let res = eval_config_entry(sess, sub); if res.as_bool() { - any.get_or_insert(res); + return res; } } - any.unwrap_or_else(|| EvalConfigResult::False { - reason: cfg_entry.clone(), - reason_span: *span, - }) + EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span } } CfgEntry::Not(sub, span) => { - if eval_config_entry(sess, sub, id, emit_lints).as_bool() { + if eval_config_entry(sess, sub).as_bool() { EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span } } else { EvalConfigResult::True @@ -244,32 +244,8 @@ pub fn eval_config_entry( EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span } } } - CfgEntry::NameValue { name, name_span, value, span } => { - if let ShouldEmit::ErrorsAndLints = emit_lints { - match sess.psess.check_config.expecteds.get(name) { - Some(ExpectedValues::Some(values)) - if !values.contains(&value.map(|(v, _)| v)) => - { - id.emit_span_lint( - sess, - UNEXPECTED_CFGS, - *span, - BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value), - ); - } - None if sess.psess.check_config.exhaustive_names => { - id.emit_span_lint( - sess, - UNEXPECTED_CFGS, - *span, - BuiltinLintDiag::UnexpectedCfgName((*name, *name_span), *value), - ); - } - _ => { /* not unexpected */ } - } - } - - if sess.psess.config.contains(&(*name, value.map(|(v, _)| v))) { + CfgEntry::NameValue { name, value, span } => { + if sess.psess.config.contains(&(*name, *value)) { EvalConfigResult::True } else { EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span } diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_old.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_old.rs index 70228d1e15101..adae3fa635f43 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_old.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_old.rs @@ -2,6 +2,7 @@ use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, Nod use rustc_ast_pretty::pprust; use rustc_feature::{Features, GatedCfg, find_gated_cfg}; use rustc_hir::RustcVersion; +use rustc_hir::lints::AttributeLintKind; use rustc_session::Session; use rustc_session::config::ExpectedValues; use rustc_session::lint::builtin::UNEXPECTED_CFGS; @@ -51,10 +52,10 @@ pub fn cfg_matches( sess, UNEXPECTED_CFGS, cfg.span, - BuiltinLintDiag::UnexpectedCfgValue( + BuiltinLintDiag::AttributeLint(AttributeLintKind::UnexpectedCfgValue( (cfg.name, cfg.name_span), cfg.value.map(|v| (v, cfg.value_span.unwrap())), - ), + )), ); } None if sess.psess.check_config.exhaustive_names => { @@ -62,10 +63,10 @@ pub fn cfg_matches( sess, UNEXPECTED_CFGS, cfg.span, - BuiltinLintDiag::UnexpectedCfgName( + BuiltinLintDiag::AttributeLint(AttributeLintKind::UnexpectedCfgName( (cfg.name, cfg.name_span), cfg.value.map(|v| (v, cfg.value_span.unwrap())), - ), + )), ); } _ => { /* not unexpected */ } diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index b24e3065622d2..7bc9080ba0229 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -26,13 +26,7 @@ pub(crate) fn expand_cfg( ExpandResult::Ready(match parse_cfg(cx, sp, tts) { Ok(cfg) => { - let matches_cfg = attr::eval_config_entry( - cx.sess, - &cfg, - cx.current_expansion.lint_node_id, - ShouldEmit::ErrorsAndLints, - ) - .as_bool(); + let matches_cfg = attr::eval_config_entry(cx.sess, &cfg).as_bool(); MacEager::expr(cx.expr_bool(sp, matches_cfg)) } diff --git a/compiler/rustc_builtin_macros/src/cfg_select.rs b/compiler/rustc_builtin_macros/src/cfg_select.rs index b77a121ca0b95..dc8077b2a1ffb 100644 --- a/compiler/rustc_builtin_macros/src/cfg_select.rs +++ b/compiler/rustc_builtin_macros/src/cfg_select.rs @@ -1,7 +1,7 @@ use rustc_ast::tokenstream::TokenStream; use rustc_attr_parsing as attr; use rustc_attr_parsing::{ - CfgSelectBranches, CfgSelectPredicate, EvalConfigResult, ShouldEmit, parse_cfg_select, + CfgSelectBranches, CfgSelectPredicate, EvalConfigResult, parse_cfg_select, }; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; use rustc_span::{Ident, Span, sym}; @@ -10,21 +10,13 @@ use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable}; /// Selects the first arm whose predicate evaluates to true. fn select_arm(ecx: &ExtCtxt<'_>, branches: CfgSelectBranches) -> Option<(TokenStream, Span)> { - let mut result = None; for (cfg, tt, arm_span) in branches.reachable { - if let EvalConfigResult::True = attr::eval_config_entry( - &ecx.sess, - &cfg, - ecx.current_expansion.lint_node_id, - ShouldEmit::ErrorsAndLints, - ) { - // FIXME(#149215) Ideally we should short-circuit here, but `eval_config_entry` currently emits lints so we cannot do this yet. - result.get_or_insert((tt, arm_span)); + if let EvalConfigResult::True = attr::eval_config_entry(&ecx.sess, &cfg) { + return Some((tt, arm_span)); } } - let wildcard = branches.wildcard.map(|(_, tt, span)| (tt, span)); - result.or(wildcard) + branches.wildcard.map(|(_, tt, span)| (tt, span)) } pub(super) fn expand_cfg_select<'cx>( diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index d35c3b6bb189e..70db6794742d0 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -13,8 +13,7 @@ use find_msvc_tools; use itertools::Itertools; use regex::Regex; use rustc_arena::TypedArena; -use rustc_ast::CRATE_NODE_ID; -use rustc_attr_parsing::{ShouldEmit, eval_config_entry}; +use rustc_attr_parsing::eval_config_entry; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; @@ -3029,9 +3028,7 @@ fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) { fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { match lib.cfg { - Some(ref cfg) => { - eval_config_entry(sess, cfg, CRATE_NODE_ID, ShouldEmit::ErrorsAndLints).as_bool() - } + Some(ref cfg) => eval_config_entry(sess, cfg).as_bool(), None => true, } } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 0b3ac472e0e83..492c845df1710 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -163,10 +163,7 @@ pub fn pre_configure_attrs(sess: &Session, attrs: &[Attribute]) -> ast::AttrVec .iter() .flat_map(|attr| strip_unconfigured.process_cfg_attr(attr)) .take_while(|attr| { - !is_cfg(attr) - || strip_unconfigured - .cfg_true(attr, strip_unconfigured.lint_node_id, ShouldEmit::Nothing) - .as_bool() + !is_cfg(attr) || strip_unconfigured.cfg_true(attr, ShouldEmit::Nothing).as_bool() }) .collect() } @@ -309,14 +306,7 @@ impl<'a> StripUnconfigured<'a> { ); } - if !attr::eval_config_entry( - self.sess, - &cfg_predicate, - ast::CRATE_NODE_ID, - ShouldEmit::ErrorsAndLints, - ) - .as_bool() - { + if !attr::eval_config_entry(self.sess, &cfg_predicate).as_bool() { return vec![trace_attr]; } @@ -400,23 +390,17 @@ impl<'a> StripUnconfigured<'a> { /// Determines if a node with the given attributes should be included in this configuration. fn in_cfg(&self, attrs: &[Attribute]) -> bool { - attrs.iter().all(|attr| { - !is_cfg(attr) - || self.cfg_true(attr, self.lint_node_id, ShouldEmit::ErrorsAndLints).as_bool() - }) + attrs + .iter() + .all(|attr| !is_cfg(attr) || self.cfg_true(attr, ShouldEmit::ErrorsAndLints).as_bool()) } - pub(crate) fn cfg_true( - &self, - attr: &Attribute, - node: NodeId, - emit_errors: ShouldEmit, - ) -> EvalConfigResult { + pub(crate) fn cfg_true(&self, attr: &Attribute, emit_errors: ShouldEmit) -> EvalConfigResult { let Some(cfg) = AttributeParser::parse_single( self.sess, attr, attr.span, - node, + self.lint_node_id, self.features, emit_errors, parse_cfg, @@ -426,7 +410,7 @@ impl<'a> StripUnconfigured<'a> { return EvalConfigResult::True; }; - eval_config_entry(self.sess, &cfg, self.lint_node_id, emit_errors) + eval_config_entry(self.sess, &cfg) } /// If attributes are not allowed on expressions, emit an error for `attr` diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 20fb321307ac5..90563b21d2e80 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -2213,7 +2213,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { attr: ast::Attribute, pos: usize, ) -> EvalConfigResult { - let res = self.cfg().cfg_true(&attr, node.node_id(), ShouldEmit::ErrorsAndLints); + let res = self.cfg().cfg_true(&attr, ShouldEmit::ErrorsAndLints); if res.as_bool() { // A trace attribute left in AST in place of the original `cfg` attribute. // It can later be used by lints or other diagnostics. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index d3cc1dd6cf751..aff79d0558387 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -190,7 +190,7 @@ pub enum CfgEntry { Any(ThinVec, Span), Not(Box, Span), Bool(bool, Span), - NameValue { name: Symbol, name_span: Span, value: Option<(Symbol, Span)>, span: Span }, + NameValue { name: Symbol, value: Option, span: Span }, Version(Option, Span), } diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index b654bc848ecfc..5345c9a4a9851 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -169,12 +169,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::UnexpectedCfgName(name, value) => { - check_cfg::unexpected_cfg_name(sess, tcx, name, value).decorate_lint(diag); - } - BuiltinLintDiag::UnexpectedCfgValue(name, value) => { - check_cfg::unexpected_cfg_value(sess, tcx, name, value).decorate_lint(diag); - } BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => { let suggestion = match sugg { Some((right_sp, sugg)) => lints::DeprecatedWhereClauseLocationSugg::MoveToEnd { @@ -307,8 +301,8 @@ pub fn decorate_builtin_lint( } pub fn decorate_attribute_lint( - _sess: &Session, - _tcx: Option>, + sess: &Session, + tcx: Option>, kind: &AttributeLintKind, diag: &mut Diag<'_, ()>, ) { @@ -364,5 +358,11 @@ pub fn decorate_attribute_lint( suggestion: lints::UnsafeAttrOutsideUnsafeSuggestion { left, right }, } .decorate_lint(diag), + &AttributeLintKind::UnexpectedCfgName(name, value) => { + check_cfg::unexpected_cfg_name(sess, tcx, name, value).decorate_lint(diag) + } + &AttributeLintKind::UnexpectedCfgValue(name, value) => { + check_cfg::unexpected_cfg_value(sess, tcx, name, value).decorate_lint(diag) + } } } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 3c6e7d04a29dd..cc4062775ad30 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -636,8 +636,6 @@ pub enum BuiltinLintDiag { }, BreakWithLabelAndLoop(Span), UnicodeTextFlow(Span, String), - UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>), - UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>), DeprecatedWhereclauseLocation(Span, Option<(Span, String)>), SingleUseLifetime { /// Span of the parameter which declares this lifetime. @@ -732,6 +730,8 @@ pub enum AttributeLintKind { attribute_name_span: Span, sugg_spans: (Span, Span), }, + UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>), + UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>), } pub type RegisteredTools = FxIndexSet; diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 250657bc6806b..e08460c3d4c9a 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -2,8 +2,7 @@ use std::ops::ControlFlow; use std::path::{Path, PathBuf}; use rustc_abi::ExternAbi; -use rustc_ast::CRATE_NODE_ID; -use rustc_attr_parsing::{ShouldEmit, eval_config_entry}; +use rustc_attr_parsing::eval_config_entry; use rustc_data_structures::fx::FxHashSet; use rustc_hir::attrs::{AttributeKind, NativeLibKind, PeImportNameType}; use rustc_hir::find_attr; @@ -188,9 +187,7 @@ pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec pub(crate) fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { match lib.cfg { - Some(ref cfg) => { - eval_config_entry(sess, cfg, CRATE_NODE_ID, ShouldEmit::ErrorsAndLints).as_bool() - } + Some(ref cfg) => eval_config_entry(sess, cfg).as_bool(), None => true, } } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 33c111708e366..a675c62ddf705 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -3070,7 +3070,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { continue; } - let item_was = if let CfgEntry::NameValue { value: Some((feature, _)), .. } = cfg.0 { + let item_was = if let CfgEntry::NameValue { value: Some(feature), .. } = cfg.0 { errors::ItemWas::BehindFeature { feature, span: cfg.1 } } else { errors::ItemWas::CfgOut { span: cfg.1 } From e7df90412c208f3e18af8ed60781a343dd87645a Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 5 Dec 2025 15:08:04 +0100 Subject: [PATCH 03/16] Changes to uitests --- .../ui/feature-gates/feature-gate-link_cfg.rs | 2 +- .../feature-gate-link_cfg.stderr | 4 ++-- tests/ui/link-native-libs/link-cfg-works.rs | 2 +- tests/ui/macros/cfg.rs | 3 ++- tests/ui/macros/cfg.stderr | 17 ++++++++++--- tests/ui/macros/cfg_select.rs | 2 ++ tests/ui/macros/cfg_select.stderr | 24 +++++++++++++++++-- 7 files changed, 44 insertions(+), 10 deletions(-) diff --git a/tests/ui/feature-gates/feature-gate-link_cfg.rs b/tests/ui/feature-gates/feature-gate-link_cfg.rs index d30ee3bcfdb14..286c3d0a4977d 100644 --- a/tests/ui/feature-gates/feature-gate-link_cfg.rs +++ b/tests/ui/feature-gates/feature-gate-link_cfg.rs @@ -1,4 +1,4 @@ -#[link(name = "foo", cfg(foo))] +#[link(name = "foo", cfg(false))] //~^ ERROR: is unstable extern "C" {} diff --git a/tests/ui/feature-gates/feature-gate-link_cfg.stderr b/tests/ui/feature-gates/feature-gate-link_cfg.stderr index bfe7f74a92137..0e9707c6c9620 100644 --- a/tests/ui/feature-gates/feature-gate-link_cfg.stderr +++ b/tests/ui/feature-gates/feature-gate-link_cfg.stderr @@ -1,8 +1,8 @@ error[E0658]: link cfg is unstable --> $DIR/feature-gate-link_cfg.rs:1:22 | -LL | #[link(name = "foo", cfg(foo))] - | ^^^^^^^^ +LL | #[link(name = "foo", cfg(false))] + | ^^^^^^^^^^ | = help: add `#![feature(link_cfg)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date diff --git a/tests/ui/link-native-libs/link-cfg-works.rs b/tests/ui/link-native-libs/link-cfg-works.rs index 7b936bc43b1a0..0e02933ce01c2 100644 --- a/tests/ui/link-native-libs/link-cfg-works.rs +++ b/tests/ui/link-native-libs/link-cfg-works.rs @@ -7,7 +7,7 @@ extern crate link_cfg_works_transitive_dylib; extern crate link_cfg_works_transitive_rlib; -#[link(name = "foo", cfg(foo))] +#[link(name = "foo", cfg(false))] extern "C" {} fn main() {} diff --git a/tests/ui/macros/cfg.rs b/tests/ui/macros/cfg.rs index d992ec82e2fd2..f0c51b2942f16 100644 --- a/tests/ui/macros/cfg.rs +++ b/tests/ui/macros/cfg.rs @@ -2,5 +2,6 @@ fn main() { cfg!(); //~ ERROR macro requires a cfg-pattern cfg!(123); //~ ERROR malformed `cfg` macro input cfg!(foo = 123); //~ ERROR malformed `cfg` macro input - cfg!(foo, bar); //~ ERROR expected 1 cfg-pattern + cfg!(false, false); //~ ERROR expected 1 cfg-pattern + cfg!(foo); //~ WARN unexpected `cfg` condition name: `foo` } diff --git a/tests/ui/macros/cfg.stderr b/tests/ui/macros/cfg.stderr index 9a4c187f37b27..06529a5b7a61f 100644 --- a/tests/ui/macros/cfg.stderr +++ b/tests/ui/macros/cfg.stderr @@ -29,9 +29,20 @@ LL | cfg!(foo = 123); error: expected 1 cfg-pattern --> $DIR/cfg.rs:5:5 | -LL | cfg!(foo, bar); - | ^^^^^^^^^^^^^^ +LL | cfg!(false, false); + | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +warning: unexpected `cfg` condition name: `foo` + --> $DIR/cfg.rs:6:10 + | +LL | cfg!(foo); + | ^^^ + | + = help: expected names are: `FALSE` and `test` and 31 more + = help: to expect this configuration use `--check-cfg=cfg(foo)` + = note: see for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default + +error: aborting due to 4 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/macros/cfg_select.rs b/tests/ui/macros/cfg_select.rs index 2a627cc05b93b..a54c385747fa6 100644 --- a/tests/ui/macros/cfg_select.rs +++ b/tests/ui/macros/cfg_select.rs @@ -89,9 +89,11 @@ cfg_select! { cfg_select! { a + 1 => {} //~^ ERROR expected one of `(`, `::`, `=>`, or `=`, found `+` + //~| WARN unexpected `cfg` condition name } cfg_select! { cfg!() => {} //~^ ERROR expected one of `(`, `::`, `=>`, or `=`, found `!` + //~| WARN unexpected `cfg` condition name } diff --git a/tests/ui/macros/cfg_select.stderr b/tests/ui/macros/cfg_select.stderr index 3a5d2b0a1e1ee..d79e1b9b5c10a 100644 --- a/tests/ui/macros/cfg_select.stderr +++ b/tests/ui/macros/cfg_select.stderr @@ -60,12 +60,32 @@ LL | a + 1 => {} | ^ expected one of `(`, `::`, `=>`, or `=` error: expected one of `(`, `::`, `=>`, or `=`, found `!` - --> $DIR/cfg_select.rs:95:8 + --> $DIR/cfg_select.rs:96:8 | LL | cfg!() => {} | ^ expected one of `(`, `::`, `=>`, or `=` -error: aborting due to 9 previous errors; 1 warning emitted +warning: unexpected `cfg` condition name: `a` + --> $DIR/cfg_select.rs:90:5 + | +LL | a + 1 => {} + | ^ help: found config with similar value: `target_feature = "a"` + | + = help: expected names are: `FALSE` and `test` and 31 more + = help: to expect this configuration use `--check-cfg=cfg(a)` + = note: see for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default + +warning: unexpected `cfg` condition name: `cfg` + --> $DIR/cfg_select.rs:96:5 + | +LL | cfg!() => {} + | ^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(cfg)` + = note: see for more information about checking conditional configuration + +error: aborting due to 9 previous errors; 3 warnings emitted Some errors have detailed explanations: E0537, E0539. For more information about an error, try `rustc --explain E0537`. From 01576a6de101ca85a8b43f2c3db2028c876a355c Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sat, 6 Dec 2025 10:38:15 +0100 Subject: [PATCH 04/16] Add regression test for nested cfgs --- tests/ui/check-cfg/nested-cfg.rs | 8 ++++++++ tests/ui/check-cfg/nested-cfg.stderr | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 tests/ui/check-cfg/nested-cfg.rs create mode 100644 tests/ui/check-cfg/nested-cfg.stderr diff --git a/tests/ui/check-cfg/nested-cfg.rs b/tests/ui/check-cfg/nested-cfg.rs new file mode 100644 index 0000000000000..2b3a21f88dc59 --- /dev/null +++ b/tests/ui/check-cfg/nested-cfg.rs @@ -0,0 +1,8 @@ +//@ check-pass + +#[cfg(unknown)] //~ WARN unexpected `cfg` condition name +#[cfg(false)] +#[cfg(unknown)] // Should not warn +fn foo() {} + +fn main() {} diff --git a/tests/ui/check-cfg/nested-cfg.stderr b/tests/ui/check-cfg/nested-cfg.stderr new file mode 100644 index 0000000000000..6fdae732bbe58 --- /dev/null +++ b/tests/ui/check-cfg/nested-cfg.stderr @@ -0,0 +1,23 @@ +warning: unexpected `cfg` condition name: `unknown` + --> $DIR/nested-cfg.rs:3:7 + | +LL | #[cfg(unknown)] + | ^^^^^^^ + | + = help: expected names are: `FALSE` and `test` and 31 more + = help: to expect this configuration use `--check-cfg=cfg(unknown)` + = note: see for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default +help: found config with similar value + | +LL - #[cfg(unknown)] +LL + #[cfg(target_os = "unknown")] + | +help: found config with similar value + | +LL - #[cfg(unknown)] +LL + #[cfg(target_vendor = "unknown")] + | + +warning: 1 warning emitted + From a3a041da11202dd4c5fbbac4dbc1461e4c783f90 Mon Sep 17 00:00:00 2001 From: Justin Tracey Date: Sat, 6 Dec 2025 11:16:14 -0500 Subject: [PATCH 05/16] rustdoc book: mention inner doc attribute --- src/doc/rustdoc/src/write-documentation/the-doc-attribute.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md index 4d7f1a4aafc93..3af53fa58e18c 100644 --- a/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md +++ b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md @@ -4,8 +4,8 @@ The `#[doc]` attribute lets you control various aspects of how `rustdoc` does its job. The most basic function of `#[doc]` is to handle the actual documentation -text. That is, `///` is syntax sugar for `#[doc]`. This means that these two -are the same: +text. That is, `///` is syntax sugar for `#[doc]` (as is `//!` for `#![doc]`). +This means that these two are the same: ```rust,no_run /// This is a doc comment. From 260b1ffc2cc6e2b83c68e6357417022db262f511 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 8 Dec 2025 09:37:11 +0800 Subject: [PATCH 06/16] compiletest: require `host`/`target` flags specified Instead of allowing them to be missing and using some placeholder "(none)" value instead. --- src/tools/compiletest/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index f3bd467db3e41..625d839dece1a 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -255,7 +255,9 @@ fn parse_config(args: Vec) -> Config { } } - let target = opt_str2(matches.opt_str("target")); + let host = matches.opt_str("host").expect("`--host` must be unconditionally specified"); + let target = matches.opt_str("target").expect("`--target` must be unconditionally specified"); + let android_cross_path = matches.opt_str("android-cross-path").map(Utf8PathBuf::from); // FIXME: `cdb_version` is *derived* from cdb, but it's *not* technically a config! let cdb = debuggers::discover_cdb(matches.opt_str("cdb"), &target); @@ -433,7 +435,7 @@ fn parse_config(args: Vec) -> Config { optimize_tests: matches.opt_present("optimize-tests"), rust_randomized_layout: matches.opt_present("rust-randomized-layout"), target, - host: opt_str2(matches.opt_str("host")), + host, cdb, cdb_version, gdb, From a7ad2142e35a1aa7e51b052b9ffad83e653e6340 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 8 Dec 2025 09:38:31 +0800 Subject: [PATCH 07/16] compiletest: make presence/absence of adb-related options clear Instead of possibly falling back to "(none)" when they are not specified. --- src/tools/compiletest/src/lib.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 625d839dece1a..cd875d78f2313 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -259,6 +259,13 @@ fn parse_config(args: Vec) -> Config { let target = matches.opt_str("target").expect("`--target` must be unconditionally specified"); let android_cross_path = matches.opt_str("android-cross-path").map(Utf8PathBuf::from); + + // FIXME: `adb_path` should be an `Option`... + let adb_path = matches.opt_str("adb-path").map(Utf8PathBuf::from).unwrap_or_default(); + // FIXME: `adb_test_dir` should be an `Option`... + let adb_test_dir = matches.opt_str("adb-test-dir").map(Utf8PathBuf::from).unwrap_or_default(); + let adb_device_status = target.contains("android") && !adb_test_dir.as_str().is_empty(); + // FIXME: `cdb_version` is *derived* from cdb, but it's *not* technically a config! let cdb = debuggers::discover_cdb(matches.opt_str("cdb"), &target); let cdb_version = cdb.as_deref().and_then(debuggers::query_cdb_version); @@ -445,11 +452,9 @@ fn parse_config(args: Vec) -> Config { llvm_version, system_llvm: matches.opt_present("system-llvm"), android_cross_path, - adb_path: Utf8PathBuf::from(opt_str2(matches.opt_str("adb-path"))), - adb_test_dir: Utf8PathBuf::from(opt_str2(matches.opt_str("adb-test-dir"))), - adb_device_status: opt_str2(matches.opt_str("target")).contains("android") - && "(none)" != opt_str2(matches.opt_str("adb-test-dir")) - && !opt_str2(matches.opt_str("adb-test-dir")).is_empty(), + adb_path, + adb_test_dir, + adb_device_status, verbose: matches.opt_present("verbose"), only_modified: matches.opt_present("only-modified"), color, From 72541e9a511f4844000ca42a60516eddb2bfa892 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Mon, 8 Dec 2025 09:41:46 +0800 Subject: [PATCH 08/16] compiletest: retire `opt_str2` We either have the value of a flag specified, or we don't. Use `Option<...>` to represent that -- don't invent a new "(none)" sentinel value... --- src/tools/compiletest/src/lib.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index cd875d78f2313..ff4cd81d33ff6 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -500,13 +500,6 @@ fn parse_config(args: Vec) -> Config { } } -fn opt_str2(maybestr: Option) -> String { - match maybestr { - None => "(none)".to_owned(), - Some(s) => s, - } -} - /// Called by `main` after the config has been parsed. fn run_tests(config: Arc) { debug!(?config, "run_tests"); From 8c582e1f530b967dcbf2a549f70fce08819da3a1 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 7 Dec 2025 15:27:50 +1100 Subject: [PATCH 09/16] Don't require `test::Coverage` to implement Ord This derive was an artifact of test-only method `Cache::all` wanting to automatically sort its output to hide HashMap iteration order. We can achieve an equivalent result by requiring the caller to provide a projection function that returns results that _are_ sortable. --- src/bootstrap/src/core/build_steps/test.rs | 2 +- src/bootstrap/src/core/builder/tests.rs | 3 +-- src/bootstrap/src/utils/cache.rs | 20 +++++++++++--------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 09c98bd7c88b4..67cd5b8d67c3a 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1582,7 +1582,7 @@ test!(AssemblyLlvm { /// Runs the coverage test suite at `tests/coverage` in some or all of the /// coverage test modes. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Coverage { pub compiler: Compiler, pub target: TargetSelection, diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index b8ba1b4c2c340..965b75f24c203 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -345,8 +345,7 @@ fn test_test_coverage() { let config = configure_with_args(cmd, &[], &[TEST_TRIPLE_1]); let mut cache = run_build(&config.paths.clone(), config); - let modes = - cache.all::().iter().map(|(step, ())| step.mode).collect::>(); + let modes = cache.inspect_all_steps_of_type::(|step, ()| step.mode); assert_eq!(modes, expected); } } diff --git a/src/bootstrap/src/utils/cache.rs b/src/bootstrap/src/utils/cache.rs index 5098e2f03c439..8c5b3529979d7 100644 --- a/src/bootstrap/src/utils/cache.rs +++ b/src/bootstrap/src/utils/cache.rs @@ -270,16 +270,18 @@ impl Cache { #[cfg(test)] impl Cache { - pub fn all(&mut self) -> Vec<(S, S::Output)> { - let cache = self.cache.get_mut(); - let type_id = TypeId::of::(); - let mut v = cache - .remove(&type_id) - .map(|b| b.downcast::>().expect("correct type")) - .map(|m| m.into_iter().collect::>()) + pub(crate) fn inspect_all_steps_of_type( + &self, + map_fn: impl Fn(&S, &S::Output) -> T, + ) -> Vec { + let cache = self.cache.borrow(); + let mut values = cache + .get(&TypeId::of::()) + .map(|any| any.downcast_ref::>().expect("correct type")) + .map(|m| m.iter().map(|(step, output)| map_fn(step, output)).collect::>()) .unwrap_or_default(); - v.sort_by_key(|(s, _)| s.clone()); - v + values.sort(); + values } pub fn contains(&self) -> bool { From dc8cdb1c00da2649fb7ff56b56af11b4e9592802 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 7 Dec 2025 15:14:26 +1100 Subject: [PATCH 10/16] Use a `CompiletestMode` enum in bootstrap --- src/bootstrap/src/core/build_steps/test.rs | 116 +++++++++++------- .../src/core/build_steps/test/compiletest.rs | 71 +++++++++++ src/bootstrap/src/core/builder/tests.rs | 3 +- 3 files changed, 142 insertions(+), 48 deletions(-) create mode 100644 src/bootstrap/src/core/build_steps/test/compiletest.rs diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 67cd5b8d67c3a..a699cd23fb607 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3,6 +3,9 @@ //! `./x.py test` (aka [`Kind::Test`]) is currently allowed to reach build steps in other modules. //! However, this contains ~all test parts we expect people to be able to build and run locally. +// (This file should be split up, but having tidy block all changes is not helpful.) +// ignore-tidy-filelength + use std::collections::HashSet; use std::env::split_paths; use std::ffi::{OsStr, OsString}; @@ -17,6 +20,7 @@ use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags}; use crate::core::build_steps::llvm::get_llvm_version; use crate::core::build_steps::run::{get_completion_paths, get_help_path}; use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget; +use crate::core::build_steps::test::compiletest::CompiletestMode; use crate::core::build_steps::tool::{ self, RustcPrivateCompilers, SourceType, TEST_FLOAT_PARSE_ALLOW_FEATURES, Tool, ToolTargetBuildMode, get_tool_target_compiler, @@ -39,6 +43,8 @@ use crate::utils::helpers::{ use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests}; use crate::{CLang, CodegenBackendKind, DocTests, GitRepo, Mode, PathSet, envify}; +mod compiletest; + /// Runs `cargo test` on various internal tools used by bootstrap. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateBootstrap { @@ -1085,7 +1091,7 @@ impl Step for RustdocJSNotStd { builder.ensure(Compiletest { test_compiler: self.compiler, target: self.target, - mode: "rustdoc-js", + mode: CompiletestMode::RustdocJs, suite: "rustdoc-js", path: "tests/rustdoc-js", compare_mode: None, @@ -1478,7 +1484,7 @@ macro_rules! test { builder.ensure(Compiletest { test_compiler: self.test_compiler, target: self.target, - mode: $mode, + mode: const { $mode }, suite: $suite, path: $path, compare_mode: (const { @@ -1493,34 +1499,39 @@ macro_rules! test { }; } -test!(Ui { path: "tests/ui", mode: "ui", suite: "ui", default: true }); +test!(Ui { path: "tests/ui", mode: CompiletestMode::Ui, suite: "ui", default: true }); -test!(Crashes { path: "tests/crashes", mode: "crashes", suite: "crashes", default: true }); +test!(Crashes { + path: "tests/crashes", + mode: CompiletestMode::Crashes, + suite: "crashes", + default: true, +}); test!(CodegenLlvm { path: "tests/codegen-llvm", - mode: "codegen", + mode: CompiletestMode::Codegen, suite: "codegen-llvm", default: true }); test!(CodegenUnits { path: "tests/codegen-units", - mode: "codegen-units", + mode: CompiletestMode::CodegenUnits, suite: "codegen-units", default: true, }); test!(Incremental { path: "tests/incremental", - mode: "incremental", + mode: CompiletestMode::Incremental, suite: "incremental", default: true, }); test!(Debuginfo { path: "tests/debuginfo", - mode: "debuginfo", + mode: CompiletestMode::Debuginfo, suite: "debuginfo", default: true, compare_mode: Some("split-dwarf"), @@ -1528,7 +1539,7 @@ test!(Debuginfo { test!(UiFullDeps { path: "tests/ui-fulldeps", - mode: "ui", + mode: CompiletestMode::Ui, suite: "ui-fulldeps", default: true, IS_HOST: true, @@ -1536,14 +1547,14 @@ test!(UiFullDeps { test!(Rustdoc { path: "tests/rustdoc", - mode: "rustdoc", + mode: CompiletestMode::Rustdoc, suite: "rustdoc", default: true, IS_HOST: true, }); test!(RustdocUi { path: "tests/rustdoc-ui", - mode: "ui", + mode: CompiletestMode::Ui, suite: "rustdoc-ui", default: true, IS_HOST: true, @@ -1551,7 +1562,7 @@ test!(RustdocUi { test!(RustdocJson { path: "tests/rustdoc-json", - mode: "rustdoc-json", + mode: CompiletestMode::RustdocJson, suite: "rustdoc-json", default: true, IS_HOST: true, @@ -1559,23 +1570,28 @@ test!(RustdocJson { test!(Pretty { path: "tests/pretty", - mode: "pretty", + mode: CompiletestMode::Pretty, suite: "pretty", default: true, IS_HOST: true, }); -test!(RunMake { path: "tests/run-make", mode: "run-make", suite: "run-make", default: true }); +test!(RunMake { + path: "tests/run-make", + mode: CompiletestMode::RunMake, + suite: "run-make", + default: true, +}); test!(RunMakeCargo { path: "tests/run-make-cargo", - mode: "run-make", + mode: CompiletestMode::RunMake, suite: "run-make-cargo", default: true }); test!(AssemblyLlvm { path: "tests/assembly-llvm", - mode: "assembly", + mode: CompiletestMode::Assembly, suite: "assembly-llvm", default: true }); @@ -1586,13 +1602,14 @@ test!(AssemblyLlvm { pub struct Coverage { pub compiler: Compiler, pub target: TargetSelection, - pub mode: &'static str, + pub(crate) mode: CompiletestMode, } impl Coverage { const PATH: &'static str = "tests/coverage"; const SUITE: &'static str = "coverage"; - const ALL_MODES: &[&str] = &["coverage-map", "coverage-run"]; + const ALL_MODES: &[CompiletestMode] = + &[CompiletestMode::CoverageMap, CompiletestMode::CoverageRun]; } impl Step for Coverage { @@ -1608,7 +1625,7 @@ impl Step for Coverage { // - `./x test coverage-run -- tests/coverage/trivial.rs` run = run.suite_path(Self::PATH); for mode in Self::ALL_MODES { - run = run.alias(mode); + run = run.alias(mode.as_str()); } run } @@ -1631,15 +1648,15 @@ impl Step for Coverage { for path in &run.paths { match path { PathSet::Set(_) => { - for mode in Self::ALL_MODES { - if path.assert_single_path().path == Path::new(mode) { + for &mode in Self::ALL_MODES { + if path.assert_single_path().path == Path::new(mode.as_str()) { modes.push(mode); break; } } } PathSet::Suite(_) => { - modes.extend(Self::ALL_MODES); + modes.extend_from_slice(Self::ALL_MODES); break; } } @@ -1647,7 +1664,9 @@ impl Step for Coverage { // Skip any modes that were explicitly skipped/excluded on the command-line. // FIXME(Zalathar): Integrate this into central skip handling somehow? - modes.retain(|mode| !run.builder.config.skip.iter().any(|skip| skip == Path::new(mode))); + modes.retain(|mode| { + !run.builder.config.skip.iter().any(|skip| skip == Path::new(mode.as_str())) + }); // FIXME(Zalathar): Make these commands skip all coverage tests, as expected: // - `./x test --skip=tests` @@ -1678,7 +1697,7 @@ impl Step for Coverage { test!(CoverageRunRustdoc { path: "tests/coverage-run-rustdoc", - mode: "coverage-run", + mode: CompiletestMode::CoverageRun, suite: "coverage-run-rustdoc", default: true, IS_HOST: true, @@ -1712,7 +1731,7 @@ impl Step for MirOpt { builder.ensure(Compiletest { test_compiler: self.compiler, target, - mode: "mir-opt", + mode: CompiletestMode::MirOpt, suite: "mir-opt", path: "tests/mir-opt", compare_mode: None, @@ -1755,7 +1774,7 @@ struct Compiletest { /// The compiler that we're testing. test_compiler: Compiler, target: TargetSelection, - mode: &'static str, + mode: CompiletestMode, suite: &'static str, path: &'static str, compare_mode: Option<&'static str>, @@ -1791,7 +1810,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the let suite_path = self.path; // Skip codegen tests if they aren't enabled in configuration. - if !builder.config.codegen_tests && mode == "codegen" { + if !builder.config.codegen_tests && mode == CompiletestMode::Codegen { return; } @@ -1829,7 +1848,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the target, }); } - if mode == "run-make" { + if mode == CompiletestMode::RunMake { builder.tool_exe(Tool::RunMakeSupport); } @@ -1886,7 +1905,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the // suites, `run-make` and `run-make-cargo`. That way, contributors who do not need to run // the `run-make` tests that need in-tree cargo do not need to spend time building in-tree // cargo. - if mode == "run-make" { + if mode == CompiletestMode::RunMake { // We need to pass the compiler that was used to compile run-make-support, // because we have to use the same compiler to compile rmake.rs recipes. let stage0_rustc_path = builder.compiler(0, test_compiler.host); @@ -1910,17 +1929,18 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the } // Avoid depending on rustdoc when we don't need it. - if mode == "rustdoc" - || mode == "run-make" - || (mode == "ui" && is_rustdoc) - || mode == "rustdoc-js" - || mode == "rustdoc-json" - || suite == "coverage-run-rustdoc" + if matches!( + mode, + CompiletestMode::RunMake + | CompiletestMode::Rustdoc + | CompiletestMode::RustdocJs + | CompiletestMode::RustdocJson + ) || matches!(suite, "rustdoc-ui" | "coverage-run-rustdoc") { cmd.arg("--rustdoc-path").arg(builder.rustdoc_for_compiler(test_compiler)); } - if mode == "rustdoc-json" { + if mode == CompiletestMode::RustdocJson { // Use the stage0 compiler for jsondocck let json_compiler = builder.compiler(0, builder.host_target); cmd.arg("--jsondocck-path") @@ -1930,7 +1950,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the ); } - if matches!(mode, "coverage-map" | "coverage-run") { + if matches!(mode, CompiletestMode::CoverageMap | CompiletestMode::CoverageRun) { let coverage_dump = builder.tool_exe(Tool::CoverageDump); cmd.arg("--coverage-dump-path").arg(coverage_dump); } @@ -1957,7 +1977,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the cmd.arg("--sysroot-base").arg(sysroot); cmd.arg("--suite").arg(suite); - cmd.arg("--mode").arg(mode); + cmd.arg("--mode").arg(mode.as_str()); cmd.arg("--target").arg(target.rustc_target_arg()); cmd.arg("--host").arg(&*test_compiler.host.triple); cmd.arg("--llvm-filecheck").arg(builder.llvm_filecheck(builder.config.host_target)); @@ -2036,7 +2056,7 @@ Please disable assertions with `rust.debug-assertions = false`. if let Some(ref nodejs) = builder.config.nodejs { cmd.arg("--nodejs").arg(nodejs); - } else if mode == "rustdoc-js" { + } else if mode == CompiletestMode::RustdocJs { panic!("need nodejs to run rustdoc-js suite"); } if builder.config.rust_optimize_tests { @@ -2055,7 +2075,7 @@ Please disable assertions with `rust.debug-assertions = false`. let mut flags = if is_rustdoc { Vec::new() } else { vec!["-Crpath".to_string()] }; flags.push(format!( "-Cdebuginfo={}", - if mode == "codegen" { + if mode == CompiletestMode::Codegen { // codegen tests typically check LLVM IR and are sensitive to additional debuginfo. // So do not apply `rust.debuginfo-level-tests` for codegen tests. if builder.config.rust_debuginfo_level_tests @@ -2122,7 +2142,7 @@ Please disable assertions with `rust.debug-assertions = false`. cmd.arg("--android-cross-path").arg(android_cross_path); } - if mode == "debuginfo" { + if mode == CompiletestMode::Debuginfo { if let Some(debuggers::Gdb { gdb }) = debuggers::discover_gdb(builder, android.as_ref()) { cmd.arg("--gdb").arg(gdb.as_ref()); @@ -2155,7 +2175,7 @@ Please disable assertions with `rust.debug-assertions = false`. // in rustdoc-js mode, allow filters to be rs files or js files. // use a late-initialized Vec to avoid cloning for other modes. let mut paths_v; - if mode == "rustdoc-js" { + if mode == CompiletestMode::RustdocJs { paths_v = paths.to_vec(); for p in &mut paths_v { if let Some(ext) = p.extension() @@ -2237,7 +2257,9 @@ Please disable assertions with `rust.debug-assertions = false`. cmd.arg("--host-rustcflags").arg(link_llvm); } - if !builder.config.dry_run() && matches!(mode, "run-make" | "coverage-run") { + if !builder.config.dry_run() + && matches!(mode, CompiletestMode::RunMake | CompiletestMode::CoverageRun) + { // The llvm/bin directory contains many useful cross-platform // tools. Pass the path to run-make tests so they can use them. // (The coverage-run tests also need these tools to process @@ -2249,7 +2271,7 @@ Please disable assertions with `rust.debug-assertions = false`. cmd.arg("--llvm-bin-dir").arg(llvm_bin_path); } - if !builder.config.dry_run() && mode == "run-make" { + if !builder.config.dry_run() && mode == CompiletestMode::RunMake { // If LLD is available, add it to the PATH if builder.config.lld_enabled { let lld_install_root = @@ -2269,7 +2291,7 @@ Please disable assertions with `rust.debug-assertions = false`. // Only pass correct values for these flags for the `run-make` suite as it // requires that a C++ compiler was configured which isn't always the case. - if !builder.config.dry_run() && mode == "run-make" { + if !builder.config.dry_run() && mode == CompiletestMode::RunMake { let mut cflags = builder.cc_handled_clags(target, CLang::C); cflags.extend(builder.cc_unhandled_cflags(target, GitRepo::Rustc, CLang::C)); let mut cxxflags = builder.cc_handled_clags(target, CLang::Cxx); @@ -2385,7 +2407,7 @@ Please disable assertions with `rust.debug-assertions = false`. builder.metrics.begin_test_suite( build_helper::metrics::TestSuiteMetadata::Compiletest { suite: suite.into(), - mode: mode.into(), + mode: mode.to_string(), compare_mode: None, target: self.target.triple.to_string(), host: self.test_compiler.host.triple.to_string(), @@ -2408,7 +2430,7 @@ Please disable assertions with `rust.debug-assertions = false`. builder.metrics.begin_test_suite( build_helper::metrics::TestSuiteMetadata::Compiletest { suite: suite.into(), - mode: mode.into(), + mode: mode.to_string(), compare_mode: Some(compare_mode.into()), target: self.target.triple.to_string(), host: self.test_compiler.host.triple.to_string(), diff --git a/src/bootstrap/src/core/build_steps/test/compiletest.rs b/src/bootstrap/src/core/build_steps/test/compiletest.rs new file mode 100644 index 0000000000000..359f6bb1a6efb --- /dev/null +++ b/src/bootstrap/src/core/build_steps/test/compiletest.rs @@ -0,0 +1,71 @@ +use std::fmt; + +/// Enum of all the "test modes" understood by compiletest. +/// +/// Some of these mode names happen to overlap with the names of test suite +/// directories, but the relationship between modes and suites is not 1:1. +/// For example: +/// - Mode `ui` is used by suites `tests/ui` and `tests/rustdoc-ui` +/// - Suite `tests/coverage` uses modes `coverage-map` and `coverage-run` +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub(crate) enum CompiletestMode { + // tidy-alphabetical-start + Assembly, + Codegen, + CodegenUnits, + CoverageMap, + CoverageRun, + Crashes, + Debuginfo, + Incremental, + MirOpt, + Pretty, + RunMake, + Rustdoc, + RustdocJs, + RustdocJson, + Ui, + // tidy-alphabetical-end +} + +impl CompiletestMode { + /// Returns a string representing this mode, which can be passed to + /// compiletest via a command-line argument. + /// + /// These mode names must be kept in sync with the ones understood by + /// compiletest's `TestMode`, but they change so rarely that doing so + /// manually should not be burdensome. + pub(crate) const fn as_str(self) -> &'static str { + match self { + // tidy-alphabetical-start + Self::Assembly => "assembly", + Self::Codegen => "codegen", + Self::CodegenUnits => "codegen-units", + Self::CoverageMap => "coverage-map", + Self::CoverageRun => "coverage-run", + Self::Crashes => "crashes", + Self::Debuginfo => "debuginfo", + Self::Incremental => "incremental", + Self::MirOpt => "mir-opt", + Self::Pretty => "pretty", + Self::RunMake => "run-make", + Self::Rustdoc => "rustdoc", + Self::RustdocJs => "rustdoc-js", + Self::RustdocJson => "rustdoc-json", + Self::Ui => "ui", + // tidy-alphabetical-end + } + } +} + +impl fmt::Display for CompiletestMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +impl fmt::Debug for CompiletestMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::fmt(self, f) + } +} diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 965b75f24c203..709d646bb359a 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -345,7 +345,8 @@ fn test_test_coverage() { let config = configure_with_args(cmd, &[], &[TEST_TRIPLE_1]); let mut cache = run_build(&config.paths.clone(), config); - let modes = cache.inspect_all_steps_of_type::(|step, ()| step.mode); + let modes = + cache.inspect_all_steps_of_type::(|step, ()| step.mode.as_str()); assert_eq!(modes, expected); } } From 0f4ec281558a40645b125fda43f36d7006180e54 Mon Sep 17 00:00:00 2001 From: SATVIKsynopsis Date: Sun, 7 Dec 2025 16:58:31 +0530 Subject: [PATCH 11/16] lint: treat unsafe binders in improper_ctypes instead of ICE Replaced _binder with _ --- compiler/rustc_lint/messages.ftl | 2 ++ compiler/rustc_lint/src/types/improper_ctypes.rs | 4 +++- .../lint/improper-ctypes/unsafe-binder-basic.rs | 10 ++++++++++ .../improper-ctypes/unsafe-binder-basic.stderr | 15 +++++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tests/ui/lint/improper-ctypes/unsafe-binder-basic.rs create mode 100644 tests/ui/lint/improper-ctypes/unsafe-binder-basic.stderr diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 1bcdda96e13a2..a90a9c91ef13c 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -416,6 +416,8 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re lint_improper_ctypes_union_layout_reason = this union has unspecified layout lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive +lint_improper_ctypes_unsafe_binder = unsafe binders are incompatible with foreign function interfaces + lint_int_to_ptr_transmutes = transmuting an integer to a pointer creates a pointer without provenance .note = this is dangerous because dereferencing the resulting pointer is undefined behavior .note_exposed_provenance = exposed provenance semantics can be used to create a pointer based on some previously exposed provenance diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 9e38ea6b685bd..38094c67c34a0 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -669,7 +669,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe } - ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"), + ty::UnsafeBinder(_) => { + FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_unsafe_binder, help: None } + } ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..) diff --git a/tests/ui/lint/improper-ctypes/unsafe-binder-basic.rs b/tests/ui/lint/improper-ctypes/unsafe-binder-basic.rs new file mode 100644 index 0000000000000..5d4279fc834d1 --- /dev/null +++ b/tests/ui/lint/improper-ctypes/unsafe-binder-basic.rs @@ -0,0 +1,10 @@ +#![feature(unsafe_binders)] +#![expect(incomplete_features)] +#![deny(improper_ctypes)] + +extern "C" { + fn exit_2(x: unsafe<'a> &'a ()); + //~^ ERROR `extern` block uses type `unsafe<'a> &'a ()`, which is not FFI-safe +} + +fn main() {} diff --git a/tests/ui/lint/improper-ctypes/unsafe-binder-basic.stderr b/tests/ui/lint/improper-ctypes/unsafe-binder-basic.stderr new file mode 100644 index 0000000000000..4b8d51690f1a0 --- /dev/null +++ b/tests/ui/lint/improper-ctypes/unsafe-binder-basic.stderr @@ -0,0 +1,15 @@ +error: `extern` block uses type `unsafe<'a> &'a ()`, which is not FFI-safe + --> $DIR/unsafe-binder-basic.rs:6:18 + | +LL | fn exit_2(x: unsafe<'a> &'a ()); + | ^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: unsafe binders are incompatible with foreign function interfaces +note: the lint level is defined here + --> $DIR/unsafe-binder-basic.rs:3:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + From 611c956c78fc9f95bd8af1f9b0567669959d1efe Mon Sep 17 00:00:00 2001 From: dianqk Date: Mon, 8 Dec 2025 21:57:05 +0800 Subject: [PATCH 12/16] test: Add test for 146133 Even if a crate is marked as #![no_builtins], we can still generate bitcode for rlib, but we cannot emit bitcode to the linker in rustc's LTO. --- .../no-builtins-linker-plugin-lto/main.rs | 3 ++ .../no_builtins.rs | 4 ++ .../no-builtins-linker-plugin-lto/rmake.rs | 53 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 tests/run-make/no-builtins-linker-plugin-lto/main.rs create mode 100644 tests/run-make/no-builtins-linker-plugin-lto/no_builtins.rs create mode 100644 tests/run-make/no-builtins-linker-plugin-lto/rmake.rs diff --git a/tests/run-make/no-builtins-linker-plugin-lto/main.rs b/tests/run-make/no-builtins-linker-plugin-lto/main.rs new file mode 100644 index 0000000000000..f96605be89499 --- /dev/null +++ b/tests/run-make/no-builtins-linker-plugin-lto/main.rs @@ -0,0 +1,3 @@ +fn main() { + no_builtins::foo(); +} diff --git a/tests/run-make/no-builtins-linker-plugin-lto/no_builtins.rs b/tests/run-make/no-builtins-linker-plugin-lto/no_builtins.rs new file mode 100644 index 0000000000000..a56c1d6b41334 --- /dev/null +++ b/tests/run-make/no-builtins-linker-plugin-lto/no_builtins.rs @@ -0,0 +1,4 @@ +#![no_builtins] + +#[inline(never)] +pub fn foo() {} diff --git a/tests/run-make/no-builtins-linker-plugin-lto/rmake.rs b/tests/run-make/no-builtins-linker-plugin-lto/rmake.rs new file mode 100644 index 0000000000000..7133085677377 --- /dev/null +++ b/tests/run-make/no-builtins-linker-plugin-lto/rmake.rs @@ -0,0 +1,53 @@ +//@ only-x86_64-unknown-linux-gnu + +use std::fs; +use std::path::Path; + +use run_make_support::{cwd, has_extension, llvm_ar, llvm_bcanalyzer, rust_lib_name, rustc}; + +// A regression test for #146133. + +fn main() { + // Compile a `#![no_builtins]` rlib crate with `-Clinker-plugin-lto`. + // It is acceptable to generate bitcode for rlib, so there is no need to check something. + rustc().input("no_builtins.rs").crate_type("rlib").linker_plugin_lto("on").run(); + + // Checks that rustc's LTO doesn't emit any bitcode to the linker. + let stdout = rustc() + .input("main.rs") + .extern_("no_builtins", rust_lib_name("no_builtins")) + .lto("thin") + .print("link-args") + .arg("-Csave-temps") + .arg("-Clinker-features=-lld") + .run() + .stdout_utf8(); + for object in stdout + .split_whitespace() + .map(|s| s.trim_matches('"')) + .filter(|path| has_extension(path, "rlib") || has_extension(path, "o")) + { + let object_path = if !fs::exists(object).unwrap() { + cwd().join(object) + } else { + Path::new(object).to_path_buf() + }; + if has_extension(object, "rlib") { + let ar_stdout = llvm_ar().arg("t").arg(&object_path).run().stdout_utf8(); + llvm_ar().extract().arg(&object_path).run(); + for object in ar_stdout.split_whitespace().filter(|o| has_extension(o, "o")) { + let object_path = cwd().join(object); + not_bitcode(&object_path); + } + } else { + not_bitcode(&object_path); + } + } +} + +fn not_bitcode(object: &Path) { + llvm_bcanalyzer() + .input(object) + .run_fail() + .assert_stderr_contains("llvm-bcanalyzer: Invalid record at top-level"); +} From 8cf942d89fcaef9eea4a472cd2213856ba27ccc5 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 8 Dec 2025 19:56:47 +0300 Subject: [PATCH 13/16] Add inline attribute to generated delegation function if needed --- compiler/rustc_ast_lowering/src/delegation.rs | 28 +++++ tests/pretty/delegation_inline_attribute.pp | 94 ++++++++++++++++ tests/pretty/delegation_inline_attribute.rs | 104 ++++++++++++++++++ tests/pretty/hir-delegation.pp | 2 + 4 files changed, 228 insertions(+) create mode 100644 tests/pretty/delegation_inline_attribute.pp create mode 100644 tests/pretty/delegation_inline_attribute.rs diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index e6e88eff2d5bc..0e7db7c9503cb 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -44,6 +44,7 @@ use hir::{BodyId, HirId}; use rustc_abi::ExternAbi; use rustc_ast::*; use rustc_errors::ErrorGuaranteed; +use rustc_hir::attrs::{AttributeKind, InlineAttr}; use rustc_hir::def_id::DefId; use rustc_middle::span_bug; use rustc_middle::ty::{Asyncness, ResolverAstLowering}; @@ -87,6 +88,8 @@ impl<'hir> LoweringContext<'_, 'hir> { let sig_id = self.get_delegation_sig_id(item_id, delegation.id, span, is_in_trait_impl); match sig_id { Ok(sig_id) => { + self.add_inline_attribute_if_needed(span); + let is_method = self.is_method(sig_id, span); let (param_count, c_variadic) = self.param_count(sig_id); let decl = self.lower_delegation_decl(sig_id, param_count, c_variadic, span); @@ -100,6 +103,31 @@ impl<'hir> LoweringContext<'_, 'hir> { } } + fn add_inline_attribute_if_needed(&mut self, span: Span) { + const PARENT_ID: hir::ItemLocalId = hir::ItemLocalId::ZERO; + let create_inline_attr_slice = + || [hir::Attribute::Parsed(AttributeKind::Inline(InlineAttr::Hint, span))]; + + let new_attributes = match self.attrs.get(&PARENT_ID) { + Some(attrs) => { + // Check if reuse already specifies any inline attribute, if so, do nothing + if attrs + .iter() + .any(|a| matches!(a, hir::Attribute::Parsed(AttributeKind::Inline(..)))) + { + return; + } + + self.arena.alloc_from_iter( + attrs.into_iter().map(|a| a.clone()).chain(create_inline_attr_slice()), + ) + } + None => self.arena.alloc_from_iter(create_inline_attr_slice()), + }; + + self.attrs.insert(PARENT_ID, new_attributes); + } + fn get_delegation_sig_id( &self, item_id: NodeId, diff --git a/tests/pretty/delegation_inline_attribute.pp b/tests/pretty/delegation_inline_attribute.pp new file mode 100644 index 0000000000000..4b3b2aa8f80a2 --- /dev/null +++ b/tests/pretty/delegation_inline_attribute.pp @@ -0,0 +1,94 @@ +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:delegation_inline_attribute.pp + +#![allow(incomplete_features)] +#![feature(fn_delegation)] +#[attr = MacroUse {arguments: UseAll}] +extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; + +mod to_reuse { + fn foo(x: usize) -> usize { x } +} + +// Check that #[inline(hint)] is added to foo reuse +#[attr = Inline(Hint)] +fn bar(arg0: _) -> _ { to_reuse::foo(self + 1) } + +trait Trait { + fn foo(&self) { } + fn foo1(&self) { } + fn foo2(&self) { } + fn foo3(&self) { } + fn foo4(&self) { } +} + +impl Trait for u8 { } + +struct S(u8); + +mod to_import { + fn check(arg: &'_ u8) -> &'_ u8 { arg } +} + +impl Trait for S { + // Check that #[inline(hint)] is added to foo reuse + #[attr = Inline(Hint)] + fn foo(self: _) + -> + _ { + { + // Check that #[inline(hint)] is added to foo0 reuse inside another reuse + #[attr = Inline(Hint)] + fn foo0(arg0: _) -> _ { to_reuse::foo(self + 1) } + + // Check that #[inline(hint)] is added when other attributes present in inner reuse + #[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}] + #[attr = MustUse] + #[attr = Cold] + #[attr = Inline(Hint)] + fn foo1(arg0: _) -> _ { to_reuse::foo(self / 2) } + + // Check that #[inline(never)] is preserved in inner reuse + #[attr = Inline(Never)] + fn foo2(arg0: _) -> _ { to_reuse::foo(self / 2) } + + // Check that #[inline(always)] is preserved in inner reuse + #[attr = Inline(Always)] + fn foo3(arg0: _) -> _ { to_reuse::foo(self / 2) } + + // Check that #[inline(never)] is preserved when there are other attributes in inner reuse + #[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}] + #[attr = Inline(Never)] + #[attr = MustUse] + #[attr = Cold] + fn foo4(arg0: _) -> _ { to_reuse::foo(self / 2) } + }.foo() + } + + // Check that #[inline(hint)] is added when there are other attributes present in trait reuse + #[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}] + #[attr = MustUse] + #[attr = Cold] + #[attr = Inline(Hint)] + fn foo1(self: _) -> _ { self.0.foo1() } + + // Check that #[inline(never)] is preserved in trait reuse + #[attr = Inline(Never)] + fn foo2(self: _) -> _ { self.0.foo2() } + + // Check that #[inline(always)] is preserved in trait reuse + #[attr = Inline(Always)] + fn foo3(self: _) -> _ { self.0.foo3() } + + // Check that #[inline(never)] is preserved when there are other attributes in trait reuse + #[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}] + #[attr = Inline(Never)] + #[attr = MustUse] + #[attr = Cold] + fn foo4(self: _) -> _ { self.0.foo4() } +} + +fn main() { } diff --git a/tests/pretty/delegation_inline_attribute.rs b/tests/pretty/delegation_inline_attribute.rs new file mode 100644 index 0000000000000..0716cfc51f5d9 --- /dev/null +++ b/tests/pretty/delegation_inline_attribute.rs @@ -0,0 +1,104 @@ +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:delegation_inline_attribute.pp + +#![allow(incomplete_features)] +#![feature(fn_delegation)] + +mod to_reuse { + pub fn foo(x: usize) -> usize { + x + } +} + +// Check that #[inline(hint)] is added to foo reuse +reuse to_reuse::foo as bar { + self + 1 +} + +trait Trait { + fn foo(&self) {} + fn foo1(&self) {} + fn foo2(&self) {} + fn foo3(&self) {} + fn foo4(&self) {} +} + +impl Trait for u8 {} + +struct S(u8); + +mod to_import { + pub fn check(arg: &u8) -> &u8 { arg } +} + +impl Trait for S { + // Check that #[inline(hint)] is added to foo reuse + reuse Trait::foo { + // Check that #[inline(hint)] is added to foo0 reuse inside another reuse + reuse to_reuse::foo as foo0 { + self + 1 + } + + // Check that #[inline(hint)] is added when other attributes present in inner reuse + #[cold] + #[must_use] + #[deprecated] + reuse to_reuse::foo as foo1 { + self / 2 + } + + // Check that #[inline(never)] is preserved in inner reuse + #[inline(never)] + reuse to_reuse::foo as foo2 { + self / 2 + } + + // Check that #[inline(always)] is preserved in inner reuse + #[inline(always)] + reuse to_reuse::foo as foo3 { + self / 2 + } + + // Check that #[inline(never)] is preserved when there are other attributes in inner reuse + #[cold] + #[must_use] + #[inline(never)] + #[deprecated] + reuse to_reuse::foo as foo4 { + self / 2 + } + } + + // Check that #[inline(hint)] is added when there are other attributes present in trait reuse + #[cold] + #[must_use] + #[deprecated] + reuse Trait::foo1 { + self.0 + } + + // Check that #[inline(never)] is preserved in trait reuse + #[inline(never)] + reuse Trait::foo2 { + self.0 + } + + // Check that #[inline(always)] is preserved in trait reuse + #[inline(always)] + reuse Trait::foo3 { + self.0 + } + + // Check that #[inline(never)] is preserved when there are other attributes in trait reuse + #[cold] + #[must_use] + #[inline(never)] + #[deprecated] + reuse Trait::foo4 { + self.0 + } +} + +fn main() { +} diff --git a/tests/pretty/hir-delegation.pp b/tests/pretty/hir-delegation.pp index f8ad02f2fccce..b5f7a14eb2fc0 100644 --- a/tests/pretty/hir-delegation.pp +++ b/tests/pretty/hir-delegation.pp @@ -12,6 +12,7 @@ fn b(e: C) { } trait G { + #[attr = Inline(Hint)] fn b(arg0: _) -> _ { b({ }) } } @@ -19,6 +20,7 @@ fn add(a: u32, b: u32) -> u32 { a + b } } +#[attr = Inline(Hint)] fn add(arg0: _, arg1: _) -> _ { m::add(arg0, arg1) } fn main() { { let _ = add(1, 2); }; } From 27a4aabffc40beda87c5990cb9b3c95e56177da9 Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Mon, 8 Dec 2025 18:52:46 +0100 Subject: [PATCH 14/16] =?UTF-8?q?Fix=20typo=20"an"=20=E2=86=92=20"and"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- library/std/src/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 8b3e943b4ccd0..d14d0ad2f0ea6 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1324,7 +1324,7 @@ impl Read for &File { /// /// # Platform-specific behavior /// - /// This function currently returns `true` on Unix an `false` on Windows. + /// This function currently returns `true` on Unix and `false` on Windows. /// Note that this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior @@ -1385,7 +1385,7 @@ impl Write for &File { /// /// # Platform-specific behavior /// - /// This function currently returns `true` on Unix an `false` on Windows. + /// This function currently returns `true` on Unix and `false` on Windows. /// Note that this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior From b8581ddecc2adb994f8ddd54a8c3dc3205dec547 Mon Sep 17 00:00:00 2001 From: Urgau <3616612+Urgau@users.noreply.github.com> Date: Mon, 8 Dec 2025 19:34:19 +0100 Subject: [PATCH 15/16] Remove `[no-mentions]` handler in the triagebot config https://github.blog/changelog/2025-11-07-removing-notifications-for-mentions-in-commit-messages/ --- triagebot.toml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index 6ad8dd6b4c886..c459f69d06bb6 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1680,12 +1680,6 @@ days-threshold = 28 # Documentation at: https://forge.rust-lang.org/triagebot/issue-links.html [issue-links] -# Prevents mentions in commits to avoid users being spammed -# Documentation at: https://forge.rust-lang.org/triagebot/no-mentions.html -[no-mentions] -# Subtree update authors can't fix it, no point in warning. -exclude-titles = ["subtree update"] - # Allow members to formally register concerns (`@rustbot concern my concern`) # Documentation at: https://forge.rust-lang.org/triagebot/concern.html [concern] From 90d2477e4e35927b20237b27869200fb1d47e7bf Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 4 Dec 2025 09:49:31 -0800 Subject: [PATCH 16/16] Add release notes for 1.92.0 --- RELEASES.md | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index b14cc499b46d0..0dffe931e6eb1 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,88 @@ +Version 1.92.0 (2025-12-11) +========================== + + + +Language +-------- +- [Document `MaybeUninit` representation and validity](https://github.com/rust-lang/rust/pull/140463) +- [Allow `&raw [mut | const]` for union field in safe code](https://github.com/rust-lang/rust/pull/141469) +- [Prefer item bounds of associated types over where-bounds for auto-traits and `Sized`](https://github.com/rust-lang/rust/pull/144064) +- [Do not materialize `X` in `[X; 0]` when `X` is unsizing a const](https://github.com/rust-lang/rust/pull/145277) +- [Support combining `#[track_caller]` and `#[no_mangle]` (requires every declaration specifying `#[track_caller]` as well)](https://github.com/rust-lang/rust/pull/145724) +- [Make never type lints `never_type_fallback_flowing_into_unsafe` and `dependency_on_unit_never_type_fallback` deny-by-default](https://github.com/rust-lang/rust/pull/146167) +- [Allow specifying multiple bounds for same associated item, except in trait objects](https://github.com/rust-lang/rust/pull/146593) +- [Slightly strengthen higher-ranked region handling in coherence](https://github.com/rust-lang/rust/pull/146725) +- [The `unused_must_use` lint no longer warns on `Result<(), Uninhabited>` (for instance, `Result<(), !>`), or `ControlFlow`](https://github.com/rust-lang/rust/pull/147382). This avoids having to check for an error that can never happen. + + + +Compiler +-------- +- [Make `mips64el-unknown-linux-muslabi64` link dynamically](https://github.com/rust-lang/rust/pull/146858) +- [Remove current code for embedding command-line args in PDB](https://github.com/rust-lang/rust/pull/147022) + Command-line information is typically not needed by debugging tools, and the removed code + was causing problems for incremental builds even on targets that don't use PDB debuginfo. + + + +Libraries +--------- +- [Specialize `Iterator::eq{_by}` for `TrustedLen` iterators](https://github.com/rust-lang/rust/pull/137122) +- [Simplify `Extend` for tuples](https://github.com/rust-lang/rust/pull/138799) +- [Added details to `Debug` for `EncodeWide`](https://github.com/rust-lang/rust/pull/140153). +- [`iter::Repeat::last`](https://github.com/rust-lang/rust/pull/147258) and [`count`](https://github.com/rust-lang/rust/pull/146410) will now panic, rather than looping infinitely. + + + +Stabilized APIs +--------------- + +- [`NonZero::div_ceil`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.div_ceil) +- [`Location::file_as_c_str`](https://doc.rust-lang.org/stable/std/panic/struct.Location.html#method.file_as_c_str) +- [`RwLockWriteGuard::downgrade`](https://doc.rust-lang.org/stable/std/sync/struct.RwLockWriteGuard.html#method.downgrade) +- [`Box::new_zeroed`](https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.new_zeroed) +- [`Box::new_zeroed_slice`](https://doc.rust-lang.org/stable/std/boxed/struct.Box.html#method.new_zeroed_slice) +- [`Rc::new_zeroed`](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.new_zeroed) +- [`Rc::new_zeroed_slice`](https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.new_zeroed_slice) +- [`Arc::new_zeroed`](https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.new_zeroed) +- [`Arc::new_zeroed_slice`](https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.new_zeroed_slice) +- [`btree_map::Entry::insert_entry`](https://doc.rust-lang.org/stable/std/collections/btree_map/enum.Entry.html#method.insert_entry) +- [`btree_map::VacantEntry::insert_entry`](https://doc.rust-lang.org/stable/std/collections/btree_map/struct.VacantEntry.html#method.insert_entry) +- [`impl Extend for proc_macro::TokenStream`](https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CGroup%3E-for-TokenStream) +- [`impl Extend for proc_macro::TokenStream`](https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CLiteral%3E-for-TokenStream) +- [`impl Extend for proc_macro::TokenStream`](https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CPunct%3E-for-TokenStream) +- [`impl Extend for proc_macro::TokenStream`](https://doc.rust-lang.org/stable/proc_macro/struct.TokenStream.html#impl-Extend%3CIdent%3E-for-TokenStream) + +These previously stable APIs are now stable in const contexts: + +- [`<[_]>::rotate_left`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.rotate_left) +- [`<[_]>::rotate_right`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.rotate_right) + + + +Cargo +----- +- [Added a new chapter](https://github.com/rust-lang/cargo/issues/16119) to the Cargo book, ["Optimizing Build Performance"](https://doc.rust-lang.org/stable/cargo/guide/build-performance.html). + + + +Rustdoc +----- +- [If a trait item appears in rustdoc search, hide the corresponding impl items](https://github.com/rust-lang/rust/pull/145898). Previously a search for "last" would show both `Iterator::last` as well as impl methods like `std::vec::IntoIter::last`. Now these impl methods will be hidden, freeing up space for inherent methods like `BTreeSet::last`. +- [Relax rules for identifiers in search](https://github.com/rust-lang/rust/pull/147860). Previously you could only search for identifiers that were valid in rust code, now searches only need to be valid as part of an identifier. For example, you can now perform a search that starts with a digit. + + + +Compatibility Notes +------------------- +* [Fix backtraces with `-C panic=abort` on Linux by generating unwind tables by default](https://github.com/rust-lang/rust/pull/143613). Build with `-C force-unwind-tables=no` to keep omitting unwind tables. +- As part of the larger effort refactoring compiler built-in attributes and their diagnostics, [the future-compatibility lint `invalid_macro_export_arguments` is upgraded to deny-by-default and will be reported in dependencies too.](https://github.com/rust-lang/rust/pull/143857) +- [Update the minimum external LLVM to 20](https://github.com/rust-lang/rust/pull/145071) +- [Prevent downstream `impl DerefMut for Pin`](https://github.com/rust-lang/rust/pull/145608) +- [Don't apply temporary lifetime extension rules to the arguments of non-extended `pin!` and formatting macros](https://github.com/rust-lang/rust/pull/145838) + + Version 1.91.1 (2025-11-10) ===========================