Rollup merge of #150943 - port_must_not_suspend, r=jdonszelmann,JonathanBrouwer

Port `#[must_not_suspend]` to attribute parser

Tracking issue: rust-lang/rust#131229

r? @JonathanBrouwer
This commit is contained in:
Jonathan Brouwer 2026-01-13 09:01:31 +01:00 committed by GitHub
commit a2994063d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 77 additions and 61 deletions

View file

@ -47,6 +47,7 @@ pub(crate) mod link_attrs;
pub(crate) mod lint_helpers;
pub(crate) mod loop_match;
pub(crate) mod macro_attrs;
pub(crate) mod must_not_suspend;
pub(crate) mod must_use;
pub(crate) mod no_implicit_prelude;
pub(crate) mod no_link;

View file

@ -0,0 +1,35 @@
use super::prelude::*;
pub(crate) struct MustNotSuspendParser;
impl<S: Stage> SingleAttributeParser<S> for MustNotSuspendParser {
const PATH: &[rustc_span::Symbol] = &[sym::must_not_suspend];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Struct),
Allow(Target::Enum),
Allow(Target::Union),
Allow(Target::Trait),
]);
const TEMPLATE: AttributeTemplate = template!(Word, List: &["count"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let reason = match args {
ArgParser::NameValue(reason) => match reason.value_as_str() {
Some(val) => Some(val),
None => {
cx.expected_nv_or_no_args(reason.value_span);
return None;
}
},
ArgParser::NoArgs => None,
ArgParser::List(list) => {
cx.expected_nv_or_no_args(list.span);
return None;
}
};
Some(AttributeKind::MustNotSupend { reason })
}
}

View file

