Auto merge of #155519 - JonathanBrouwer:rollup-P17uwTS, r=JonathanBrouwer

Rollup of 12 pull requests

Successful merges:

 - rust-lang/rust#155370 (Add regression test for dead code elimination with drop + panic)
 - rust-lang/rust#154823 (Replace the spdx-rs dependency with a minimal in-tree SPDX tag-value parser)
 - rust-lang/rust#155294 (Add test for coalescing of diagnostic attribute duplicates)
 - rust-lang/rust#155352 (triagebot.toml: Sync `assign.owners` with `autolabel."T-compiler"`)
 - rust-lang/rust#155431 (Add temporary scope to assert_matches)
 - rust-lang/rust#153873 (deprecate `std::char` constants and functions)
 - rust-lang/rust#154865 (libtest: use binary search for --exact test filtering)
 - rust-lang/rust#154979 (add #[must_use] macros for floats)
 - rust-lang/rust#155486 (c-variadic: add roundtrip test)
 - rust-lang/rust#155504 (Remove `AttributeLintKind` variants - part 2)
 - rust-lang/rust#155510 (Update Tidy python executable path)
 - rust-lang/rust#155514 (codegen-options docs: remove -Csoft-float)
This commit is contained in:
bors
2026-04-19 16:04:18 +00:00
41 changed files with 897 additions and 448 deletions
+41 -108
View File
@@ -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]]
@@ -1036,7 +1034,7 @@ dependencies = [
"proc-macro2",
"quote",
"scratch",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -1050,7 +1048,7 @@ dependencies = [
"indexmap",
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -1068,7 +1066,7 @@ dependencies = [
"indexmap",
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -1092,7 +1090,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -1103,7 +1101,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -1124,7 +1122,7 @@ checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -1145,7 +1143,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -1155,7 +1153,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
dependencies = [
"derive_builder_core",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -1167,7 +1165,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -1242,7 +1240,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -1663,12 +1661,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"
@@ -2069,7 +2061,7 @@ checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -2356,7 +2348,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -2501,7 +2493,7 @@ checksum = "4568f25ccbd45ab5d5603dc34318c1ec56b117531781260002151b8530a9f931"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -2875,7 +2867,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -3085,7 +3077,7 @@ checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -3276,7 +3268,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -3369,7 +3361,7 @@ checksum = "8100bb34c0a1d0f907143db3149e6b4eea3c33b9ee8b189720168e818303986f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -4085,7 +4077,7 @@ version = "0.0.0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -4231,7 +4223,7 @@ dependencies = [
"fluent-syntax",
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
"synstructure",
]
@@ -4812,7 +4804,7 @@ version = "0.0.0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
"synstructure",
]
@@ -4912,7 +4904,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -5029,7 +5021,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -5115,7 +5107,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -5126,7 +5118,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -5259,35 +5251,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"
@@ -5353,36 +5316,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"
@@ -5402,7 +5335,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -5539,7 +5472,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -5550,7 +5483,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -5792,7 +5725,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -5988,7 +5921,7 @@ checksum = "a1249a628de3ad34b821ecb1001355bca3940bcb2f88558f1a8bd82e977f75b5"
dependencies = [
"proc-macro-hack",
"quote",
"syn 2.0.110",
"syn",
"unic-langid-impl",
]
@@ -6232,7 +6165,7 @@ dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
"wasm-bindgen-shared",
]
@@ -6489,7 +6422,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -6500,7 +6433,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -6796,7 +6729,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
"synstructure",
]
@@ -6817,7 +6750,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -6837,7 +6770,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
"synstructure",
]
@@ -6872,7 +6805,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.110",
"syn",
]
[[package]]
@@ -13,6 +13,7 @@
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 @@ fn add_alias<S: Stage>(
}
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 @@ fn parse_auto_cfg<S: Stage>(
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 @@ fn parse_auto_cfg<S: Stage>(
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 @@ fn accept_single_doc_attr<S: Stage>(
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,
);
+15 -5
View File
@@ -897,11 +897,18 @@ pub(crate) fn expected_specific_argument_strings(
}
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 @@ pub(crate) fn warn_ill_formed_attribute_input_with_help(
) {
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,
);
}
+112 -1
View File
@@ -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<crate::session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion>,
}
#[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<IllFormedAttributeInputHelp>,
}
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<Span>,
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;
@@ -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 @@ pub(crate) fn check_target(
};
let attr_span = cx.attr_span;
cx.emit_lint(
cx.emit_dyn_lint(
lint,
AttributeLintKind::InvalidTarget {
move |dcx, level| {
InvalidTargetLint {
name: name.to_string(),
target: target.plural_name(),
only: if only { "only " } else { "" },
applied,
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 @@ pub(crate) fn check_crate_level(target: Target, cx: &mut AcceptContext<'_, 'sess
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
@@ -8,10 +8,9 @@
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 {
@@ -370,6 +370,8 @@ fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> Box<ast::Expr> {
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(
+14 -7
View File
@@ -3,7 +3,8 @@
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 @@ fn codegen_intrinsic_call(
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 @@ fn codegen_intrinsic_call(
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 {
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 => {
+1 -53
View File
@@ -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 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
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)
+1 -95
View File
@@ -6,7 +6,7 @@
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) enum CargoHelp {
}
}
// 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<IllFormedAttributeInputHelp>,
}
#[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 @@ fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
}
}
#[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<Span>,
pub target: &'static str,
}
#[derive(Diagnostic)]
#[diag("malformed `doc` attribute input")]
#[warning(
@@ -3386,17 +3303,6 @@ pub(crate) struct InvalidAttrStyle {
)]
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;
+14 -80
View File
@@ -654,98 +654,32 @@ pub enum DeprecatedSinceKind {
#[derive(Debug)]
pub enum AttributeLintKind {
IllFormedAttributeInput {
suggestions: Vec<String>,
docs: Option<&'static str>,
help: Option<String>,
},
EmptyAttribute {
first_span: Span,
attr_path: String,
valid_without_list: bool,
},
InvalidTarget {
name: String,
target: &'static str,
applied: Vec<String>,
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<Symbol>,
},
CrateTypeUnknown { span: Span, suggested: Option<Symbol> },
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,
}
+13 -10
View File
@@ -93,31 +93,31 @@
/// 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<I: IntoIterator<Item = u16>>(iter: I) -> DecodeUtf16<I::IntoIter> {
self::decode::decode_utf16(iter)
@@ -126,6 +126,7 @@ pub fn decode_utf16<I: IntoIterator<Item = u16>>(iter: I) -> DecodeUtf16<I::Into
/// Converts a `u32` to a `char`. Use [`char::from_u32`] 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_u32` method on `char`")]
#[must_use]
#[inline]
pub const fn from_u32(i: u32) -> Option<char> {
@@ -136,6 +137,7 @@ pub const fn from_u32(i: u32) -> Option<char> {
/// 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 fn from_u32(i: u32) -> Option<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<char> {
+4 -4
View File
@@ -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 @@ macro_rules! assert_ne {
);
}
}
},
($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 @@ macro_rules! assert_ne {
);
}
}
},
}},
}
/// Selects code at compile-time based on `cfg` predicates.
+5
View File
@@ -508,6 +508,7 @@ pub const fn is_normal(self) -> bool {
/// ```
#[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 @@ pub const fn is_sign_negative(self) -> bool {
#[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 @@ pub const fn next_up(self) -> Self {
#[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 @@ pub const fn minimum(self, other: f128) -> 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.;
+5
View File
@@ -500,6 +500,7 @@ pub const fn is_normal(self) -> bool {
/// ```
#[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 @@ pub const fn is_sign_negative(self) -> bool {
#[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 @@ pub const fn next_up(self) -> Self {
#[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 @@ pub const fn minimum(self, other: f16) -> 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.;
+5
View File
@@ -723,6 +723,7 @@ pub const fn is_normal(self) -> bool {
/// ```
#[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 @@ pub const fn is_sign_negative(self) -> bool {
#[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 @@ pub const fn next_up(self) -> Self {
#[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 @@ pub const fn minimum(self, other: f32) -> 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
+5
View File
@@ -722,6 +722,7 @@ pub const fn is_normal(self) -> bool {
/// ```
#[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 @@ pub fn is_negative(self) -> bool {
#[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 @@ pub const fn next_up(self) -> Self {
#[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 @@ pub const fn minimum(self, other: f64) -> 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.;
-1
View File
@@ -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)]
+26
View File
@@ -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));
}
+1
View File
@@ -643,6 +643,7 @@ pub fn atan2(self, other: f128) -> 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())
}
+1
View File
@@ -608,6 +608,7 @@ pub fn atan2(self, other: f16) -> 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())
}
+1
View File
@@ -907,6 +907,7 @@ pub fn atan2(self, other: f32) -> 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())
}
+1
View File
@@ -907,6 +907,7 @@ pub fn atan2(self, other: f64) -> 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())
}
+5 -4
View File
@@ -16,7 +16,7 @@
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 @@ fn current_test_count(&self) -> usize {
}
// List the tests to console, and optionally to logfile. Filters are honored.
pub(crate) fn list_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> 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<TestDescAndFn>) ->
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<dyn Out
/// A simple console test runner.
/// Runs provided tests reporting process and results to the stdout.
pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<bool> {
pub fn run_tests_console(opts: &TestOpts, tests: TestList) -> io::Result<bool> {
let max_name_len = tests
.tests
.iter()
.max_by_key(|t| len_if_padded(t))
.map(|t| t.desc.name.as_slice().len())
+78 -17
View File
@@ -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<F: FnOnce()>(
tests: Vec<TestDescAndFn>,
options: Option<Options>,
exit_callback: F,
) {
let tests = TestList::new(tests, TestListOrder::Unsorted);
test_main_inner(args, tests, options, exit_callback)
}
fn test_main_inner<F: FnOnce()>(
args: &[String],
tests: TestList,
options: Option<Options>,
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<F: FnOnce()>(
pub fn test_main_static(tests: &[&TestDescAndFn]) {
let args = env::args().collect::<Vec<_>>();
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::<Vec<_>>();
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 @@ fn total_len(&self) -> usize {
pub fn run_tests<F>(
opts: &TestOpts,
tests: Vec<TestDescAndFn>,
tests: TestList,
mut notify_about_test_event: F,
) -> io::Result<()>
where
@@ -334,7 +348,7 @@ struct TimeoutEntry {
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 @@ fn calc_timeout(timeout_queue: &VecDeque<TimeoutEntry>) -> Option<Duration> {
Ok(())
}
pub fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescAndFn> {
pub fn filter_tests(opts: &TestOpts, tests: TestList) -> Vec<TestDescAndFn> {
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<TestDescAndFn>) -> Vec<TestDescA
filtered
}
/// Extract tests whose names exactly match one of the given `filters`, using
/// binary search on the (assumed sorted) test list.
fn filter_exact_match(mut tests: Vec<TestDescAndFn>, filters: &[String]) -> Vec<TestDescAndFn> {
// Binary search for each filter in the sorted test list.
let mut indexes: Vec<usize> = 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<TestDescAndFn>) -> Vec<TestDescAndFn> {
// convert benchmarks to tests, if we're not benchmarking them
tests
+9 -7
View File
@@ -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<TestDescAndFn> {
["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 tests() -> Vec<TestDescAndFn> {
},
testfn: DynTestFn(Box::new(move || Ok(()))),
})
.collect()
.collect();
TestList::new(tests, TestListOrder::Sorted)
}
let substr =
@@ -908,7 +909,8 @@ fn f(_: &mut Bencher) -> Result<(), String> {
}
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);
}
+27
View File
@@ -284,3 +284,30 @@ pub const fn new_doctest(
}
}
}
/// 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<TestDescAndFn>,
pub order: TestListOrder,
}
impl TestList {
pub fn new(tests: Vec<TestDescAndFn>, order: TestListOrder) -> Self {
Self { tests, order }
}
}
@@ -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
@@ -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"
@@ -1,6 +1,7 @@
mod licenses;
mod path_tree;
mod reuse;
mod spdx;
use std::path::PathBuf;
@@ -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)
@@ -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 `<text>…</text>`.
pub(crate) fn parse_tag_value(input: &str) -> Result<Vec<SpdxFileEntry>, Error> {
let mut files = Vec::new();
let mut current_name: Option<String> = None;
let mut current_license: Option<String> = None;
let mut current_copyright: Option<String> = 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 `<text>…</text>`.
fn resolve_multiline_value<'a>(
value: &str,
further_lines: &mut impl Iterator<Item = &'a str>,
) -> Result<String, Error> {
let Some(start) = value.strip_prefix("<text>") else {
return Ok(value.to_string());
};
// The closing tag might be on the same line.
if let Some(content) = start.strip_suffix("</text>") {
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>") {
text.push('\n');
text.push_str(rest);
return Ok(text);
}
text.push('\n');
text.push_str(line);
}
anyhow::bail!("unexpected end of input inside <text> block")
}
fn build_file_entry(
name: String,
concluded_license: Option<String>,
copyright_text: Option<String>,
) -> Result<SpdxFileEntry, Error> {
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;
@@ -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.
// <https://spdx.github.io/spdx-spec/v2.3/file-information/>
#[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)".
// <https://spdx.github.io/spdx-spec/v2.3/file-information/>
#[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
// <text>...</text> block: e.g.
// `FileCopyrightText: <text>Copyright 2008-2010 John Smith</text>`
// <https://spdx.github.io/spdx-spec/v2.3/file-information/>
#[test]
fn single_line_text_block() {
let input = "\
FileName: ./package/foo.c
LicenseConcluded: LGPL-2.0-only
FileCopyrightText: <text>Copyright 2008-2010 John Smith</text>";
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 <text>...</text> block.
// <https://spdx.github.io/spdx-spec/v2.3/document-creation-information/>
#[test]
fn multi_line_text_block() {
let input = "\
FileName: ./package/foo.c
LicenseConcluded: MIT
FileCopyrightText: <text>Copyright 2008-2010 John Smith
Copyright 2019 Jane Doe</text>";
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.
// <https://spdx.github.io/spdx-spec/v2.3/composition-of-an-SPDX-document/>
#[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 <text> block (no closing </text>) is malformed.
#[test]
fn unterminated_text_block_is_an_error() {
let input = "\
FileName: ./package/foo.c
LicenseConcluded: MIT
FileCopyrightText: <text>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());
}
+1 -1
View File
@@ -35,7 +35,7 @@
/// 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"];
+34
View File
@@ -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;
}
+78
View File
@@ -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<T: VaArgSafe>(mut ap: ...) -> (T, T) {
let x = ap.arg::<T>();
// 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::<u32>() == 0xAAAA_AAAA);
let y = ap.arg::<T>();
(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);
}
}
@@ -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`.
@@ -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`.
@@ -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`.
@@ -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
}
+13
View File
@@ -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"]