Auto merge of #142901 - matthiaskrgr:rollup-topt4p6, r=matthiaskrgr

Rollup of 5 pull requests

Successful merges:

 - rust-lang/rust#141597 (Document subdirectories of UI tests with README files)
 - rust-lang/rust#142823 (Port `#[no_mangle]` to new attribute parsing infrastructure)
 - rust-lang/rust#142828 (1.88.0 release notes)
 - rust-lang/rust#142854 (centralize `-Zmin-function-alignment` logic)
 - rust-lang/rust#142875 (Check rustdoc-json-types FORMAT_VERSION is correctly updated)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-06-23 06:37:23 +00:00
commit 22be76b7e2
24 changed files with 1854 additions and 97 deletions

View file

@ -1,3 +1,103 @@
Version 1.88.0 (2025-06-26)
==========================
<a id="1.88.0-Language"></a>
Language
--------
- [Stabilize `#![feature(let_chains)]` in the 2024 edition.](https://github.com/rust-lang/rust/pull/132833)
This feature allows `&&`-chaining `let` statements inside `if` and `while`, allowing intermixture with boolean expressions. The patterns inside the `let` sub-expressions can be irrefutable or refutable.
- [Stabilize `#![feature(naked_functions)]`.](https://github.com/rust-lang/rust/pull/134213)
Naked functions allow writing functions with no compiler-generated epilogue and prologue, allowing full control over the generated assembly for a particular function.
- [Stabilize `#![feature(cfg_boolean_literals)]`.](https://github.com/rust-lang/rust/pull/138632)
This allows using boolean literals as `cfg` predicates, e.g. `#[cfg(true)]` and `#[cfg(false)]`.
- [Fully de-stabilize the `#[bench]` attribute](https://github.com/rust-lang/rust/pull/134273). Usage of `#[bench]` without `#![feature(custom_test_frameworks)]` already triggered a deny-by-default future-incompatibility lint since Rust 1.77, but will now become a hard error.
- [Add warn-by-default `dangerous_implicit_autorefs` lint against implicit autoref of raw pointer dereference.](https://github.com/rust-lang/rust/pull/123239)
The lint [will be bumped to deny-by-default](https://github.com/rust-lang/rust/pull/141661) in the next version of Rust.
- [Add `invalid_null_arguments` lint to prevent invalid usage of null pointers.](https://github.com/rust-lang/rust/pull/119220)
This lint is uplifted from `clippy::invalid_null_ptr_usage`.
- [Change trait impl candidate preference for builtin impls and trivial where-clauses.](https://github.com/rust-lang/rust/pull/138176)
- [Check types of generic const parameter defaults](https://github.com/rust-lang/rust/pull/139646)
<a id="1.88.0-Compiler"></a>
Compiler
--------
- [Stabilize `-Cdwarf-version` for selecting the version of DWARF debug information to generate.](https://github.com/rust-lang/rust/pull/136926)
<a id="1.88.0-Platform-Support"></a>
Platform Support
----------------
- [Demote `i686-pc-windows-gnu` to Tier 2.](https://blog.rust-lang.org/2025/05/26/demoting-i686-pc-windows-gnu/)
Refer to Rust's [platform support page][platform-support-doc]
for more information on Rust's tiered platform support.
[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html
<a id="1.88.0-Libraries"></a>
Libraries
---------
- [Remove backticks from `#[should_panic]` test failure message.](https://github.com/rust-lang/rust/pull/136160)
- [Guarantee that `[T; N]::from_fn` is generated in order of increasing indices.](https://github.com/rust-lang/rust/pull/139099), for those passing it a stateful closure.
- [The libtest flag `--nocapture` is deprecated in favor of the more consistent `--no-capture` flag.](https://github.com/rust-lang/rust/pull/139224)
- [Guarantee that `{float}::NAN` is a quiet NaN.](https://github.com/rust-lang/rust/pull/139483)
<a id="1.88.0-Stabilized-APIs"></a>
Stabilized APIs
---------------
- [`Cell::update`](https://doc.rust-lang.org/stable/std/cell/struct.Cell.html#method.update)
- [`impl Default for *const T`](https://doc.rust-lang.org/nightly/std/primitive.pointer.html#impl-Default-for-*const+T)
- [`impl Default for *mut T`](https://doc.rust-lang.org/nightly/std/primitive.pointer.html#impl-Default-for-*mut+T)
- [`HashMap::extract_if`](https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html#method.extract_if)
- [`HashSet::extract_if`](https://doc.rust-lang.org/stable/std/collections/struct.HashSet.html#method.extract_if)
- [`proc_macro::Span::line`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.line)
- [`proc_macro::Span::column`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.column)
- [`proc_macro::Span::start`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.start)
- [`proc_macro::Span::end`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.end)
- [`proc_macro::Span::file`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.file)
- [`proc_macro::Span::local_file`](https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.local_file)
These previously stable APIs are now stable in const contexts:
- [`NonNull<T>::replace`](https://doc.rust-lang.org/stable/std/ptr/struct.NonNull.html#method.replace)
- [`<*mut T>::replace`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.replace)
- [`std::ptr::swap_nonoverlapping`](https://github.com/rust-lang/rust/pull/137280)
- [`Cell::{replace, get, get_mut, from_mut, as_slice_of_cells}`](https://github.com/rust-lang/rust/pull/137928)
<a id="1.88.0-Cargo"></a>
Cargo
-----
- [Stabilize automatic garbage collection.](https://github.com/rust-lang/cargo/pull/14287/)
- [use `zlib-rs` for gzip compression in rust code](https://github.com/rust-lang/cargo/pull/15417/)
<a id="1.88.0-Rustdoc"></a>
Rustdoc
-----
- [Doctests can be ignored based on target names using `ignore-*` attributes.](https://github.com/rust-lang/rust/pull/137096)
- [Stabilize the `--test-runtool` and `--test-runtool-arg` CLI options to specify a program (like qemu) and its arguments to run a doctest.](https://github.com/rust-lang/rust/pull/137096)
<a id="1.88.0-Compatibility-Notes"></a>
Compatibility Notes
-------------------
- [Finish changing the internal representation of pasted tokens](https://github.com/rust-lang/rust/pull/124141). Certain invalid declarative macros that were previously accepted in obscure circumstances are now correctly rejected by the compiler. Use of a `tt` fragment specifier can often fix these macros.
- [Fully de-stabilize the `#[bench]` attribute](https://github.com/rust-lang/rust/pull/134273). Usage of `#[bench]` without `#![feature(custom_test_frameworks)]` already triggered a deny-by-default future-incompatibility lint since Rust 1.77, but will now become a hard error.
- [Fix borrow checking some always-true patterns.](https://github.com/rust-lang/rust/pull/139042)
The borrow checker was overly permissive in some cases, allowing programs that shouldn't have compiled.
- [Update the minimum external LLVM to 19.](https://github.com/rust-lang/rust/pull/139275)
- [Make it a hard error to use a vector type with a non-Rust ABI without enabling the required target feature.](https://github.com/rust-lang/rust/pull/139309)
Version 1.87.0 (2025-05-15)
==========================

View file

@ -244,6 +244,9 @@ pub enum AttributeKind {
reason: Option<Symbol>,
},
/// Represents `#[no_mangle]`
NoMangle(Span),
/// Represents `#[optimize(size|speed)]`
Optimize(OptimizeAttr, Span),

View file

@ -56,3 +56,21 @@ impl<S: Stage> SingleAttributeParser<S> for ColdParser {
Some(AttributeKind::Cold(cx.attr_span))
}
}
pub(crate) struct NoMangleParser;
impl<S: Stage> SingleAttributeParser<S> for NoMangleParser {
const PATH: &[rustc_span::Symbol] = &[sym::no_mangle];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const TEMPLATE: AttributeTemplate = template!(Word);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
if !args.no_args() {
cx.expected_no_args(args.span().unwrap_or(cx.attr_span));
return None;
};
Some(AttributeKind::NoMangle(cx.attr_span))
}
}

View file

@ -15,7 +15,7 @@ use rustc_session::Session;
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
use crate::attributes::codegen_attrs::{ColdParser, OptimizeParser};
use crate::attributes::codegen_attrs::{ColdParser, NoMangleParser, OptimizeParser};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
@ -114,6 +114,7 @@ attribute_parsers!(
Single<InlineParser>,
Single<MayDangleParser>,
Single<MustUseParser>,
Single<NoMangleParser>,
Single<OptimizeParser>,
Single<PubTransparentParser>,
Single<RustcForceInlineParser>,

View file

@ -491,11 +491,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
let allocated_pointer = AttributeKind::AllocatedPointer.create_attr(cx.llcx);
attributes::apply_to_llfn(llfn, AttributePlace::Argument(0), &[allocated_pointer]);
}
// function alignment can be set globally with the `-Zmin-function-alignment=<n>` flag;
// the alignment from a `#[repr(align(<n>))]` is used if it specifies a higher alignment.
if let Some(align) =
Ord::max(cx.tcx.sess.opts.unstable_opts.min_function_alignment, codegen_fn_attrs.alignment)
{
if let Some(align) = codegen_fn_attrs.alignment {
llvm::set_alignment(llfn, align);
}
if let Some(backchain) = backchain_attr(cx) {

View file

@ -221,6 +221,8 @@ codegen_ssa_multiple_main_functions = entry symbol `main` declared multiple time
codegen_ssa_no_field = no field `{$name}`
codegen_ssa_no_mangle_nameless = `#[no_mangle]` cannot be used on {$definition} as it has no name
codegen_ssa_no_module_named =
no module named `{$user_path}` (mangled: {$cgu_name}). available modules: {$cgu_names}

View file

@ -23,6 +23,7 @@ use rustc_span::{Ident, Span, sym};
use rustc_target::spec::SanitizerSet;
use crate::errors;
use crate::errors::NoMangleNameless;
use crate::target_features::{
check_target_feature_trait_unsafe, check_tied_features, from_target_feature_attr,
};
@ -87,7 +88,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
let mut link_ordinal_span = None;
let mut no_sanitize_span = None;
let mut mixed_export_name_no_mangle_lint_state = MixedExportNameAndNoMangleState::default();
let mut no_mangle_span = None;
for attr in attrs.iter() {
// In some cases, attribute are only valid on functions, but it's the `check_attr`
@ -122,10 +122,35 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
}
AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD,
AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align),
AttributeKind::NoMangle(attr_span) => {
if tcx.opt_item_name(did.to_def_id()).is_some() {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
mixed_export_name_no_mangle_lint_state.track_no_mangle(
*attr_span,
tcx.local_def_id_to_hir_id(did),
attr,
);
} else {
tcx.dcx().emit_err(NoMangleNameless {
span: *attr_span,
definition: format!(
"{} {}",
tcx.def_descr_article(did.to_def_id()),
tcx.def_descr(did.to_def_id())
),
});
}
}
_ => {}
}
}
// Apply the minimum function alignment here, so that individual backends don't have to.
codegen_fn_attrs.alignment = Ord::max(
codegen_fn_attrs.alignment,
tcx.sess.opts.unstable_opts.min_function_alignment,
);
let Some(Ident { name, .. }) = attr.ident() else {
continue;
};
@ -141,28 +166,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED
}
sym::naked => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED,
sym::no_mangle => {
no_mangle_span = Some(attr.span());
if tcx.opt_item_name(did.to_def_id()).is_some() {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
mixed_export_name_no_mangle_lint_state.track_no_mangle(
attr.span(),
tcx.local_def_id_to_hir_id(did),
attr,
);
} else {
tcx.dcx()
.struct_span_err(
attr.span(),
format!(
"`#[no_mangle]` cannot be used on {} {} as it has no name",
tcx.def_descr_article(did.to_def_id()),
tcx.def_descr(did.to_def_id()),
),
)
.emit();
}
}
sym::rustc_std_internal_symbol => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
}
@ -544,12 +547,15 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
&& codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
{
let no_mangle_span =
find_attr!(attrs, AttributeKind::NoMangle(no_mangle_span) => *no_mangle_span)
.unwrap_or_default();
let lang_item =
lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name));
let mut err = tcx
.dcx()
.struct_span_err(
no_mangle_span.unwrap_or_default(),
no_mangle_span,
"`#[no_mangle]` cannot be used on internal language items",
)
.with_note("Rustc requires this item to have a specific mangled name.")

View file

@ -1310,3 +1310,11 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_
diag
}
}
#[derive(Diagnostic)]
#[diag(codegen_ssa_no_mangle_nameless)]
pub(crate) struct NoMangleNameless {
#[primary_span]
pub span: Span,
pub definition: String,
}

View file

@ -131,12 +131,8 @@ fn prefix_and_suffix<'tcx>(
let attrs = tcx.codegen_fn_attrs(instance.def_id());
let link_section = attrs.link_section.map(|symbol| symbol.as_str().to_string());
// function alignment can be set globally with the `-Zmin-function-alignment=<n>` flag;
// the alignment from a `#[repr(align(<n>))]` is used if it specifies a higher alignment.
// if no alignment is specified, an alignment of 4 bytes is used.
let min_function_alignment = tcx.sess.opts.unstable_opts.min_function_alignment;
let align_bytes =
Ord::max(min_function_alignment, attrs.alignment).map(|a| a.bytes()).unwrap_or(4);
// If no alignment is specified, an alignment of 4 bytes is used.
let align_bytes = attrs.alignment.map(|a| a.bytes()).unwrap_or(4);
// In particular, `.arm` can also be written `.code 32` and `.thumb` as `.code 16`.
let (arch_prefix, arch_suffix) = if is_arm {

View file

@ -877,12 +877,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
if let Some(fn_val) = self.get_fn_alloc(id) {
let align = match fn_val {
FnVal::Instance(instance) => {
// Function alignment can be set globally with the `-Zmin-function-alignment=<n>` flag;
// the alignment from a `#[repr(align(<n>))]` is used if it specifies a higher alignment.
let fn_align = self.tcx.codegen_fn_attrs(instance.def_id()).alignment;
let global_align = self.tcx.sess.opts.unstable_opts.min_function_alignment;
Ord::max(global_align, fn_align).unwrap_or(Align::ONE)
self.tcx.codegen_fn_attrs(instance.def_id()).alignment.unwrap_or(Align::ONE)
}
// Machine-specific extra functions currently do not support alignment restrictions.
FnVal::Other(_) => Align::ONE,

View file

@ -21,6 +21,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_ast::visit::{FnCtxt, FnKind};
use rustc_ast::{self as ast, *};
use rustc_ast_pretty::pprust::expr_to_string;
use rustc_attr_data_structures::{AttributeKind, find_attr};
use rustc_errors::{Applicability, LintDiagnostic};
use rustc_feature::GateIssue;
use rustc_hir as hir;
@ -954,7 +955,7 @@ declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GEN
impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
let attrs = cx.tcx.hir_attrs(it.hir_id());
let check_no_mangle_on_generic_fn = |attr: &hir::Attribute,
let check_no_mangle_on_generic_fn = |attr_span: Span,
impl_generics: Option<&hir::Generics<'_>>,
generics: &hir::Generics<'_>,
span| {
@ -967,7 +968,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
cx.emit_span_lint(
NO_MANGLE_GENERIC_ITEMS,
span,
BuiltinNoMangleGeneric { suggestion: attr.span() },
BuiltinNoMangleGeneric { suggestion: attr_span },
);
break;
}
@ -976,14 +977,15 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
};
match it.kind {
hir::ItemKind::Fn { generics, .. } => {
if let Some(attr) = attr::find_by_name(attrs, sym::export_name)
.or_else(|| attr::find_by_name(attrs, sym::no_mangle))
if let Some(attr_span) = attr::find_by_name(attrs, sym::export_name)
.map(|at| at.span())
.or_else(|| find_attr!(attrs, AttributeKind::NoMangle(span) => *span))
{
check_no_mangle_on_generic_fn(attr, None, generics, it.span);
check_no_mangle_on_generic_fn(attr_span, None, generics, it.span);
}
}
hir::ItemKind::Const(..) => {
if attr::contains_name(attrs, sym::no_mangle) {
if find_attr!(attrs, AttributeKind::NoMangle(..)) {
// account for "pub const" (#45562)
let start = cx
.tcx
@ -1008,11 +1010,12 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
for it in *items {
if let hir::AssocItemKind::Fn { .. } = it.kind {
let attrs = cx.tcx.hir_attrs(it.id.hir_id());
if let Some(attr) = attr::find_by_name(attrs, sym::export_name)
.or_else(|| attr::find_by_name(attrs, sym::no_mangle))
if let Some(attr_span) = attr::find_by_name(attrs, sym::export_name)
.map(|at| at.span())
.or_else(|| find_attr!(attrs, AttributeKind::NoMangle(span) => *span))
{
check_no_mangle_on_generic_fn(
attr,
attr_span,
Some(generics),
cx.tcx.hir_get_generics(it.id.owner_id.def_id).unwrap(),
it.span,

View file

@ -1,5 +1,5 @@
use rustc_abi::ExternAbi;
use rustc_attr_data_structures::{AttributeKind, ReprAttr};
use rustc_attr_data_structures::{AttributeKind, ReprAttr, find_attr};
use rustc_attr_parsing::AttributeParser;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::FnKind;
@ -396,7 +396,9 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
match &fk {
FnKind::Method(ident, sig, ..) => match method_context(cx, id) {
MethodLateContext::PlainImpl => {
if sig.header.abi != ExternAbi::Rust && cx.tcx.has_attr(id, sym::no_mangle) {
if sig.header.abi != ExternAbi::Rust
&& find_attr!(cx.tcx.get_all_attrs(id), AttributeKind::NoMangle(..))
{
return;
}
self.check_snake_case(cx, "method", ident);
@ -408,7 +410,9 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
},
FnKind::ItemFn(ident, _, header) => {
// Skip foreign-ABI #[no_mangle] functions (Issue #31924)
if header.abi != ExternAbi::Rust && cx.tcx.has_attr(id, sym::no_mangle) {
if header.abi != ExternAbi::Rust
&& find_attr!(cx.tcx.get_all_attrs(id), AttributeKind::NoMangle(..))
{
return;
}
self.check_snake_case(cx, "function", ident);
@ -514,7 +518,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
let attrs = cx.tcx.hir_attrs(it.hir_id());
match it.kind {
hir::ItemKind::Static(_, ident, ..)
if !ast::attr::contains_name(attrs, sym::no_mangle) =>
if !find_attr!(attrs, AttributeKind::NoMangle(..)) =>
{
NonUpperCaseGlobals::check_upper_case(cx, "static variable", &ident);
}

View file

@ -174,6 +174,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
Attribute::Parsed(AttributeKind::MustUse { span, .. }) => {
self.check_must_use(hir_id, *span, target)
}
Attribute::Parsed(AttributeKind::NoMangle(attr_span)) => {
self.check_no_mangle(hir_id, *attr_span, span, target)
}
Attribute::Unparsed(attr_item) => {
style = Some(attr_item.style);
match attr.path().as_slice() {
@ -261,7 +264,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
[sym::link, ..] => self.check_link(hir_id, attr, span, target),
[sym::link_name, ..] => self.check_link_name(hir_id, attr, span, target),
[sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target),
[sym::no_mangle, ..] => self.check_no_mangle(hir_id, attr, span, target),
[sym::macro_use, ..] | [sym::macro_escape, ..] => {
self.check_macro_use(hir_id, attr, target)
}
@ -698,6 +700,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
AttributeKind::Deprecation { .. }
| AttributeKind::Repr { .. }
| AttributeKind::Align { .. }
| AttributeKind::NoMangle(..)
| AttributeKind::Cold(..)
| AttributeKind::MustUse { .. },
) => {
@ -1952,7 +1955,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
/// Checks if `#[no_mangle]` is applied to a function or static.
fn check_no_mangle(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
fn check_no_mangle(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) {
match target {
Target::Static | Target::Fn => {}
Target::Method(..) if self.is_impl_item(hir_id) => {}
@ -1961,7 +1964,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "no_mangle");
self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "no_mangle");
}
// FIXME: #[no_mangle] was previously allowed on non-functions/statics, this should be an error
// The error should specify that the item that is wrong is specifically a *foreign* fn/static
@ -1975,8 +1978,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span(),
errors::NoMangleForeign { span, attr_span: attr.span(), foreign_item_kind },
attr_span,
errors::NoMangleForeign { span, attr_span, foreign_item_kind },
);
}
_ => {
@ -1985,7 +1988,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
self.tcx.emit_node_span_lint(
UNUSED_ATTRIBUTES,
hir_id,
attr.span(),
attr_span,
errors::NoMangle { span },
);
}

View file

@ -13,6 +13,11 @@ used for many other purposes. For example, tests can also be configured to [run
the resulting program](#controlling-passfail-expectations) to verify its
behavior.
For a survey of each subdirectory's purpose under `tests/ui`, consult the
[SUMMARY.md](https://github.com/rust-lang/rust/tree/master/tests/ui/SUMMARY.md).
This is useful if you write a new test, and are looking for a category to
place it in.
If you need to work with `#![no_std]` cross-compiling tests, consult the
[`minicore` test auxiliary](./minicore.md) chapter.

View file

@ -746,15 +746,17 @@ impl Item {
Some(tcx.visibility(def_id))
}
pub(crate) fn attributes_without_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec<String> {
fn attributes_without_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec<String> {
const ALLOWED_ATTRIBUTES: &[Symbol] =
&[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive];
self.attrs
.other_attrs
.iter()
.filter_map(|attr| {
if is_json {
// NoMangle is special-cased because cargo-semver-checks uses it
if matches!(attr, hir::Attribute::Parsed(AttributeKind::NoMangle(..))) {
Some("#[no_mangle]".to_string())
} else if is_json {
match attr {
// rustdoc-json stores this in `Item::deprecation`, so we
// don't want it it `Item::attrs`.
@ -767,26 +769,22 @@ impl Item {
s
}),
}
} else if attr.has_any_name(ALLOWED_ATTRIBUTES) {
} else {
if !attr.has_any_name(ALLOWED_ATTRIBUTES) {
return None;
}
Some(
rustc_hir_pretty::attribute_to_string(&tcx, attr)
.replace("\\\n", "")
.replace('\n', "")
.replace(" ", " "),
)
} else {
None
}
})
.collect()
}
pub(crate) fn attributes_and_repr(
&self,
tcx: TyCtxt<'_>,
cache: &Cache,
is_json: bool,
) -> Vec<String> {
pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Vec<String> {
let mut attrs = self.attributes_without_repr(tcx, is_json);
if let Some(repr_attr) = self.repr(tcx, cache, is_json) {

View file

@ -1194,7 +1194,7 @@ fn render_assoc_item(
// a whitespace prefix and newline.
fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display {
fmt::from_fn(move |f| {
for a in it.attributes_and_repr(cx.tcx(), cx.cache(), false) {
for a in it.attributes(cx.tcx(), cx.cache(), false) {
writeln!(f, "{prefix}{a}")?;
}
Ok(())
@ -1210,7 +1210,7 @@ fn render_code_attribute(code_attr: CodeAttribute, w: &mut impl fmt::Write) {
// When an attribute is rendered inside a <code> tag, it is formatted using
// a div to produce a newline after it.
fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) {
for attr in it.attributes_and_repr(cx.tcx(), cx.cache(), false) {
for attr in it.attributes(cx.tcx(), cx.cache(), false) {
render_code_attribute(CodeAttribute(attr), w);
}
}

View file

@ -1491,7 +1491,7 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
writeln!(f, "{repr}")?;
};
} else {
for a in self.it.attributes_and_repr(self.cx.tcx(), self.cx.cache(), false) {
for a in self.it.attributes(self.cx.tcx(), self.cx.cache(), false) {
writeln!(f, "{a}")?;
}
}

View file

@ -40,7 +40,7 @@ impl JsonRenderer<'_> {
})
.collect();
let docs = item.opt_doc_value();
let attrs = item.attributes_and_repr(self.tcx, &self.cache, true);
let attrs = item.attributes(self.tcx, &self.cache, true);
let span = item.span(self.tcx);
let visibility = item.visibility(self.tcx);
let clean::ItemInner { name, item_id, .. } = *item.inner;

View file

@ -37,8 +37,8 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
// will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line
// are deliberately not in a doc comment, because they need not be in public docs.)
//
// Latest feature: Pretty printing of must_use attributes changed
pub const FORMAT_VERSION: u32 = 52;
// Latest feature: Pretty printing of no_mangle attributes changed
pub const FORMAT_VERSION: u32 = 53;
/// The root of the emitted JSON blob.
///

View file

@ -15,9 +15,8 @@ use clippy_utils::ty::is_must_use_ty;
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{return_ty, trait_ref_of_method};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_attr_data_structures::AttributeKind;
use rustc_span::Symbol;
use rustc_attr_data_structures::find_attr;
use rustc_attr_data_structures::{AttributeKind, find_attr};
use core::ops::ControlFlow;
@ -36,7 +35,7 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
if let Some((attr_span, reason)) = attr {
check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, *attr_span, *reason, attrs, sig);
} else if is_public && !is_proc_macro(attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) {
} else if is_public && !is_proc_macro(attrs) && !find_attr!(attrs, AttributeKind::NoMangle(..)) {
check_must_use_candidate(
cx,
sig.decl,

View file

@ -6,6 +6,8 @@ use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::{BytePos, Pos};
use rustc_attr_data_structures::AttributeKind;
use rustc_hir::Attribute;
declare_clippy_lint! {
/// ### What it does
@ -44,8 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
let mut app = Applicability::MaybeIncorrect;
let fn_snippet = snippet_with_applicability(cx, fn_sig.span.with_hi(ident.span.lo()), "..", &mut app);
for attr in attrs {
if let Some(ident) = attr.ident()
&& ident.name == rustc_span::sym::no_mangle
if let Attribute::Parsed(AttributeKind::NoMangle(attr_span)) = attr
&& fn_sig.header.abi == ExternAbi::Rust
&& let Some((fn_attrs, _)) = fn_snippet.rsplit_once("fn")
&& !fn_attrs.contains("extern")
@ -54,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
.span
.with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len()))
.shrink_to_lo();
let attr_snippet = snippet(cx, attr.span(), "..");
let attr_snippet = snippet(cx, *attr_span, "..");
span_lint_and_then(
cx,

View file

@ -4,6 +4,7 @@
use std::ffi::OsStr;
use std::path::Path;
use std::process::Command;
use std::str::FromStr;
use build_helper::ci::CiEnv;
use build_helper::git::{GitConfig, get_closest_upstream_commit};
@ -71,9 +72,22 @@ pub fn check(src_path: &Path, bad: &mut bool) {
Some(output) => {
let mut format_version_updated = false;
let mut latest_feature_comment_updated = false;
let mut new_version = None;
let mut old_version = None;
for line in output.lines() {
if line.starts_with("+pub const FORMAT_VERSION: u32 =") {
format_version_updated = true;
new_version = line
.split('=')
.nth(1)
.and_then(|s| s.trim().split(';').next())
.and_then(|s| u32::from_str(s.trim()).ok());
} else if line.starts_with("-pub const FORMAT_VERSION: u32 =") {
old_version = line
.split('=')
.nth(1)
.and_then(|s| s.trim().split(';').next())
.and_then(|s| u32::from_str(s.trim()).ok());
} else if line.starts_with("+// Latest feature:") {
latest_feature_comment_updated = true;
}
@ -92,6 +106,17 @@ pub fn check(src_path: &Path, bad: &mut bool) {
);
}
}
match (new_version, old_version) {
(Some(new_version), Some(old_version)) if new_version != old_version + 1 => {
*bad = true;
eprintln!(
"error in `rustdoc_json` tidy check: invalid `FORMAT_VERSION` increase in \
`{RUSTDOC_JSON_TYPES}/lib.rs`, should be `{}`, found `{new_version}`",
old_version + 1,
);
}
_ => {}
}
}
None => {
*bad = true;

1594
tests/ui/SUMMARY.md Normal file

File diff suppressed because it is too large Load diff

View file

@ -114,18 +114,6 @@ LL | #[export_name = "exported_symbol_name2"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
error: unused attribute
--> $DIR/unused-attr-duplicate.rs:98:1
|
LL | #[no_mangle]
| ^^^^^^^^^^^^ help: remove this attribute
|
note: attribute also specified here
--> $DIR/unused-attr-duplicate.rs:97:1
|
LL | #[no_mangle]
| ^^^^^^^^^^^^
error: unused attribute
--> $DIR/unused-attr-duplicate.rs:102:1
|
@ -289,5 +277,17 @@ note: attribute also specified here
LL | #[cold]
| ^^^^^^^
error: unused attribute
--> $DIR/unused-attr-duplicate.rs:98:1
|
LL | #[no_mangle]
| ^^^^^^^^^^^^ help: remove this attribute
|
note: attribute also specified here
--> $DIR/unused-attr-duplicate.rs:97:1
|
LL | #[no_mangle]
| ^^^^^^^^^^^^
error: aborting due to 23 previous errors