@ -51,6 +51,7 @@ use crate::attributes::macro_attrs::{
AllowInternalUnsafeParser, CollapseDebugInfoParser, MacroEscapeParser, MacroExportParser,
MacroUseParser,
};
use crate::attributes::must_not_suspend::MustNotSuspendParser;
use crate::attributes::must_use::MustUseParser;
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
use crate::attributes::no_link::NoLinkParser;
@ -89,7 +90,6 @@ use crate::session_diagnostics::{
AttributeParseError, AttributeParseErrorReason, ParsedDescription,
};
use crate::target_checking::AllowedTargets;
type GroupType<S> = LazyLock<GroupTypeInner<S>>;
pub(super) struct GroupTypeInner<S: Stage> {
@ -208,6 +208,7 @@ attribute_parsers!(
Single<LinkageParser>,
Single<MacroExportParser>,
Single<MoveSizeLimitParser>,
Single<MustNotSuspendParser>,
Single<MustUseParser>,
Single<ObjcClassParser>,
Single<ObjcSelectorParser>,

View file

@ -824,6 +824,9 @@ pub enum AttributeKind {
/// Represents `#[move_size_limit]`
MoveSizeLimit { attr_span: Span, limit_span: Span, limit: Limit },
/// Represents `#[must_not_suspend]`
MustNotSupend { reason: Option<Symbol> },
/// Represents `#[must_use]`.
MustUse {
span: Span,

View file

@ -71,6 +71,7 @@ impl AttributeKind {
Marker(..) => No,
MayDangle(..) => No,
MoveSizeLimit { .. } => No,
MustNotSupend { .. } => Yes,
MustUse { .. } => Yes,
Naked(..) => No,
NoCore(..) => No,

View file

@ -64,9 +64,9 @@ use itertools::izip;
use rustc_abi::{FieldIdx, VariantIdx};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::pluralize;
use rustc_hir as hir;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{CoroutineDesugaring, CoroutineKind};
use rustc_hir::{self as hir, CoroutineDesugaring, CoroutineKind, find_attr};
use rustc_index::bit_set::{BitMatrix, DenseBitSet, GrowableBitSet};
use rustc_index::{Idx, IndexVec, indexvec};
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
@ -85,7 +85,6 @@ use rustc_mir_dataflow::{
};
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::source_map::dummy_spanned;
use rustc_span::symbol::sym;
use rustc_span::{DUMMY_SP, Span};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::infer::TyCtxtInferExt as _;
@ -1989,11 +1988,11 @@ fn check_must_not_suspend_def(
hir_id: hir::HirId,
data: SuspendCheckData<'_>,
) -> bool {
if let Some(attr) = tcx.get_attr(def_id, sym::must_not_suspend) {
let reason = attr.value_str().map(|s| errors::MustNotSuspendReason {
span: data.source_span,
reason: s.as_str().to_string(),
});
if let Some(reason_str) =
find_attr!(tcx.get_all_attrs(def_id), AttributeKind::MustNotSupend {reason} => reason)
{
let reason =
reason_str.map(|s| errors::MustNotSuspendReason { span: data.source_span, reason: s });
tcx.emit_node_span_lint(
rustc_session::lint::builtin::MUST_NOT_SUSPEND,
hir_id,

View file

@ -323,7 +323,7 @@ impl<'a> LintDiagnostic<'a, ()> for MustNotSupend<'_, '_> {
pub(crate) struct MustNotSuspendReason {
#[primary_span]
pub span: Span,
pub reason: String,
pub reason: Symbol,
}
#[derive(Diagnostic)]

View file

@ -368,10 +368,6 @@ passes_must_implement_not_function_note = all `#[rustc_must_implement_one_of]` a
passes_must_implement_not_function_span_note = required by this annotation
passes_must_not_suspend =
`must_not_suspend` attribute should be applied to a struct, enum, union, or trait
.label = is not a struct, enum, union, or trait
passes_no_main_function =
`main` function not found in crate `{$crate_name}`
.here_is_main = here is a function named `main`

View file

@ -308,6 +308,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::ThreadLocal
| AttributeKind::CfiEncoding { .. }
| AttributeKind::RustcHasIncoherentInherentImpls
| AttributeKind::MustNotSupend { .. }
) => { /* do nothing */ }
Attribute::Unparsed(attr_item) => {
style = Some(attr_item.style);
@ -325,7 +326,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| [sym::rustc_dirty, ..]
| [sym::rustc_if_this_changed, ..]
| [sym::rustc_then_this_would_need, ..] => self.check_rustc_dirty_clean(attr),
[sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target),
[sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => {
self.check_autodiff(hir_id, attr, span, target)
}
@ -1152,16 +1152,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
/// Checks if `#[must_not_suspend]` is applied to a struct, enum, union, or trait.
fn check_must_not_suspend(&self, attr: &Attribute, span: Span, target: Target) {
match target {
Target::Struct | Target::Enum | Target::Union | Target::Trait => {}
_ => {
self.dcx().emit_err(errors::MustNotSuspend { attr_span: attr.span(), span });
}
}
}
/// Checks if `#[may_dangle]` is applied to a lifetime or type generic parameter in `Drop` impl.
fn check_may_dangle(&self, hir_id: HirId, attr_span: Span) {
if let hir::Node::GenericParam(param) = self.tcx.hir_node(hir_id)

View file

@ -186,15 +186,6 @@ pub(crate) struct BothFfiConstAndPure {
pub attr_span: Span,
}
#[derive(Diagnostic)]
#[diag(passes_must_not_suspend)]
pub(crate) struct MustNotSuspend {
#[primary_span]
pub attr_span: Span,
#[label]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(passes_link)]
#[warning]

View file

@ -32,21 +32,6 @@ error: malformed `patchable_function_entry` attribute input
LL | #[patchable_function_entry]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]`
error: malformed `must_not_suspend` attribute input
--> $DIR/malformed-attrs.rs:138:1
|
LL | #[must_not_suspend()]
| ^^^^^^^^^^^^^^^^^^^^^
|
help: the following are the possible correct uses
|
LL - #[must_not_suspend()]
LL + #[must_not_suspend = "reason"]
|
LL - #[must_not_suspend()]
LL + #[must_not_suspend]
|
error: malformed `allow` attribute input
--> $DIR/malformed-attrs.rs:184:1
|
@ -527,6 +512,22 @@ LL | #[rustc_layout_scalar_valid_range_end]
| expected this to be a list
| help: must be of the form: `#[rustc_layout_scalar_valid_range_end(end)]`
error[E0539]: malformed `must_not_suspend` attribute input
--> $DIR/malformed-attrs.rs:138:1
|
LL | #[must_not_suspend()]
| ^^^^^^^^^^^^^^^^^^--^
| |
| didn't expect a list here
|
help: try changing it to one of the following valid forms of the attribute
|
LL | #[must_not_suspend(count)]
| +++++
LL - #[must_not_suspend()]
LL + #[must_not_suspend]
|
error[E0539]: malformed `cfi_encoding` attribute input
--> $DIR/malformed-attrs.rs:140:1
|

View file

@ -2,7 +2,7 @@
#![feature(must_not_suspend)]
#![deny(must_not_suspend)]
#[must_not_suspend] //~ ERROR attribute should be
#[must_not_suspend] //~ ERROR attribute cannot be used on modules
mod inner {}
fn main() {}

View file

@ -1,10 +1,10 @@
error: `must_not_suspend` attribute should be applied to a struct, enum, union, or trait
error: `#[must_not_suspend]` attribute cannot be used on modules
--> $DIR/other_items.rs:5:1
|
LL | #[must_not_suspend]
| ^^^^^^^^^^^^^^^^^^^
LL | mod inner {}
| ------------ is not a struct, enum, union, or trait
|
= help: `#[must_not_suspend]` can be applied to data types, traits, and unions
error: aborting due to 1 previous error

View file

@ -2,7 +2,7 @@
#![feature(must_not_suspend)]
#![deny(must_not_suspend)]
#[must_not_suspend] //~ ERROR attribute should be
#[must_not_suspend] //~ ERROR attribute cannot be used on functions
fn foo() -> i32 {
0
}

View file

@ -1,12 +1,10 @@
error: `must_not_suspend` attribute should be applied to a struct, enum, union, or trait
error: `#[must_not_suspend]` attribute cannot be used on functions
--> $DIR/return.rs:5:1
|
LL | #[must_not_suspend]
| ^^^^^^^^^^^^^^^^^^^
LL | / fn foo() -> i32 {
LL | | 0
LL | | }
| |_- is not a struct, enum, union, or trait
LL | #[must_not_suspend]
| ^^^^^^^^^^^^^^^^^^^
|
= help: `#[must_not_suspend]` can be applied to data types, traits, and unions
error: aborting due to 1 previous error