Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit a23917c

Browse files
committed
Add hard error and migration lint for unsafe attrs
1 parent 33422e7 commit a23917c

24 files changed

+473
-53
lines changed

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,7 @@ fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericPara
899899

900900
impl<'a> Visitor<'a> for AstValidator<'a> {
901901
fn visit_attribute(&mut self, attr: &Attribute) {
902-
validate_attr::check_attr(&self.session.psess, attr);
902+
validate_attr::check_attr(&self.features, &self.session.psess, attr);
903903
}
904904

905905
fn visit_ty(&mut self, ty: &'a Ty) {

compiler/rustc_expand/src/expand.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1882,7 +1882,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
18821882
let mut span: Option<Span> = None;
18831883
while let Some(attr) = attrs.next() {
18841884
rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features);
1885-
validate_attr::check_attr(&self.cx.sess.psess, attr);
1885+
validate_attr::check_attr(features, &self.cx.sess.psess, attr);
18861886

18871887
let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span };
18881888
span = Some(current_span);

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,10 +1145,6 @@ pub fn is_valid_for_get_attr(name: Symbol) -> bool {
11451145
})
11461146
}
11471147

1148-
pub fn is_unsafe_attr(name: Symbol) -> bool {
1149-
BUILTIN_ATTRIBUTE_MAP.get(&name).is_some_and(|attr| attr.safety == AttributeSafety::Unsafe)
1150-
}
1151-
11521148
pub static BUILTIN_ATTRIBUTE_MAP: LazyLock<FxHashMap<Symbol, &BuiltinAttribute>> =
11531149
LazyLock::new(|| {
11541150
let mut map = FxHashMap::default();

compiler/rustc_feature/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ pub use accepted::ACCEPTED_FEATURES;
125125
pub use builtin_attrs::AttributeDuplicates;
126126
pub use builtin_attrs::{
127127
deprecated_attributes, encode_cross_crate, find_gated_cfg, is_builtin_attr_name,
128-
is_unsafe_attr, is_valid_for_get_attr, AttributeGate, AttributeTemplate, AttributeType,
128+
is_valid_for_get_attr, AttributeGate, AttributeSafety, AttributeTemplate, AttributeType,
129129
BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP,
130130
};
131131
pub use removed::REMOVED_FEATURES;

compiler/rustc_lint/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,10 @@ lint_unnameable_test_items = cannot test inner items
825825
lint_unnecessary_qualification = unnecessary qualification
826826
.suggestion = remove the unnecessary path segments
827827
828+
lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe
829+
.label = usage of unsafe attribute
830+
lint_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)`
831+
828832
lint_unsupported_group = `{$lint_group}` lint group is not supported with ´--force-warn´
829833
830834
lint_untranslatable_diag = diagnostics should be created using translatable messages

compiler/rustc_lint/src/context/diagnostics.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,16 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
319319
BuiltinLintDiag::UnusedQualifications { removal_span } => {
320320
lints::UnusedQualifications { removal_span }.decorate_lint(diag);
321321
}
322+
BuiltinLintDiag::UnsafeAttrOutsideUnsafe {
323+
attribute_name_span,
324+
sugg_spans: (left, right),
325+
} => {
326+
lints::UnsafeAttrOutsideUnsafe {
327+
span: attribute_name_span,
328+
suggestion: lints::UnsafeAttrOutsideUnsafeSuggestion { left, right },
329+
}
330+
.decorate_lint(diag);
331+
}
322332
BuiltinLintDiag::AssociatedConstElidedLifetime {
323333
elided,
324334
span: lt_span,

compiler/rustc_lint/src/lints.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2890,3 +2890,24 @@ pub struct RedundantImportVisibility {
28902890
pub import_vis: String,
28912891
pub max_vis: String,
28922892
}
2893+
2894+
#[derive(LintDiagnostic)]
2895+
#[diag(lint_unsafe_attr_outside_unsafe)]
2896+
pub struct UnsafeAttrOutsideUnsafe {
2897+
#[label]
2898+
pub span: Span,
2899+
#[subdiagnostic]
2900+
pub suggestion: UnsafeAttrOutsideUnsafeSuggestion,
2901+
}
2902+
2903+
#[derive(Subdiagnostic)]
2904+
#[multipart_suggestion(
2905+
lint_unsafe_attr_outside_unsafe_suggestion,
2906+
applicability = "machine-applicable"
2907+
)]
2908+
pub struct UnsafeAttrOutsideUnsafeSuggestion {
2909+
#[suggestion_part(code = "unsafe(")]
2910+
pub left: Span,
2911+
#[suggestion_part(code = ")")]
2912+
pub right: Span,
2913+
}

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ declare_lint_pass! {
115115
UNNAMEABLE_TYPES,
116116
UNREACHABLE_CODE,
117117
UNREACHABLE_PATTERNS,
118+
UNSAFE_ATTR_OUTSIDE_UNSAFE,
118119
UNSAFE_OP_IN_UNSAFE_FN,
119120
UNSTABLE_NAME_COLLISIONS,
120121
UNSTABLE_SYNTAX_PRE_EXPANSION,
@@ -4902,3 +4903,45 @@ declare_lint! {
49024903
reference: "issue #123743 <https://github.com/rust-lang/rust/issues/123743>",
49034904
};
49044905
}
4906+
4907+
declare_lint! {
4908+
/// The `unsafe_attr_outside_unsafe` lint detects a missing unsafe keyword
4909+
/// on attributes considered unsafe.
4910+
///
4911+
/// ### Example
4912+
///
4913+
/// ```rust
4914+
/// #![feature(unsafe_attributes)]
4915+
/// #![warn(unsafe_attr_outside_unsafe)]
4916+
///
4917+
/// #[no_mangle]
4918+
/// extern "C" fn foo() {}
4919+
///
4920+
/// fn main() {}
4921+
/// ```
4922+
///
4923+
/// {{produces}}
4924+
///
4925+
/// ### Explanation
4926+
///
4927+
/// Some attributes (e.g. `no_mangle`, `export_name`, `link_section` -- see
4928+
/// [issue #82499] for a more complete list) are considered "unsafe" attributes.
4929+
/// An unsafe attribute must only be used inside unsafe(...).
4930+
///
4931+
/// This lint can automatically wrap the attributes in `unsafe(...)` , but this
4932+
/// obviously cannot verify that the preconditions of the `unsafe`
4933+
/// attributes are fulfilled, so that is still up to the user.
4934+
///
4935+
/// The lint is currently "allow" by default, but that might change in the
4936+
/// future.
4937+
///
4938+
/// [editions]: https://doc.rust-lang.org/edition-guide/
4939+
/// [issue #82499]: https://github.com/rust-lang/rust/issues/82499
4940+
pub UNSAFE_ATTR_OUTSIDE_UNSAFE,
4941+
Allow,
4942+
"detects unsafe attributes outside of unsafe",
4943+
@future_incompatible = FutureIncompatibleInfo {
4944+
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
4945+
reference: "issue #123757 <https://github.com/rust-lang/rust/issues/123757>",
4946+
};
4947+
}

