diff --git a/Cargo.lock b/Cargo.lock index 3b56dd6bd9ceb..5a9764bfe084c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -199,7 +199,7 @@ dependencies = [ "rustc-hash 2.1.1", "serde", "serde_derive", - "syn 2.0.110", + "syn", ] [[package]] @@ -396,7 +396,7 @@ checksum = "89385e82b5d1821d2219e0b095efa2cc1f246cbf99080f3be46a1a85c0d392d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -573,7 +573,6 @@ checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ "iana-time-zone", "num-traits", - "serde", "windows-link 0.2.1", ] @@ -635,10 +634,10 @@ version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -768,7 +767,6 @@ dependencies = [ "serde", "serde_json", "similar", - "spdx-rs", ] [[package]] @@ -804,7 +802,7 @@ dependencies = [ "nom", "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -1047,7 +1045,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.110", + "syn", ] [[package]] @@ -1061,7 +1059,7 @@ dependencies = [ "indexmap", "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -1079,7 +1077,7 @@ dependencies = [ "indexmap", "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -1103,7 +1101,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.110", + "syn", ] [[package]] @@ -1114,7 +1112,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -1146,7 +1144,7 @@ checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -1167,7 +1165,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -1177,7 +1175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.110", + "syn", ] [[package]] @@ -1189,7 +1187,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -1264,7 +1262,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -1686,12 +1684,6 @@ dependencies = [ "foldhash 0.2.0", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -2092,7 +2084,7 @@ checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -2389,7 +2381,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -2534,7 +2526,7 @@ checksum = "4568f25ccbd45ab5d5603dc34318c1ec56b117531781260002151b8530a9f931" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -2915,7 +2907,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -3125,7 +3117,7 @@ checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -3316,7 +3308,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -3409,7 +3401,7 @@ checksum = "8100bb34c0a1d0f907143db3149e6b4eea3c33b9ee8b189720168e818303986f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -4125,7 +4117,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -4271,7 +4263,7 @@ dependencies = [ "fluent-syntax", "proc-macro2", "quote", - "syn 2.0.110", + "syn", "synstructure", ] @@ -4852,7 +4844,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", "synstructure", ] @@ -4952,7 +4944,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.110", + "syn", ] [[package]] @@ -5069,7 +5061,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.110", + "syn", ] [[package]] @@ -5155,7 +5147,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -5166,7 +5158,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -5299,35 +5291,6 @@ dependencies = [ "color-eyre", ] -[[package]] -name = "spdx-expression" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d7ac03c67c572d85049d6db815e20a4a19b41b3d5cca732ac582342021ad77" -dependencies = [ - "nom", - "serde", - "thiserror 1.0.69", - "tracing", -] - -[[package]] -name = "spdx-rs" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990870190ec8d8c64ba66e4a6746243d6e57d99353991e0e6092334833f429b1" -dependencies = [ - "chrono", - "log", - "nom", - "serde", - "spdx-expression", - "strum", - "strum_macros", - "thiserror 1.0.69", - "uuid", -] - [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -5393,36 +5356,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" - -[[package]] -name = "strum_macros" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 1.0.109", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.110" @@ -5442,7 +5375,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -5579,7 +5512,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -5590,7 +5523,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -5832,7 +5765,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -6028,7 +5961,7 @@ checksum = "a1249a628de3ad34b821ecb1001355bca3940bcb2f88558f1a8bd82e977f75b5" dependencies = [ "proc-macro-hack", "quote", - "syn 2.0.110", + "syn", "unic-langid-impl", ] @@ -6266,7 +6199,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.110", + "syn", "wasm-bindgen-shared", ] @@ -6523,7 +6456,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -6534,7 +6467,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -6904,7 +6837,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", "synstructure", ] @@ -6925,7 +6858,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] @@ -6945,7 +6878,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", "synstructure", ] @@ -6980,7 +6913,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn", ] [[package]] diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index a5b8c0ebe25eb..665c516c3e8e8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -13,6 +13,7 @@ use thin_vec::ThinVec; use super::prelude::{ALL_TARGETS, AllowedTargets}; use super::{AcceptMapping, AttributeParser}; use crate::context::{AcceptContext, FinalizeContext, Stage}; +use crate::errors::{DocAliasDuplicated, DocAutoCfgExpectsHideOrShow, IllFormedAttributeInput}; use crate::parser::{ArgParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser}; use crate::session_diagnostics::{ DocAliasBadChar, DocAliasEmpty, DocAliasMalformed, DocAliasStartEnd, DocAttrNotCrateLevel, @@ -255,9 +256,9 @@ impl DocParser { } if let Some(first_definition) = self.attribute.aliases.get(&alias).copied() { - cx.emit_lint( + cx.emit_dyn_lint( rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - AttributeLintKind::DuplicateDocAlias { first_definition }, + move |dcx, level| DocAliasDuplicated { first_definition }.into_diag(dcx, level), span, ); } @@ -343,9 +344,9 @@ impl DocParser { ArgParser::List(list) => { for meta in list.mixed() { let MetaItemOrLitParser::MetaItemParser(item) = meta else { - cx.emit_lint( + cx.emit_dyn_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - AttributeLintKind::DocAutoCfgExpectsHideOrShow, + |dcx, level| DocAutoCfgExpectsHideOrShow.into_diag(dcx, level), meta.span(), ); continue; @@ -354,9 +355,9 @@ impl DocParser { Some(sym::hide) => (HideOrShow::Hide, sym::hide), Some(sym::show) => (HideOrShow::Show, sym::show), _ => { - cx.emit_lint( + cx.emit_dyn_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - AttributeLintKind::DocAutoCfgExpectsHideOrShow, + |dcx, level| DocAutoCfgExpectsHideOrShow.into_diag(dcx, level), item.span(), ); continue; @@ -666,12 +667,10 @@ impl DocParser { ArgParser::NoArgs => { let suggestions = cx.adcx().suggestions(); let span = cx.attr_span; - cx.emit_lint( + cx.emit_dyn_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - AttributeLintKind::IllFormedAttributeInput { - suggestions, - docs: None, - help: None, + move |dcx, level| { + IllFormedAttributeInput::new(&suggestions, None, None).into_diag(dcx, level) }, span, ); diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index aa9284e54d369..becdaee0f3d3a 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -897,11 +897,18 @@ where } pub(crate) fn warn_empty_attribute(&mut self, span: Span) { - let attr_path = self.attr_path.clone().to_string(); + let attr_path = self.attr_path.to_string(); let valid_without_list = self.template.word; - self.emit_lint( + self.emit_dyn_lint( rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - AttributeLintKind::EmptyAttribute { first_span: span, attr_path, valid_without_list }, + move |dcx, level| { + crate::errors::EmptyAttributeList { + attr_span: span, + attr_path: &attr_path, + valid_without_list, + } + .into_diag(dcx, level) + }, span, ); } @@ -916,9 +923,12 @@ where ) { let suggestions = self.suggestions(); let span = self.attr_span; - self.emit_lint( + self.emit_dyn_lint( lint, - AttributeLintKind::IllFormedAttributeInput { suggestions, docs: None, help }, + move |dcx, level| { + crate::errors::IllFormedAttributeInput::new(&suggestions, None, help.as_deref()) + .into_diag(dcx, level) + }, span, ); } diff --git a/compiler/rustc_attr_parsing/src/errors.rs b/compiler/rustc_attr_parsing/src/errors.rs index 7049ffae89ab1..8148a859958b5 100644 --- a/compiler/rustc_attr_parsing/src/errors.rs +++ b/compiler/rustc_attr_parsing/src/errors.rs @@ -1,4 +1,4 @@ -use rustc_errors::MultiSpan; +use rustc_errors::{DiagArgValue, MultiSpan}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; @@ -66,3 +66,114 @@ pub(crate) struct UnsafeAttrOutsideUnsafeLint { #[subdiagnostic] pub suggestion: Option, } + +#[derive(Diagnostic)] +#[diag( + "{$num_suggestions -> + [1] attribute must be of the form {$suggestions} + *[other] valid forms for the attribute are {$suggestions} + }" +)] +pub(crate) struct IllFormedAttributeInput { + pub num_suggestions: usize, + pub suggestions: DiagArgValue, + #[note("for more information, visit <{$docs}>")] + pub has_docs: bool, + pub docs: &'static str, + #[subdiagnostic] + help: Option, +} + +impl IllFormedAttributeInput { + pub(crate) fn new( + suggestions: &[String], + docs: Option<&'static str>, + help: Option<&str>, + ) -> Self { + Self { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + has_docs: docs.is_some(), + docs: docs.unwrap_or(""), + help: help.map(|h| IllFormedAttributeInputHelp { lint: h.to_string() }), + } + } +} + +#[derive(Subdiagnostic)] +#[help( + "if you meant to silence a warning, consider using #![allow({$lint})] or #![expect({$lint})]" +)] +struct IllFormedAttributeInputHelp { + pub lint: String, +} + +#[derive(Diagnostic)] +#[diag("unused attribute")] +#[note( + "{$valid_without_list -> + [true] using `{$attr_path}` with an empty list is equivalent to not using a list at all + *[other] using `{$attr_path}` with an empty list has no effect + }" +)] +pub(crate) struct EmptyAttributeList<'a> { + #[suggestion( + "{$valid_without_list -> + [true] remove these parentheses + *[other] remove this attribute + }", + code = "", + applicability = "machine-applicable" + )] + pub attr_span: Span, + pub attr_path: &'a str, + pub valid_without_list: bool, +} + +#[derive(Diagnostic)] +#[diag("`#[{$name}]` attribute cannot be used on {$target}")] +#[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" +)] +#[help("`#[{$name}]` can {$only}be applied to {$applied}")] +pub(crate) struct InvalidTargetLint { + pub name: String, + pub target: &'static str, + pub applied: DiagArgValue, + pub only: &'static str, + #[suggestion( + "remove the attribute", + code = "", + applicability = "machine-applicable", + style = "tool-only" + )] + pub attr_span: Span, +} + +#[derive(Diagnostic)] +#[diag( + "{$is_used_as_inner -> + [false] crate-level attribute should be an inner attribute: add an exclamation mark: `#![{$name}]` + *[other] the `#![{$name}]` attribute can only be used at the crate root + }" +)] +pub(crate) struct InvalidAttrStyle<'a> { + pub name: &'a str, + pub is_used_as_inner: bool, + #[note("this attribute does not have an `!`, which means it is applied to this {$target}")] + pub target_span: Option, + pub target: &'static str, +} + +#[derive(Diagnostic)] +#[diag("doc alias is duplicated")] +pub(crate) struct DocAliasDuplicated { + #[label("first defined here")] + pub first_definition: Span, +} + +#[derive(Diagnostic)] +#[diag("only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]`")] +pub(crate) struct DocAutoCfgExpectsHideOrShow; diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index 253a089e49f1a..65e716921f5cb 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -1,17 +1,17 @@ use std::borrow::Cow; use rustc_ast::AttrStyle; -use rustc_errors::{DiagArgValue, MultiSpan, StashKey}; +use rustc_errors::{DiagArgValue, Diagnostic, MultiSpan, StashKey}; use rustc_feature::Features; use rustc_hir::attrs::AttributeKind; -use rustc_hir::lints::AttributeLintKind; use rustc_hir::{AttrItem, Attribute, MethodKind, Target}; use rustc_span::{BytePos, Span, Symbol, sym}; use crate::AttributeParser; use crate::context::{AcceptContext, Stage}; use crate::errors::{ - InvalidAttrAtCrateLevel, ItemFollowingInnerAttr, UnsupportedAttributesInWhere, + InvalidAttrAtCrateLevel, InvalidTargetLint, ItemFollowingInnerAttr, + UnsupportedAttributesInWhere, }; use crate::session_diagnostics::InvalidTarget; use crate::target_checking::Policy::Allow; @@ -142,14 +142,19 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { }; let attr_span = cx.attr_span; - cx.emit_lint( + cx.emit_dyn_lint( lint, - AttributeLintKind::InvalidTarget { - name: name.to_string(), - target: target.plural_name(), - only: if only { "only " } else { "" }, - applied, - attr_span, + move |dcx, level| { + InvalidTargetLint { + name: name.to_string(), + target: target.plural_name(), + only: if only { "only " } else { "" }, + applied: DiagArgValue::StrListSepByAnd( + applied.iter().map(|i| Cow::Owned(i.to_string())).collect(), + ), + attr_span, + } + .into_diag(dcx, level) }, attr_span, ); @@ -176,15 +181,24 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { return; } - let kind = AttributeLintKind::InvalidStyle { - name: cx.attr_path.to_string(), - is_used_as_inner: cx.attr_style == AttrStyle::Inner, - target: target.name(), - target_span: cx.target_span, - }; + let name = cx.attr_path.to_string(); + let is_used_as_inner = cx.attr_style == AttrStyle::Inner; + let target_span = cx.target_span; let attr_span = cx.attr_span; - cx.emit_lint(rustc_session::lint::builtin::UNUSED_ATTRIBUTES, kind, attr_span); + cx.emit_dyn_lint( + rustc_session::lint::builtin::UNUSED_ATTRIBUTES, + move |dcx, level| { + crate::errors::InvalidAttrStyle { + name: &name, + is_used_as_inner, + target_span: (!is_used_as_inner).then_some(target_span), + target: target.name(), + } + .into_diag(dcx, level) + }, + attr_span, + ); } // FIXME: Fix "Cannot determine resolution" error and remove built-in macros diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs index 06ff674f25389..d8c4aaa2e11ef 100644 --- a/compiler/rustc_attr_parsing/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -8,10 +8,9 @@ use rustc_ast::tokenstream::DelimSpan; use rustc_ast::{ self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, Safety, }; -use rustc_errors::{Applicability, PResult}; +use rustc_errors::{Applicability, Diagnostic, PResult}; use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, template}; use rustc_hir::AttrPath; -use rustc_hir::lints::AttributeLintKind; use rustc_parse::parse_in; use rustc_session::errors::report_lit_error; use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; @@ -210,14 +209,14 @@ pub fn emit_malformed_attribute( suggestions.clear(); } if should_warn(name) { - psess.buffer_lint( + let suggestions = suggestions.clone(); + psess.dyn_buffer_lint( ILL_FORMED_ATTRIBUTE_INPUT, span, ast::CRATE_NODE_ID, - AttributeLintKind::IllFormedAttributeInput { - suggestions: suggestions.clone(), - docs: template.docs, - help: None, + move |dcx, level| { + crate::errors::IllFormedAttributeInput::new(&suggestions, template.docs, None) + .into_diag(dcx, level) }, ); } else { diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index adcf4086fb1e4..db5aecde3472c 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -370,6 +370,8 @@ fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> Box { let ecx = &cx.ext_cx; let mut tests = cx.test_cases.clone(); + // Note that this sort is load-bearing: the libtest harness uses binary search to find tests by + // name. tests.sort_by(|a, b| a.name.as_str().cmp(b.name.as_str())); ecx.expr_array_ref( diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index b8be612fbe694..b8ba6db9aa807 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -3,7 +3,8 @@ use std::ffi::c_uint; use std::{assert_matches, iter, ptr}; use rustc_abi::{ - Align, BackendRepr, Float, HasDataLayout, NumScalableVectors, Primitive, Size, WrappingRange, + Align, BackendRepr, Float, HasDataLayout, Integer, NumScalableVectors, Primitive, Size, + WrappingRange, }; use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh}; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; @@ -288,10 +289,17 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { bug!("the va_arg intrinsic does not support non-scalar types") }; + // We reject types that would never be passed as varargs in C because + // they get promoted to a larger type, specifically integers smaller than + // c_int and float type smaller than c_double. match scalar.primitive() { Primitive::Pointer(_) => { // Pointers are always OK. - emit_va_arg(self, args[0], result.layout.ty) + } + Primitive::Int(Integer::I128, _) => { + // FIXME: maybe we should support these? At least on 32-bit powerpc + // the logic in LLVM does not handle i128 correctly though. + bug!("the va_arg intrinsic does not support `i128`/`u128`") } Primitive::Int(..) => { let int_width = self.cx().size_of(result.layout.ty).bits(); @@ -305,27 +313,26 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { target_c_int_width ); } - emit_va_arg(self, args[0], result.layout.ty) } Primitive::Float(Float::F16) => { bug!("the va_arg intrinsic does not support `f16`") } Primitive::Float(Float::F32) => { - if self.cx().sess().target.arch == Arch::Avr { - // c_double is actually f32 on avr. - emit_va_arg(self, args[0], result.layout.ty) - } else { + // c_double is actually f32 on avr. + if self.cx().sess().target.arch != Arch::Avr { bug!("the va_arg intrinsic does not support `f32` on this target") } } Primitive::Float(Float::F64) => { // 64-bit floats are always OK. - emit_va_arg(self, args[0], result.layout.ty) } Primitive::Float(Float::F128) => { + // FIXME(f128) figure out whether we should support this. bug!("the va_arg intrinsic does not support `f128`") } } + + emit_va_arg(self, args[0], result.layout.ty) } sym::volatile_load | sym::unaligned_volatile_load => { diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 7340ba0b2f391..fb59cd35ad463 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -1,8 +1,7 @@ use std::any::Any; -use std::borrow::Cow; use rustc_data_structures::sync::DynSend; -use rustc_errors::{Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, Level}; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_hir::lints::{AttributeLintKind, FormatWarning}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -35,50 +34,6 @@ pub struct DecorateAttrLint<'a, 'sess, 'tcx> { impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { match self.diagnostic { - AttributeLintKind::IllFormedAttributeInput { suggestions, docs, help } => { - lints::IllFormedAttributeInput { - num_suggestions: suggestions.len(), - suggestions: DiagArgValue::StrListSepByAnd( - suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), - ), - has_docs: docs.is_some(), - docs: docs.unwrap_or(""), - help: help.clone().map(|h| lints::IllFormedAttributeInputHelp { lint: h }), - } - .into_diag(dcx, level) - } - AttributeLintKind::EmptyAttribute { first_span, attr_path, valid_without_list } => { - lints::EmptyAttributeList { - attr_span: *first_span, - attr_path: attr_path.clone(), - valid_without_list: *valid_without_list, - } - .into_diag(dcx, level) - } - AttributeLintKind::InvalidTarget { name, target, applied, only, attr_span } => { - lints::InvalidTargetLint { - name: name.clone(), - target, - applied: DiagArgValue::StrListSepByAnd( - applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(), - ), - only, - attr_span: *attr_span, - } - .into_diag(dcx, level) - } - &AttributeLintKind::InvalidStyle { - ref name, - is_used_as_inner, - target, - target_span, - } => lints::InvalidAttrStyle { - name: name.clone(), - is_used_as_inner, - target_span: (!is_used_as_inner).then_some(target_span), - target, - } - .into_diag(dcx, level), &AttributeLintKind::UnexpectedCfgName(name, value) => { check_cfg::unexpected_cfg_name(self.sess, self.tcx, name, value) .into_diag(dcx, level) @@ -87,13 +42,6 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { check_cfg::unexpected_cfg_value(self.sess, self.tcx, name, value) .into_diag(dcx, level) } - &AttributeLintKind::DuplicateDocAlias { first_definition } => { - lints::DocAliasDuplicated { first_defn: first_definition }.into_diag(dcx, level) - } - - &AttributeLintKind::DocAutoCfgExpectsHideOrShow => { - lints::DocAutoCfgExpectsHideOrShow.into_diag(dcx, level) - } &AttributeLintKind::AmbiguousDeriveHelpers => { lints::AmbiguousDeriveHelpers.into_diag(dcx, level) diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 20d88505f042d..b92efc408ae81 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -6,7 +6,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::codes::*; use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ - Applicability, Diag, DiagArgValue, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic, + Applicability, Diag, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic, EmissionGuarantee, Level, Subdiagnostic, SuggestionStyle, msg, }; use rustc_hir as hir; @@ -3025,32 +3025,6 @@ pub(crate) mod unexpected_cfg_value { } } -// FIXME(jdonszelmann): duplicated in rustc_attr_parsing, should be moved there completely. -#[derive(Diagnostic)] -#[diag( - "{$num_suggestions -> - [1] attribute must be of the form {$suggestions} - *[other] valid forms for the attribute are {$suggestions} - }" -)] -pub(crate) struct IllFormedAttributeInput { - pub num_suggestions: usize, - pub suggestions: DiagArgValue, - #[note("for more information, visit <{$docs}>")] - pub has_docs: bool, - pub docs: &'static str, - #[subdiagnostic] - pub help: Option, -} - -#[derive(Subdiagnostic)] -#[help( - "if you meant to silence a warning, consider using #![allow({$lint})] or #![expect({$lint})]" -)] -pub(crate) struct IllFormedAttributeInputHelp { - pub lint: String, -} - #[derive(Diagnostic)] #[diag("creating a {$shared_label}reference to mutable static")] pub(crate) struct RefOfMutStatic<'a> { @@ -3308,63 +3282,6 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { } } -#[derive(Diagnostic)] -#[diag("unused attribute")] -#[note( - "{$valid_without_list -> - [true] using `{$attr_path}` with an empty list is equivalent to not using a list at all - *[other] using `{$attr_path}` with an empty list has no effect - }" -)] -pub(crate) struct EmptyAttributeList { - #[suggestion( - "{$valid_without_list -> - [true] remove these parentheses - *[other] remove this attribute - }", - code = "", - applicability = "machine-applicable" - )] - pub attr_span: Span, - pub attr_path: String, - pub valid_without_list: bool, -} - -#[derive(Diagnostic)] -#[diag("`#[{$name}]` attribute cannot be used on {$target}")] -#[warning( - "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" -)] -#[help("`#[{$name}]` can {$only}be applied to {$applied}")] -pub(crate) struct InvalidTargetLint { - pub name: String, - pub target: &'static str, - pub applied: DiagArgValue, - pub only: &'static str, - #[suggestion( - "remove the attribute", - code = "", - applicability = "machine-applicable", - style = "tool-only" - )] - pub attr_span: Span, -} - -#[derive(Diagnostic)] -#[diag( - "{$is_used_as_inner -> - [false] crate-level attribute should be an inner attribute: add an exclamation mark: `#![{$name}]` - *[other] the `#![{$name}]` attribute can only be used at the crate root - }" -)] -pub(crate) struct InvalidAttrStyle { - pub name: String, - pub is_used_as_inner: bool, - #[note("this attribute does not have an `!`, which means it is applied to this {$target}")] - pub target_span: Option, - pub target: &'static str, -} - #[derive(Diagnostic)] #[diag("malformed `doc` attribute input")] #[warning( @@ -3386,17 +3303,6 @@ pub(crate) struct ExpectedNoArgs; )] pub(crate) struct ExpectedNameValue; -#[derive(Diagnostic)] -#[diag("doc alias is duplicated")] -pub(crate) struct DocAliasDuplicated { - #[label("first defined here")] - pub first_defn: Span, -} - -#[derive(Diagnostic)] -#[diag("only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]`")] -pub(crate) struct DocAutoCfgExpectsHideOrShow; - #[derive(Diagnostic)] #[diag("there exists a built-in attribute with the same name")] pub(crate) struct AmbiguousDeriveHelpers; diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index ea5006c7f03f3..29da46770d52b 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -654,98 +654,32 @@ pub enum DeprecatedSinceKind { #[derive(Debug)] pub enum AttributeLintKind { - IllFormedAttributeInput { - suggestions: Vec, - docs: Option<&'static str>, - help: Option, - }, - EmptyAttribute { - first_span: Span, - attr_path: String, - valid_without_list: bool, - }, - InvalidTarget { - name: String, - target: &'static str, - applied: Vec, - only: &'static str, - attr_span: Span, - }, - InvalidStyle { - name: String, - is_used_as_inner: bool, - target: &'static str, - target_span: Span, - }, UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>), UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>), - DuplicateDocAlias { - first_definition: Span, - }, - DocAutoCfgExpectsHideOrShow, - DocAutoCfgHideShowUnexpectedItem { - attr_name: Symbol, - }, - DocAutoCfgHideShowExpectsList { - attr_name: Symbol, - }, + DocAutoCfgHideShowUnexpectedItem { attr_name: Symbol }, + DocAutoCfgHideShowExpectsList { attr_name: Symbol }, DocInvalid, AmbiguousDeriveHelpers, - DocUnknownInclude { - span: Span, - inner: &'static str, - value: Symbol, - }, - DocUnknownSpotlight { - span: Span, - }, - DocUnknownPasses { - name: Symbol, - span: Span, - }, - DocUnknownPlugins { - span: Span, - }, - DocUnknownAny { - name: Symbol, - }, + DocUnknownInclude { span: Span, inner: &'static str, value: Symbol }, + DocUnknownSpotlight { span: Span }, + DocUnknownPasses { name: Symbol, span: Span }, + DocUnknownPlugins { span: Span }, + DocUnknownAny { name: Symbol }, DocAutoCfgWrongLiteral, DocTestTakesList, - DocTestUnknown { - name: Symbol, - }, + DocTestUnknown { name: Symbol }, DocTestLiteral, AttrCrateLevelOnly, DoNotRecommendDoesNotExpectArgs, - CrateTypeUnknown { - span: Span, - suggested: Option, - }, + CrateTypeUnknown { span: Span, suggested: Option }, MalformedDoc, ExpectedNoArgs, ExpectedNameValue, - MalFormedDiagnosticAttribute { - attribute: &'static str, - options: &'static str, - span: Span, - }, - MalformedDiagnosticFormat { - warning: FormatWarning, - }, - DiagnosticWrappedParserError { - description: String, - label: String, - span: Span, - }, - IgnoredDiagnosticOption { - option_name: Symbol, - first_span: Span, - later_span: Span, - }, - MissingOptionsForDiagnosticAttribute { - attribute: &'static str, - options: &'static str, - }, + MalFormedDiagnosticAttribute { attribute: &'static str, options: &'static str, span: Span }, + MalformedDiagnosticFormat { warning: FormatWarning }, + DiagnosticWrappedParserError { description: String, label: String, span: Span }, + IgnoredDiagnosticOption { option_name: Symbol, first_span: Span, later_span: Span }, + MissingOptionsForDiagnosticAttribute { attribute: &'static str, options: &'static str }, NonMetaItemDiagnosticAttribute, } diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 3231c4193064c..2416406576204 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -93,31 +93,31 @@ const MAX_THREE_B: u32 = 0x10000; /// The highest valid code point a `char` can have, `'\u{10FFFF}'`. Use [`char::MAX`] instead. #[stable(feature = "rust1", since = "1.0.0")] +#[deprecated(since = "TBD", note = "replaced by the `MAX` associated constant on `char`")] pub const MAX: char = char::MAX; -/// The maximum number of bytes required to [encode](char::encode_utf8) a `char` to -/// UTF-8 encoding. -#[unstable(feature = "char_max_len", issue = "121714")] -pub const MAX_LEN_UTF8: usize = char::MAX_LEN_UTF8; - -/// The maximum number of two-byte units required to [encode](char::encode_utf16) a `char` -/// to UTF-16 encoding. -#[unstable(feature = "char_max_len", issue = "121714")] -pub const MAX_LEN_UTF16: usize = char::MAX_LEN_UTF16; - /// `U+FFFD REPLACEMENT CHARACTER` (�) is used in Unicode to represent a /// decoding error. Use [`char::REPLACEMENT_CHARACTER`] instead. #[stable(feature = "decode_utf16", since = "1.9.0")] +#[deprecated( + since = "TBD", + note = "replaced by the `REPLACEMENT_CHARACTER` associated constant on `char`" +)] pub const REPLACEMENT_CHARACTER: char = char::REPLACEMENT_CHARACTER; /// The version of [Unicode](https://www.unicode.org/) that the Unicode parts of /// `char` and `str` methods are based on. Use [`char::UNICODE_VERSION`] instead. #[stable(feature = "unicode_version", since = "1.45.0")] +#[deprecated( + since = "TBD", + note = "replaced by the `UNICODE_VERSION` associated constant on `char`" +)] pub const UNICODE_VERSION: (u8, u8, u8) = char::UNICODE_VERSION; /// Creates an iterator over the UTF-16 encoded code points in `iter`, returning /// unpaired surrogates as `Err`s. Use [`char::decode_utf16`] instead. #[stable(feature = "decode_utf16", since = "1.9.0")] +#[deprecated(since = "TBD", note = "replaced by the `decode_utf16` method on `char`")] #[inline] pub fn decode_utf16>(iter: I) -> DecodeUtf16 { self::decode::decode_utf16(iter) @@ -126,6 +126,7 @@ pub fn decode_utf16>(iter: I) -> DecodeUtf16 Option { @@ -136,6 +137,7 @@ pub const fn from_u32(i: u32) -> Option { /// instead. #[stable(feature = "char_from_unchecked", since = "1.5.0")] #[rustc_const_stable(feature = "const_char_from_u32_unchecked", since = "1.81.0")] +#[deprecated(since = "TBD", note = "replaced by the `from_u32_unchecked` method on `char`")] #[must_use] #[inline] pub const unsafe fn from_u32_unchecked(i: u32) -> char { @@ -146,6 +148,7 @@ pub const unsafe fn from_u32_unchecked(i: u32) -> char { /// Converts a digit in the given radix to a `char`. Use [`char::from_digit`] instead. #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_char_convert", since = "1.67.0")] +#[deprecated(since = "TBD", note = "replaced by the `from_digit` method on `char`")] #[must_use] #[inline] pub const fn from_digit(num: u32, radix: u32) -> Option { diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 33397e56b86c5..08a12b6447e61 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -168,7 +168,7 @@ macro_rules! assert_ne { #[allow_internal_unstable(panic_internals)] #[rustc_macro_transparency = "semiopaque"] pub macro assert_matches { - ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => { + ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => {{ match $left { $( $pattern )|+ $( if $guard )? => {} ref left_val => { @@ -179,8 +179,8 @@ pub macro assert_matches { ); } } - }, - ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )?, $($arg:tt)+) => { + }}, + ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )?, $($arg:tt)+) => {{ match $left { $( $pattern )|+ $( if $guard )? => {} ref left_val => { @@ -191,7 +191,7 @@ pub macro assert_matches { ); } } - }, + }}, } /// Selects code at compile-time based on `cfg` predicates. diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index d833653fdba65..49b57d4791de3 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -508,6 +508,7 @@ impl f128 { /// ``` #[inline] #[unstable(feature = "f128", issue = "116909")] + #[must_use] pub const fn classify(self) -> FpCategory { let bits = self.to_bits(); match (bits & Self::MAN_MASK, bits & Self::EXP_MASK) { @@ -608,6 +609,7 @@ impl f128 { #[inline] #[doc(alias = "nextUp")] #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] pub const fn next_up(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -662,6 +664,7 @@ impl f128 { #[inline] #[doc(alias = "nextDown")] #[unstable(feature = "f128", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] pub const fn next_down(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -907,6 +910,8 @@ impl f128 { #[doc(alias = "average")] #[unstable(feature = "f128", issue = "116909")] #[rustc_const_unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] pub const fn midpoint(self, other: f128) -> f128 { const HI: f128 = f128::MAX / 2.; diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index b0664abcdc739..e3f546926f48e 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -500,6 +500,7 @@ impl f16 { /// ``` #[inline] #[unstable(feature = "f16", issue = "116909")] + #[must_use] pub const fn classify(self) -> FpCategory { let b = self.to_bits(); match (b & Self::MAN_MASK, b & Self::EXP_MASK) { @@ -604,6 +605,7 @@ impl f16 { #[inline] #[doc(alias = "nextUp")] #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] pub const fn next_up(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -658,6 +660,7 @@ impl f16 { #[inline] #[doc(alias = "nextDown")] #[unstable(feature = "f16", issue = "116909")] + #[must_use = "method returns a new number and does not mutate the original value"] pub const fn next_down(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -901,6 +904,8 @@ impl f16 { #[doc(alias = "average")] #[unstable(feature = "f16", issue = "116909")] #[rustc_const_unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] pub const fn midpoint(self, other: f16) -> f16 { const HI: f16 = f16::MAX / 2.; diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index b85bd2b271588..ac4eedaefbdce 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -723,6 +723,7 @@ impl f32 { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_float_classify", since = "1.83.0")] + #[must_use] pub const fn classify(self) -> FpCategory { // We used to have complicated logic here that avoids the simple bit-based tests to work // around buggy codegen for x87 targets (see @@ -822,6 +823,7 @@ impl f32 { #[doc(alias = "nextUp")] #[stable(feature = "float_next_up_down", since = "1.86.0")] #[rustc_const_stable(feature = "float_next_up_down", since = "1.86.0")] + #[must_use = "method returns a new number and does not mutate the original value"] pub const fn next_up(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -873,6 +875,7 @@ impl f32 { #[doc(alias = "nextDown")] #[stable(feature = "float_next_up_down", since = "1.86.0")] #[rustc_const_stable(feature = "float_next_up_down", since = "1.86.0")] + #[must_use = "method returns a new number and does not mutate the original value"] pub const fn next_down(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -1089,6 +1092,8 @@ impl f32 { #[doc(alias = "average")] #[stable(feature = "num_midpoint", since = "1.85.0")] #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] pub const fn midpoint(self, other: f32) -> f32 { cfg_select! { // Allow faster implementation that have known good 64-bit float diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 3e7c1e792ace6..8682774aa3ae2 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -722,6 +722,7 @@ impl f64 { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_float_classify", since = "1.83.0")] + #[must_use] pub const fn classify(self) -> FpCategory { // We used to have complicated logic here that avoids the simple bit-based tests to work // around buggy codegen for x87 targets (see @@ -839,6 +840,7 @@ impl f64 { #[doc(alias = "nextUp")] #[stable(feature = "float_next_up_down", since = "1.86.0")] #[rustc_const_stable(feature = "float_next_up_down", since = "1.86.0")] + #[must_use = "method returns a new number and does not mutate the original value"] pub const fn next_up(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -890,6 +892,7 @@ impl f64 { #[doc(alias = "nextDown")] #[stable(feature = "float_next_up_down", since = "1.86.0")] #[rustc_const_stable(feature = "float_next_up_down", since = "1.86.0")] + #[must_use = "method returns a new number and does not mutate the original value"] pub const fn next_down(self) -> Self { // Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing // denormals to zero. This is in general unsound and unsupported, but here @@ -1107,6 +1110,8 @@ impl f64 { #[doc(alias = "average")] #[stable(feature = "num_midpoint", since = "1.85.0")] #[rustc_const_stable(feature = "num_midpoint", since = "1.85.0")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] pub const fn midpoint(self, other: f64) -> f64 { const HI: f64 = f64::MAX / 2.; diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 2c561b5b0529e..ed18f5a34fd28 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -12,7 +12,6 @@ #![feature(bstr)] #![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_internals)] -#![feature(char_max_len)] #![feature(clone_to_uninit)] #![feature(cmp_minmax)] #![feature(const_array)] diff --git a/library/coretests/tests/macros.rs b/library/coretests/tests/macros.rs index 50b5eb63e43a7..9f73ebd253c3b 100644 --- a/library/coretests/tests/macros.rs +++ b/library/coretests/tests/macros.rs @@ -1,5 +1,7 @@ #![allow(unused_must_use)] +use std::{assert_matches, debug_assert_matches}; + #[allow(dead_code)] trait Trait { fn blah(&self); @@ -219,3 +221,27 @@ fn _matches_does_not_trigger_non_exhaustive_omitted_patterns_lint(o: core::sync: // Ordering is a #[non_exhaustive] enum from a separate crate let _m = matches!(o, core::sync::atomic::Ordering::Relaxed); } + +struct MutRefWithDrop<'a>(&'a mut u32); + +// MutRefWithDrop needs to have a non-trivial drop to encounter potential lifetime issues if the +// macros don't introduce a temporary scope. +impl Drop for MutRefWithDrop<'_> { + fn drop(&mut self) { + *self.0 = u32::MAX; + } +} + +#[test] +fn temporary_scope_introduction() { + // Fails to compile if the macros don't introduce a temporary scope, since `&mut val` would + // create a second mutable borrow while `MutRefWithDrop` still holds a unique ref. + // See https://github.com/rust-lang/rust/issues/154406 for reference. + let mut val = 0; + + (assert_matches!(*MutRefWithDrop(&mut val).0, 0), std::mem::take(&mut val)); + (assert_matches!(*MutRefWithDrop(&mut val).0, 0, "msg"), std::mem::take(&mut val)); + + (debug_assert_matches!(*MutRefWithDrop(&mut val).0, 0), std::mem::take(&mut val)); + (debug_assert_matches!(*MutRefWithDrop(&mut val).0, 0, "msg"), std::mem::take(&mut val)); +} diff --git a/library/std/src/num/f128.rs b/library/std/src/num/f128.rs index d7c7a82674bf0..7b3221bc0fca2 100644 --- a/library/std/src/num/f128.rs +++ b/library/std/src/num/f128.rs @@ -643,6 +643,7 @@ impl f128 { #[doc(alias = "sincos")] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn sin_cos(self) -> (f128, f128) { (self.sin(), self.cos()) } diff --git a/library/std/src/num/f16.rs b/library/std/src/num/f16.rs index ef610eacb05d7..9ac0e11a4948f 100644 --- a/library/std/src/num/f16.rs +++ b/library/std/src/num/f16.rs @@ -608,6 +608,7 @@ impl f16 { #[doc(alias = "sincos")] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn sin_cos(self) -> (f16, f16) { (self.sin(), self.cos()) } diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index 771a0cae6dcd8..4f38d3be52f7d 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -907,6 +907,7 @@ impl f32 { #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn sin_cos(self) -> (f32, f32) { (self.sin(), self.cos()) } diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index 59ef39a382b27..8a771185f6fe4 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -907,6 +907,7 @@ impl f64 { #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[must_use = "this returns the result of the operation, without modifying the original"] pub fn sin_cos(self) -> (f64, f64) { (self.sin(), self.cos()) } diff --git a/library/test/src/console.rs b/library/test/src/console.rs index 13b2b3d502c81..b1c5404a7160c 100644 --- a/library/test/src/console.rs +++ b/library/test/src/console.rs @@ -16,7 +16,7 @@ use super::helpers::metrics::MetricMap; use super::options::{Options, OutputFormat}; use super::test_result::TestResult; use super::time::{TestExecTime, TestSuiteExecTime}; -use super::types::{NamePadding, TestDesc, TestDescAndFn}; +use super::types::{NamePadding, TestDesc, TestDescAndFn, TestList}; use super::{filter_tests, run_tests, term}; /// Generic wrapper over stdout. @@ -170,7 +170,7 @@ impl ConsoleTestState { } // List the tests to console, and optionally to logfile. Filters are honored. -pub(crate) fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Result<()> { +pub(crate) fn list_tests_console(opts: &TestOpts, tests: TestList) -> io::Result<()> { let output = match term::stdout() { None => OutputLocation::Raw(io::stdout().lock()), Some(t) => OutputLocation::Pretty(t), @@ -186,7 +186,7 @@ pub(crate) fn list_tests_console(opts: &TestOpts, tests: Vec) -> let mut st = ConsoleTestDiscoveryState::new(opts)?; out.write_discovery_start()?; - for test in filter_tests(opts, tests).into_iter() { + for test in filter_tests(opts, tests) { use crate::TestFn::*; let TestDescAndFn { desc, testfn } = test; @@ -307,8 +307,9 @@ pub(crate) fn get_formatter(opts: &TestOpts, max_name_len: usize) -> Box) -> io::Result { +pub fn run_tests_console(opts: &TestOpts, tests: TestList) -> io::Result { let max_name_len = tests + .tests .iter() .max_by_key(|t| len_if_padded(t)) .map(|t| t.desc.name.as_slice().len()) diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index f3dbd3d0556ab..791af5f8ee2a9 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -49,7 +49,7 @@ pub mod test { pub use crate::time::{TestExecTime, TestTimeOptions}; pub use crate::types::{ DynTestFn, DynTestName, StaticBenchFn, StaticTestFn, StaticTestName, TestDesc, - TestDescAndFn, TestId, TestName, TestType, + TestDescAndFn, TestId, TestList, TestListOrder, TestName, TestType, }; pub use crate::{assert_test_result, filter_tests, run_test, test_main, test_main_static}; } @@ -106,6 +106,16 @@ pub fn test_main_with_exit_callback( tests: Vec, options: Option, exit_callback: F, +) { + let tests = TestList::new(tests, TestListOrder::Unsorted); + test_main_inner(args, tests, options, exit_callback) +} + +fn test_main_inner( + args: &[String], + tests: TestList, + options: Option, + exit_callback: F, ) { let mut opts = match cli::parse_opts(args) { Some(Ok(o)) => o, @@ -180,7 +190,9 @@ pub fn test_main_with_exit_callback( pub fn test_main_static(tests: &[&TestDescAndFn]) { let args = env::args().collect::>(); let owned_tests: Vec<_> = tests.iter().map(make_owned_test).collect(); - test_main(&args, owned_tests, None) + // Tests are sorted by name at compile time by mk_tests_slice. + let tests = TestList::new(owned_tests, TestListOrder::Sorted); + test_main_inner(&args, tests, None, || {}) } /// A variant optimized for invocation with a static test vector. @@ -229,7 +241,9 @@ pub fn test_main_static_abort(tests: &[&TestDescAndFn]) { let args = env::args().collect::>(); let owned_tests: Vec<_> = tests.iter().map(make_owned_test).collect(); - test_main(&args, owned_tests, Some(Options::new().panic_abort(true))) + // Tests are sorted by name at compile time by mk_tests_slice. + let tests = TestList::new(owned_tests, TestListOrder::Sorted); + test_main_inner(&args, tests, Some(Options::new().panic_abort(true)), || {}) } /// Clones static values for putting into a dynamic vector, which test_main() @@ -298,7 +312,7 @@ impl FilteredTests { pub fn run_tests( opts: &TestOpts, - tests: Vec, + tests: TestList, mut notify_about_test_event: F, ) -> io::Result<()> where @@ -334,7 +348,7 @@ where timeout: Instant, } - let tests_len = tests.len(); + let tests_len = tests.tests.len(); let mut filtered = FilteredTests { tests: Vec::new(), benches: Vec::new(), next_id: 0 }; @@ -512,25 +526,48 @@ where Ok(()) } -pub fn filter_tests(opts: &TestOpts, tests: Vec) -> Vec { +pub fn filter_tests(opts: &TestOpts, tests: TestList) -> Vec { + let TestList { tests, order } = tests; let mut filtered = tests; - let matches_filter = |test: &TestDescAndFn, filter: &str| { - let test_name = test.desc.name.as_slice(); - - match opts.filter_exact { - true => test_name == filter, - false => test_name.contains(filter), - } - }; - // Remove tests that don't match the test filter + // Remove tests that don't match the test filter. if !opts.filters.is_empty() { - filtered.retain(|test| opts.filters.iter().any(|filter| matches_filter(test, filter))); + if opts.filter_exact && order == TestListOrder::Sorted { + // Let's say that `f` is the number of filters and `n` is the number + // of tests. + // + // The test array is sorted by name (guaranteed by the caller via + // TestListOrder::Sorted), so use binary search for O(f log n) + // exact-match lookups instead of an O(n) linear scan. + // + // This is important for Miri, where the interpreted execution makes + // the linear scan very expensive. + filtered = filter_exact_match(filtered, &opts.filters); + } else { + filtered.retain(|test| { + let test_name = test.desc.name.as_slice(); + opts.filters.iter().any(|filter| { + if opts.filter_exact { + test_name == filter.as_str() + } else { + test_name.contains(filter.as_str()) + } + }) + }); + } } // Skip tests that match any of the skip filters + // + // After exact positive filtering above, the filtered set is small, so a + // linear scan is acceptable even under Miri. if !opts.skip.is_empty() { - filtered.retain(|test| !opts.skip.iter().any(|sf| matches_filter(test, sf))); + filtered.retain(|test| { + let name = test.desc.name.as_slice(); + !opts.skip.iter().any(|sf| { + if opts.filter_exact { name == sf.as_str() } else { name.contains(sf.as_str()) } + }) + }); } // Excludes #[should_panic] tests @@ -553,6 +590,30 @@ pub fn filter_tests(opts: &TestOpts, tests: Vec) -> Vec, filters: &[String]) -> Vec { + // Binary search for each filter in the sorted test list. + let mut indexes: Vec = filters + .iter() + .filter_map(|f| tests.binary_search_by(|t| t.desc.name.as_slice().cmp(f.as_str())).ok()) + .collect(); + indexes.sort_unstable(); + indexes.dedup(); + + // Extract matching tests. Process indexes in descending order so that + // swap_remove (which replaces the removed element with the last) does not + // invalidate indexes we haven't visited yet. + let mut result = Vec::with_capacity(indexes.len()); + for &idx in indexes.iter().rev() { + result.push(tests.swap_remove(idx)); + } + // Reverse to restore the original sorted order, since we extracted the + // matching tests in descending index order. + result.reverse(); + result +} + pub fn convert_benchmarks_to_tests(tests: Vec) -> Vec { // convert benchmarks to tests, if we're not benchmarking them tests diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index d986bd74f772b..b25462cce1f99 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -477,7 +477,7 @@ fn filter_for_ignored_option() { opts.run_tests = true; opts.run_ignored = RunIgnored::Only; - let tests = one_ignored_one_unignored_test(); + let tests = TestList::new(one_ignored_one_unignored_test(), TestListOrder::Unsorted); let filtered = filter_tests(&opts, tests); assert_eq!(filtered.len(), 1); @@ -494,7 +494,7 @@ fn run_include_ignored_option() { opts.run_tests = true; opts.run_ignored = RunIgnored::Yes; - let tests = one_ignored_one_unignored_test(); + let tests = TestList::new(one_ignored_one_unignored_test(), TestListOrder::Unsorted); let filtered = filter_tests(&opts, tests); assert_eq!(filtered.len(), 2); @@ -527,7 +527,7 @@ fn exclude_should_panic_option() { testfn: DynTestFn(Box::new(move || Ok(()))), }); - let filtered = filter_tests(&opts, tests); + let filtered = filter_tests(&opts, TestList::new(tests, TestListOrder::Unsorted)); assert_eq!(filtered.len(), 2); assert!(filtered.iter().all(|test| test.desc.should_panic == ShouldPanic::No)); @@ -535,8 +535,8 @@ fn exclude_should_panic_option() { #[test] fn exact_filter_match() { - fn tests() -> Vec { - ["base", "base::test", "base::test1", "base::test2"] + fn tests() -> TestList { + let tests = ["base", "base::test", "base::test1", "base::test2"] .into_iter() .map(|name| TestDescAndFn { desc: TestDesc { @@ -555,7 +555,8 @@ fn exact_filter_match() { }, testfn: DynTestFn(Box::new(move || Ok(()))), }) - .collect() + .collect(); + TestList::new(tests, TestListOrder::Sorted) } let substr = @@ -908,7 +909,8 @@ fn test_dyn_bench_returning_err_fails_when_run_as_test() { } Ok(()) }; - run_tests(&TestOpts { run_tests: true, ..TestOpts::new() }, vec![desc], notify).unwrap(); + let tests = TestList::new(vec![desc], TestListOrder::Unsorted); + run_tests(&TestOpts { run_tests: true, ..TestOpts::new() }, tests, notify).unwrap(); let result = rx.recv().unwrap().result; assert_eq!(result, TrFailed); } diff --git a/library/test/src/types.rs b/library/test/src/types.rs index 802cab989c6a9..14c81bc2d1cf1 100644 --- a/library/test/src/types.rs +++ b/library/test/src/types.rs @@ -284,3 +284,30 @@ impl TestDescAndFn { } } } + +/// Whether a [`TestList`]'s tests are known to be sorted by name. +/// +/// When tests are sorted, `filter_tests` can use binary search for `--exact` +/// matches instead of a linear scan. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum TestListOrder { + /// Tests are sorted by name. This is guaranteed for tests generated by + /// `rustc --test` (see `mk_tests_slice` in + /// `compiler/rustc_builtin_macros/src/test_harness.rs`). + Sorted, + /// Test order is unknown; binary search must not be used. + Unsorted, +} + +/// A list of tests, tagged with whether they are sorted by name. +#[derive(Debug)] +pub struct TestList { + pub tests: Vec, + pub order: TestListOrder, +} + +impl TestList { + pub fn new(tests: Vec, order: TestListOrder) -> Self { + Self { tests, order } + } +} diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index f0f991ed0c909..7af10298470ee 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -655,16 +655,6 @@ deleted once compilation finishes. It takes one of the following values: * `y`, `yes`, `on`, `true` or no value: save temporary files. * `n`, `no`, `off` or `false`: delete temporary files (the default). -## soft-float - -This option controls whether `rustc` generates code that emulates floating -point instructions in software. It takes one of the following values: - -* `y`, `yes`, `on`, `true` or no value: use soft floats. -* `n`, `no`, `off` or `false`: use hardware floats (the default). - -This flag only works on `*eabihf` targets and **is unsound and deprecated**. - ## split-debuginfo This option controls the emission of "split debuginfo" for debug information diff --git a/src/tools/collect-license-metadata/Cargo.toml b/src/tools/collect-license-metadata/Cargo.toml index f84da24428155..ae41d2fbb009b 100644 --- a/src/tools/collect-license-metadata/Cargo.toml +++ b/src/tools/collect-license-metadata/Cargo.toml @@ -10,4 +10,3 @@ anyhow = "1.0.65" serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.85" similar = "2.7.0" -spdx-rs = "0.5.1" diff --git a/src/tools/collect-license-metadata/src/main.rs b/src/tools/collect-license-metadata/src/main.rs index 4e218ea59fda6..156871b1b3a80 100644 --- a/src/tools/collect-license-metadata/src/main.rs +++ b/src/tools/collect-license-metadata/src/main.rs @@ -1,6 +1,7 @@ mod licenses; mod path_tree; mod reuse; +mod spdx; use std::path::PathBuf; diff --git a/src/tools/collect-license-metadata/src/reuse.rs b/src/tools/collect-license-metadata/src/reuse.rs index dbe46781b7c5b..6bc41453a53ff 100644 --- a/src/tools/collect-license-metadata/src/reuse.rs +++ b/src/tools/collect-license-metadata/src/reuse.rs @@ -15,18 +15,15 @@ pub(crate) fn collect( let raw = &obtain_spdx_document(reuse_exe)?; println!("finished gathering the license information from REUSE in {:.2?}", start.elapsed()); - let document = spdx_rs::parsers::spdx_from_tag_value(&raw)?; + let files = crate::spdx::parse_tag_value(raw)?; let mut result = Vec::new(); - for file in document.file_information { - let concluded_license = file.concluded_license.expect("File should have licence info"); - let copyright_text = file.copyright_text.expect("File should have copyright text"); + for file in files { let license = interner.intern(License { - spdx: concluded_license.to_string(), - copyright: copyright_text.split('\n').map(|s| s.into()).collect(), + spdx: file.concluded_license, + copyright: file.copyright_text.split('\n').map(|s| s.into()).collect(), }); - - result.push((file.file_name.into(), license)); + result.push((file.name.into(), license)); } Ok(result) diff --git a/src/tools/collect-license-metadata/src/spdx/mod.rs b/src/tools/collect-license-metadata/src/spdx/mod.rs new file mode 100644 index 0000000000000..a94f2bcf51eca --- /dev/null +++ b/src/tools/collect-license-metadata/src/spdx/mod.rs @@ -0,0 +1,102 @@ +use anyhow::Error; + +/// A single file entry extracted from an SPDX tag-value document. +pub(crate) struct SpdxFileEntry { + pub(crate) name: String, + pub(crate) concluded_license: String, + pub(crate) copyright_text: String, +} + +/// Parses an SPDX tag-value document and extracts file information. +/// +/// This is a minimal parser that only extracts the fields we need +/// (FileName, LicenseConcluded, FileCopyrightText) rather than the full model. +/// The format is specified by the SPDX specification: +/// each line is a `Tag: Value` pair, +/// and multi-line values are wrapped in ``. +pub(crate) fn parse_tag_value(input: &str) -> Result, Error> { + let mut files = Vec::new(); + let mut current_name: Option = None; + let mut current_license: Option = None; + let mut current_copyright: Option = None; + + let mut lines = input.lines(); + while let Some(line) = lines.next() { + let Some((tag, value)) = line.split_once(": ") else { + continue; + }; + + let value = resolve_multiline_value(value, &mut lines)?; + + match tag { + "FileName" => { + // A new file section begins. Flush the previous one if present. + if let Some(name) = current_name.take() { + files.push(build_file_entry( + name, + current_license.take(), + current_copyright.take(), + )?); + } + current_name = Some(value); + current_license = None; + current_copyright = None; + } + "LicenseConcluded" => current_license = Some(value), + "FileCopyrightText" => current_copyright = Some(value), + _ => {} + } + } + + // Flush the last file section. + if let Some(name) = current_name { + files.push(build_file_entry(name, current_license, current_copyright)?); + } + + Ok(files) +} + +/// Resolves a tag value that might span multiple lines using ``. +fn resolve_multiline_value<'a>( + value: &str, + further_lines: &mut impl Iterator, +) -> Result { + let Some(start) = value.strip_prefix("") else { + return Ok(value.to_string()); + }; + + // The closing tag might be on the same line. + if let Some(content) = start.strip_suffix("") { + return Ok(content.to_string()); + } + + let mut text = start.to_string(); + for line in further_lines.by_ref() { + if let Some(rest) = line.strip_suffix("") { + text.push('\n'); + text.push_str(rest); + return Ok(text); + } + text.push('\n'); + text.push_str(line); + } + + anyhow::bail!("unexpected end of input inside block") +} + +fn build_file_entry( + name: String, + concluded_license: Option, + copyright_text: Option, +) -> Result { + Ok(SpdxFileEntry { + name, + concluded_license: concluded_license + .ok_or_else(|| anyhow::anyhow!("file missing LicenseConcluded"))?, + copyright_text: copyright_text + .ok_or_else(|| anyhow::anyhow!("file missing FileCopyrightText"))?, + }) +} + +#[cfg(test)] +mod tests; diff --git a/src/tools/collect-license-metadata/src/spdx/tests.rs b/src/tools/collect-license-metadata/src/spdx/tests.rs new file mode 100644 index 0000000000000..5b7cb411931dc --- /dev/null +++ b/src/tools/collect-license-metadata/src/spdx/tests.rs @@ -0,0 +1,134 @@ +use super::*; + +// Clause 8.1 ("File name field") specifies that each file section begins with +// a `FileName` tag whose value is a relative path prefixed with "./". +// Clause 8.5 ("Concluded license") and 8.8 ("Copyright text") give the +// corresponding per-file fields. +// +#[test] +fn single_file_entry() { + let input = "\ +FileName: ./package/foo.c +LicenseConcluded: LGPL-2.0-only +FileCopyrightText: Copyright 2008-2010 John Smith"; + + let files = parse_tag_value(input).unwrap(); + assert_eq!(files.len(), 1); + assert_eq!(files[0].name, "./package/foo.c"); + assert_eq!(files[0].concluded_license, "LGPL-2.0-only"); + assert_eq!(files[0].copyright_text, "Copyright 2008-2010 John Smith"); +} + +// Clause 8.5 shows compound SPDX licence expressions as valid values for +// `LicenseConcluded`, e.g. "(LGPL-2.0-only OR LicenseRef-2)". +// +#[test] +fn compound_license_expression() { + let input = "\ +FileName: ./src/lib.rs +LicenseConcluded: (LGPL-2.0-only OR LicenseRef-2) +FileCopyrightText: Copyright Example Company"; + + let files = parse_tag_value(input).unwrap(); + assert_eq!(files.len(), 1); + assert_eq!(files[0].concluded_license, "(LGPL-2.0-only OR LicenseRef-2)"); +} + +// Clause 8.8 shows the copyright text wrapped in a single-line +// ... block: e.g. +// `FileCopyrightText: Copyright 2008-2010 John Smith` +// +#[test] +fn single_line_text_block() { + let input = "\ +FileName: ./package/foo.c +LicenseConcluded: LGPL-2.0-only +FileCopyrightText: Copyright 2008-2010 John Smith"; + + let files = parse_tag_value(input).unwrap(); + assert_eq!(files.len(), 1); + assert_eq!(files[0].copyright_text, "Copyright 2008-2010 John Smith"); +} + +// Clause 6.10 ("Creator comment") demonstrates a multi-line ... block. +// +#[test] +fn multi_line_text_block() { + let input = "\ +FileName: ./package/foo.c +LicenseConcluded: MIT +FileCopyrightText: Copyright 2008-2010 John Smith +Copyright 2019 Jane Doe"; + + let files = parse_tag_value(input).unwrap(); + assert_eq!(files.len(), 1); + assert_eq!(files[0].copyright_text, "Copyright 2008-2010 John Smith\nCopyright 2019 Jane Doe"); +} + +// Clause 5 ("Composition of an SPDX document") states that a document may +// contain zero or many File Information sections. Each `FileName` tag starts +// a new section, so consecutive file blocks must be parsed independently. +// +#[test] +fn multiple_file_entries() { + let input = "\ +FileName: ./package/foo.c +LicenseConcluded: LGPL-2.0-only +FileCopyrightText: Copyright 2008-2010 John Smith +FileName: ./package/bar.c +LicenseConcluded: MIT +FileCopyrightText: Copyright Example Company"; + + let files = parse_tag_value(input).unwrap(); + assert_eq!(files.len(), 2); + + assert_eq!(files[0].name, "./package/foo.c"); + assert_eq!(files[0].concluded_license, "LGPL-2.0-only"); + assert_eq!(files[0].copyright_text, "Copyright 2008-2010 John Smith"); + + assert_eq!(files[1].name, "./package/bar.c"); + assert_eq!(files[1].concluded_license, "MIT"); + assert_eq!(files[1].copyright_text, "Copyright Example Company"); +} + +// A file section without a `LicenseConcluded` tag is malformed. +#[test] +fn missing_license_is_an_error() { + let input = "\ +FileName: ./package/foo.c +FileCopyrightText: Copyright 2008-2010 John Smith"; + + assert!(parse_tag_value(input).is_err()); +} + +// A file section without a `FileCopyrightText` tag is malformed. +#[test] +fn missing_copyright_is_an_error() { + let input = "\ +FileName: ./package/foo.c +LicenseConcluded: MIT"; + + assert!(parse_tag_value(input).is_err()); +} + +// A section with an unterminated block (no closing ) is malformed. +#[test] +fn unterminated_text_block_is_an_error() { + let input = "\ +FileName: ./package/foo.c +LicenseConcluded: MIT +FileCopyrightText: Copyright 2008-2010 John Smith"; + + assert!(parse_tag_value(input).is_err()); +} + +// A document with no `FileName` tags at all should produce an empty result. +#[test] +fn empty_document_returns_no_entries() { + let input = "\ +SPDXVersion: SPDX-2.3 +DataLicense: CC0-1.0"; + + let files = parse_tag_value(input).unwrap(); + assert!(files.is_empty()); +} diff --git a/src/tools/tidy/src/extra_checks/mod.rs b/src/tools/tidy/src/extra_checks/mod.rs index 4ba2da7b5d3f7..008cc0322534a 100644 --- a/src/tools/tidy/src/extra_checks/mod.rs +++ b/src/tools/tidy/src/extra_checks/mod.rs @@ -35,7 +35,7 @@ const MIN_PY_REV_STR: &str = "≥3.9"; /// Path to find the python executable within a virtual environment #[cfg(target_os = "windows")] -const REL_PY_PATH: &[&str] = &["Scripts", "python3.exe"]; +const REL_PY_PATH: &[&str] = &["Scripts", "python.exe"]; #[cfg(not(target_os = "windows"))] const REL_PY_PATH: &[&str] = &["bin", "python3"]; diff --git a/tests/codegen-llvm/issues/issue-114532.rs b/tests/codegen-llvm/issues/issue-114532.rs new file mode 100644 index 0000000000000..41d9effbe4c94 --- /dev/null +++ b/tests/codegen-llvm/issues/issue-114532.rs @@ -0,0 +1,34 @@ +//@ compile-flags: -Copt-level=3 +#![crate_type = "lib"] + +// Regression test for #114532. +// Dead code elimination used to fail when a Drop impl contained a panic +// and a potentially-panicking function was called after the value was created. + +struct Foo(bool); + +impl Drop for Foo { + fn drop(&mut self) { + if self.0 { + return; + } + panic!("dead"); + } +} + +// CHECK-LABEL: @foo( +// CHECK-NOT: panic +// CHECK-NOT: call void @{{.*}}panicking +// CHECK: call {{.*}} @unknown( +// CHECK-NEXT: ret void +#[no_mangle] +pub fn foo() { + let _a = Foo(true); + unsafe { + unknown(9); + } +} + +extern "Rust" { + fn unknown(x: i32) -> bool; +} diff --git a/tests/ui/c-variadic/roundtrip.rs b/tests/ui/c-variadic/roundtrip.rs new file mode 100644 index 0000000000000..b6a858c715dfd --- /dev/null +++ b/tests/ui/c-variadic/roundtrip.rs @@ -0,0 +1,78 @@ +//@ run-pass +//@ ignore-backends: gcc +#![feature(c_variadic, const_c_variadic, const_destruct, const_raw_ptr_comparison)] + +use std::ffi::*; + +// In rustc we implement `va_arg` for the callee reading from a VaList, but still rely on LLVM +// for exactly how to pass c-variadic arguments and for constructing the VaList. Here we test +// that the rustc implementation works with what LLVM gives us. + +#[allow(improper_ctypes_definitions)] +const unsafe extern "C" fn variadic(mut ap: ...) -> (T, T) { + let x = ap.arg::(); + // Intersperse a small type to test alignment logic. A `u32` (i.e. `c_uint`) is the smallest + // type that implements `VaArgSafe`: smaller types would automatically be promoted. + assert!(ap.arg::() == 0xAAAA_AAAA); + let y = ap.arg::(); + + (x, y) +} + +macro_rules! roundtrip { + ($ty:ty, $a:expr, $b:expr) => { + const { + let a: $ty = $a; + let b: $ty = $b; + let (x, y) = variadic::<$ty>(a, 0xAAAA_AAAAu32, b); + assert!(a == x); + assert!(b == y); + } + + let a: $ty = $a; + let b: $ty = $b; + assert_eq!(variadic::<$ty>(a, 0xAAAA_AAAAu32, b), (a, b)) + }; +} + +macro_rules! roundtrip_ptr { + ($ty:ty, $a:expr, $b:expr) => { + const { + let a: $ty = $a; + let b: $ty = $b; + let (x, y) = variadic::<$ty>(a, 0xAAAA_AAAAu32, b); + assert!(a.guaranteed_eq(x).unwrap()); + assert!(b.guaranteed_eq(y).unwrap()); + } + + let a: $ty = $a; + let b: $ty = $b; + assert_eq!(variadic::<$ty>(a, 0xAAAA_AAAAu32, b), (a, b)) + }; +} + +fn main() { + unsafe { + roundtrip!(i32, -1, -2); + roundtrip!(i64, -1, -2); + roundtrip!(isize, -1, -2); + roundtrip!(c_int, -1, -2); + roundtrip!(c_long, -1, -2); + roundtrip!(c_longlong, -1, -2); + + roundtrip!(u32, 1, 2); + roundtrip!(u64, 1, 2); + roundtrip!(usize, 1, 2); + roundtrip!(c_uint, 1, 2); + roundtrip!(c_ulong, 1, 2); + roundtrip!(c_ulonglong, 1, 2); + + roundtrip!(f64, 3.14, 6.28); + roundtrip!(c_double, 3.14, 6.28); + + static mut A: u32 = 1u32; + static mut B: u32 = 2u32; + roundtrip_ptr!(*const u32, &raw const A, &raw const B); + roundtrip_ptr!(*mut u32, &raw mut A, &raw mut B); + } +} diff --git a/tests/ui/diagnostic_namespace/duplicate_coalescing.both.stderr b/tests/ui/diagnostic_namespace/duplicate_coalescing.both.stderr new file mode 100644 index 0000000000000..e1ccd6f078ef9 --- /dev/null +++ b/tests/ui/diagnostic_namespace/duplicate_coalescing.both.stderr @@ -0,0 +1,25 @@ +error[E0277]: this message + --> $DIR/duplicate_coalescing.rs:15:17 + | +LL | takes_trait(()); + | ----------- ^^ this label + | | + | required by a bound introduced by this call + | + = help: the trait `Trait` is not implemented for `()` + = note: a note + = note: another note +help: this trait has no implementations, consider adding one + --> $DIR/duplicate_coalescing.rs:10:1 + | +LL | trait Trait {} + | ^^^^^^^^^^^ +note: required by a bound in `takes_trait` + --> $DIR/duplicate_coalescing.rs:12:24 + | +LL | fn takes_trait(_: impl Trait) {} + | ^^^^^ required by this bound in `takes_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/duplicate_coalescing.label.stderr b/tests/ui/diagnostic_namespace/duplicate_coalescing.label.stderr new file mode 100644 index 0000000000000..85c8e0a6984f1 --- /dev/null +++ b/tests/ui/diagnostic_namespace/duplicate_coalescing.label.stderr @@ -0,0 +1,24 @@ +error[E0277]: this message + --> $DIR/duplicate_coalescing.rs:15:17 + | +LL | takes_trait(()); + | ----------- ^^ this label + | | + | required by a bound introduced by this call + | + = help: the trait `Trait` is not implemented for `()` + = note: a note +help: this trait has no implementations, consider adding one + --> $DIR/duplicate_coalescing.rs:10:1 + | +LL | trait Trait {} + | ^^^^^^^^^^^ +note: required by a bound in `takes_trait` + --> $DIR/duplicate_coalescing.rs:12:24 + | +LL | fn takes_trait(_: impl Trait) {} + | ^^^^^ required by this bound in `takes_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/duplicate_coalescing.note.stderr b/tests/ui/diagnostic_namespace/duplicate_coalescing.note.stderr new file mode 100644 index 0000000000000..69701d43f2499 --- /dev/null +++ b/tests/ui/diagnostic_namespace/duplicate_coalescing.note.stderr @@ -0,0 +1,24 @@ +error[E0277]: this message + --> $DIR/duplicate_coalescing.rs:15:17 + | +LL | takes_trait(()); + | ----------- ^^ the trait `Trait` is not implemented for `()` + | | + | required by a bound introduced by this call + | + = note: a note + = note: another note +help: this trait has no implementations, consider adding one + --> $DIR/duplicate_coalescing.rs:10:1 + | +LL | trait Trait {} + | ^^^^^^^^^^^ +note: required by a bound in `takes_trait` + --> $DIR/duplicate_coalescing.rs:12:24 + | +LL | fn takes_trait(_: impl Trait) {} + | ^^^^^ required by this bound in `takes_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/duplicate_coalescing.rs b/tests/ui/diagnostic_namespace/duplicate_coalescing.rs new file mode 100644 index 0000000000000..cf610841dc8e1 --- /dev/null +++ b/tests/ui/diagnostic_namespace/duplicate_coalescing.rs @@ -0,0 +1,22 @@ +//@ revisions: label note both +//@[both] compile-flags: --cfg label --cfg note +//@ dont-require-annotations: NOTE + +//! Example and test of multiple diagnostic attributes coalescing together. + +#[diagnostic::on_unimplemented(message = "this message", note = "a note")] +#[cfg_attr(label, diagnostic::on_unimplemented(label = "this label"))] +#[cfg_attr(note, diagnostic::on_unimplemented(note = "another note"))] +trait Trait {} + +fn takes_trait(_: impl Trait) {} + +fn main() { + takes_trait(()); + //~^ERROR this message + //[label]~|NOTE this label + //[both]~|NOTE this label + //~|NOTE a note + //[note]~|NOTE another note + //[both]~|NOTE another note +} diff --git a/triagebot.toml b/triagebot.toml index 953ef08a9cc8a..a4155914fb02b 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1633,6 +1633,18 @@ dep-bumps = [ "/src/llvm-project" = ["@cuviper"] "/src/rustdoc-json-types" = ["rustdoc"] "/src/stage0" = ["bootstrap"] +"/tests/assembly-llvm" = ["compiler"] +"/tests/auxiliary" = ["compiler"] +"/tests/codegen-llvm" = ["compiler"] +"/tests/codegen-units" = ["compiler"] +"/tests/COMPILER_TESTS.md" = ["compiler"] +"/tests/coverage-run-rustdoc" = ["compiler"] +"/tests/coverage" = ["compiler"] +"/tests/crashes" = ["compiler"] +"/tests/debuginfo" = ["compiler"] +"/tests/incremental" = ["compiler"] +"/tests/mir-opt" = ["compiler"] +"/tests/pretty" = ["compiler"] "/tests/run-make" = ["compiler"] "/tests/run-make-cargo" = ["compiler"] "/tests/rustdoc-html" = ["rustdoc"] @@ -1642,6 +1654,7 @@ dep-bumps = [ "/tests/rustdoc-json" = ["@aDotInTheVoid"] "/tests/rustdoc-ui" = ["rustdoc"] "/tests/ui" = ["compiler"] +"/tests/ui-fulldeps" = ["compiler"] "/src/tools/build-manifest" = ["bootstrap"] "/src/tools/cargo" = ["@ehuss"] "/src/tools/compiletest" = ["bootstrap", "@wesleywiser", "@oli-obk", "@jieyouxu"]