Rollup merge of #140617 - Urgau:unsafe_attr-lint-allow, r=jieyouxu

Report the `unsafe_attr_outside_unsafe` lint at the closest node

This PR have `AstValidation` track a linting node id and then uses it when reporting the `unsafe_attr_outside_unsafe` lint, so that instead of being bound at the crate-root, `#[allow]` of the lint works at any node.

Fixes rust-lang/rust#140602
r? `@jieyouxu`
This commit is contained in:
Stuart Cook 2025-05-04 13:21:09 +10:00 committed by GitHub
commit 54821b9ed0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 46 additions and 8 deletions

View file

@ -82,6 +82,8 @@ struct AstValidator<'a> {
/// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe.
extern_mod_safety: Option<Safety>,
lint_node_id: NodeId,
lint_buffer: &'a mut LintBuffer,
}
@ -826,7 +828,7 @@ fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericPara
impl<'a> Visitor<'a> for AstValidator<'a> {
fn visit_attribute(&mut self, attr: &Attribute) {
validate_attr::check_attr(&self.sess.psess, attr);
validate_attr::check_attr(&self.sess.psess, attr, self.lint_node_id);
}
fn visit_ty(&mut self, ty: &'a Ty) {
@ -839,6 +841,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.has_proc_macro_decls = true;
}
let previous_lint_node_id = mem::replace(&mut self.lint_node_id, item.id);
if let Some(ident) = item.kind.ident()
&& attr::contains_name(&item.attrs, sym::no_mangle)
{
@ -1128,6 +1132,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
_ => visit::walk_item(self, item),
}
self.lint_node_id = previous_lint_node_id;
}
fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
@ -1694,6 +1700,7 @@ pub fn check_crate(
outer_impl_trait_span: None,
disallow_tilde_const: Some(TildeConstReason::Item),
extern_mod_safety: None,
lint_node_id: CRATE_NODE_ID,
lint_buffer: lints,
};
visit::walk_crate(&mut validator, krate);

View file

@ -274,7 +274,12 @@ impl<'a> StripUnconfigured<'a> {
/// is in the original source file. Gives a compiler error if the syntax of
/// the attribute is incorrect.
pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec<Attribute> {
validate_attr::check_attribute_safety(&self.sess.psess, AttributeSafety::Normal, &cfg_attr);
validate_attr::check_attribute_safety(
&self.sess.psess,
AttributeSafety::Normal,
&cfg_attr,
ast::CRATE_NODE_ID,
);
// A trace attribute left in AST in place of the original `cfg_attr` attribute.
// It can later be used by lints or other diagnostics.

View file

@ -1983,7 +1983,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
let mut span: Option<Span> = None;
while let Some(attr) = attrs.next() {
rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features);
validate_attr::check_attr(&self.cx.sess.psess, attr);
validate_attr::check_attr(
&self.cx.sess.psess,
attr,
self.cx.current_expansion.lint_node_id,
);
let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span };
span = Some(current_span);

View file

@ -3,7 +3,8 @@
use rustc_ast::token::Delimiter;
use rustc_ast::tokenstream::DelimSpan;
use rustc_ast::{
self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, Safety,
self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, NodeId,
Safety,
};
use rustc_errors::{Applicability, FatalError, PResult};
use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
@ -15,7 +16,7 @@ use rustc_span::{Span, Symbol, sym};
use crate::{errors, parse_in};
pub fn check_attr(psess: &ParseSess, attr: &Attribute) {
pub fn check_attr(psess: &ParseSess, attr: &Attribute, id: NodeId) {
if attr.is_doc_comment() || attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace)
{
return;
@ -26,7 +27,7 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) {
// All non-builtin attributes are considered safe
let safety = attr_info.map(|x| x.safety).unwrap_or(AttributeSafety::Normal);
check_attribute_safety(psess, safety, attr);
check_attribute_safety(psess, safety, attr, id);
// Check input tokens for built-in and key-value attributes.
match attr_info {
@ -154,7 +155,12 @@ fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaIte
}
}
pub fn check_attribute_safety(psess: &ParseSess, safety: AttributeSafety, attr: &Attribute) {
pub fn check_attribute_safety(
psess: &ParseSess,
safety: AttributeSafety,
attr: &Attribute,
id: NodeId,
) {
let attr_item = attr.get_normal_item();
if let AttributeSafety::Unsafe { unsafe_since } = safety {
@ -185,7 +191,7 @@ pub fn check_attribute_safety(psess: &ParseSess, safety: AttributeSafety, attr:
psess.buffer_lint(
UNSAFE_ATTR_OUTSIDE_UNSAFE,
path_span,
ast::CRATE_NODE_ID,
id,
BuiltinLintDiag::UnsafeAttrOutsideUnsafe {
attribute_name_span: path_span,
sugg_spans: (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()),

View file

@ -0,0 +1,16 @@
//@ check-pass
//@ edition: 2021
//
// Anti-regression test for https://github.com/rust-lang/rust/issues/140602
// where the generated warning couldn't be allowed due too being attached to
// the wrong AST node.
#![deny(unsafe_attr_outside_unsafe)]
#[allow(unsafe_attr_outside_unsafe)]
mod generated {
#[no_mangle]
fn _generated_foo() {}
}
fn main() {}