compiler/rustc_lint_defs/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,10 @@ pub enum BuiltinLintDiag {
691691
/// The span of the unnecessarily-qualified path to remove.
692692
removal_span: Span,
693693
},
694+
UnsafeAttrOutsideUnsafe {
695+
attribute_name_span: Span,
696+
sugg_spans: (Span, Span),
697+
},
694698
AssociatedConstElidedLifetime {
695699
elided: bool,
696700
span: Span,

compiler/rustc_parse/messages.ftl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,10 @@ parse_inner_doc_comment_not_permitted = expected outer doc comment
366366
.label_does_not_annotate_this = the inner doc comment doesn't annotate this {$item}
367367
.sugg_change_inner_to_outer = to annotate the {$item}, change the doc comment from inner to outer style
368368
369+
parse_invalid_attr_unsafe = `{$name}` is not an unsafe attribute
370+
.suggestion = remove the `unsafe(...)`
371+
.note = extraneous unsafe is not allowed in attributes
372+
369373
parse_invalid_block_macro_segment = cannot use a `block` macro fragment here
370374
.label = the `block` fragment is within this context
371375
.suggestion = wrap this in another block
@@ -866,6 +870,11 @@ parse_unmatched_angle_brackets = {$num_extra_brackets ->
866870
*[other] remove extra angle brackets
867871
}
868872
873+
parse_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe
874+
.label = usage of unsafe attribute
875+
parse_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)`
876+
877+
869878
parse_unskipped_whitespace = whitespace symbol '{$ch}' is not skipped
870879
.label = {parse_unskipped_whitespace}
871880

0 commit comments

Comments
 (0)