Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1067,7 +1067,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, self.lint_node_id);
validate_attr::check_attr(&self.sess.psess, attr);
}

fn visit_ty(&mut self, ty: &'a Ty) {
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_attr_parsing/src/attributes/cfg.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::convert::identity;

use rustc_ast::token::Delimiter;
use rustc_ast::tokenstream::DelimSpan;
use rustc_ast::{AttrItem, Attribute, CRATE_NODE_ID, LitKind, NodeId, ast, token};
Expand Down Expand Up @@ -353,7 +355,7 @@ pub fn parse_cfg_attr(
span,
attr_span: cfg_attr.span,
template: CFG_ATTR_TEMPLATE,
path: AttrPath::from_ast(&cfg_attr.get_normal_item().path),
path: AttrPath::from_ast(&cfg_attr.get_normal_item().path, identity),
description: ParsedDescription::Attribute,
reason,
suggestions: CFG_ATTR_TEMPLATE
Expand Down Expand Up @@ -398,6 +400,7 @@ fn parse_cfg_attr_internal<'a>(
.into_boxed_slice(),
span: attribute.span,
},
Some(attribute.get_normal_item().unsafety),
ParsedDescription::Attribute,
pred_span,
CRATE_NODE_ID,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_attr_parsing/src/attributes/cfg_select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ pub fn parse_cfg_select(
segments: vec![Ident::from_str("cfg_select")].into_boxed_slice(),
span: cfg_span,
},
None,
ParsedDescription::Macro,
cfg_span,
lint_node_id,
Expand Down
36 changes: 28 additions & 8 deletions compiler/rustc_attr_parsing/src/interface.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::borrow::Cow;

use rustc_ast as ast;
use rustc_ast::{AttrStyle, NodeId};
use rustc_ast::{AttrStyle, NodeId, Safety};
use rustc_errors::DiagCtxtHandle;
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::attrs::AttributeKind;
Expand Down Expand Up @@ -146,6 +146,7 @@ impl<'sess> AttributeParser<'sess, Early> {
normal_attr.item.span(),
attr.style,
path.get_attribute_path(),
Some(normal_attr.item.unsafety),
ParsedDescription::Attribute,
target_span,
target_node_id,
Expand All @@ -165,6 +166,7 @@ impl<'sess> AttributeParser<'sess, Early> {
inner_span: Span,
attr_style: AttrStyle,
attr_path: AttrPath,
attr_safety: Option<Safety>,
parsed_description: ParsedDescription,
target_span: Span,
target_node_id: NodeId,
Expand All @@ -181,14 +183,24 @@ impl<'sess> AttributeParser<'sess, Early> {
sess,
stage: Early { emit_errors },
};
let mut emit_lint = |lint| {
crate::lints::emit_attribute_lint(&lint, sess);
};
if let Some(safety) = attr_safety {
parser.check_attribute_safety(
&attr_path,
inner_span,
safety,
&mut emit_lint,
target_node_id,
)
}
let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
shared: SharedContext {
cx: &mut parser,
target_span,
target_id: target_node_id,
emit_lint: &mut |lint| {
crate::lints::emit_attribute_lint(&lint, sess);
},
emit_lint: &mut emit_lint,
},
attr_span,
inner_span,
Expand Down Expand Up @@ -288,6 +300,15 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
// }
ast::AttrKind::Normal(n) => {
attr_paths.push(PathParser(Cow::Borrowed(&n.item.path)));
let attr_path = AttrPath::from_ast(&n.item.path, lower_span);

self.check_attribute_safety(
&attr_path,
lower_span(n.item.span()),
n.item.unsafety,
&mut emit_lint,
target_id,
);

let parts =
n.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
Expand All @@ -301,7 +322,6 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
) else {
continue;
};
let path = parser.path();
let args = parser.args();
for accept in accepts {
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
Expand All @@ -312,11 +332,11 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
emit_lint: &mut emit_lint,
},
attr_span: lower_span(attr.span),
inner_span: lower_span(attr.get_normal_item().span()),
inner_span: lower_span(n.item.span()),
attr_style: attr.style,
parsed_description: ParsedDescription::Attribute,
template: &accept.template,
attr_path: path.get_attribute_path(),
attr_path: attr_path.clone(),
};

(accept.accept_fn)(&mut cx, args);
Expand All @@ -341,7 +361,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
// );

attributes.push(Attribute::Unparsed(Box::new(AttrItem {
path: AttrPath::from_ast(&n.item.path),
path: attr_path.clone(),
args: self.lower_attr_args(&n.item.args, lower_span),
id: HashIgnoredAttrId { attr_id: attr.id },
style: attr.style,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_attr_parsing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ mod interface;
pub mod parser;

mod lints;
mod safety;
mod session_diagnostics;
mod target_checking;
pub mod validate_attr;
Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_attr_parsing/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,17 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<L::Id>, lint_emi
},
)
}
&AttributeLintKind::UnsafeAttrOutsideUnsafe {
attribute_name_span,
sugg_spans: (left, right),
} => lint_emitter.emit_node_span_lint(
rustc_session::lint::builtin::UNSAFE_ATTR_OUTSIDE_UNSAFE,
*id,
*span,
session_diagnostics::UnsafeAttrOutsideUnsafeLint {
span: attribute_name_span,
suggestion: session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion { left, right },
},
),
}
}
116 changes: 116 additions & 0 deletions compiler/rustc_attr_parsing/src/safety.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
use rustc_ast::Safety;
use rustc_feature::{AttributeSafety, BUILTIN_ATTRIBUTE_MAP};
use rustc_hir::AttrPath;
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
use rustc_span::{Span, sym};

use crate::context::Stage;
use crate::{AttributeParser, ShouldEmit};

impl<'sess, S: Stage> AttributeParser<'sess, S> {
pub fn check_attribute_safety(
&mut self,
attr_path: &AttrPath,
attr_span: Span,
attr_safety: Safety,
emit_lint: &mut impl FnMut(AttributeLint<S::Id>),
target_id: S::Id,
) {
if matches!(self.stage.should_emit(), ShouldEmit::Nothing) {
return;
}

let name = (attr_path.segments.len() == 1).then_some(attr_path.segments[0].name);
if let Some(name) = name
&& [sym::cfg_trace, sym::cfg_attr_trace].contains(&name)
{
return;
}

// FIXME: We should retrieve this information from the attribute parsers instead of from `BUILTIN_ATTRIBUTE_MAP`
let builtin_attr_info = name.and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name));
let builtin_attr_safety = builtin_attr_info.map(|x| x.safety);

match (builtin_attr_safety, attr_safety) {
// - Unsafe builtin attribute
// - User wrote `#[unsafe(..)]`, which is permitted on any edition
(Some(AttributeSafety::Unsafe { .. }), Safety::Unsafe(..)) => {
// OK
}

// - Unsafe builtin attribute
// - User did not write `#[unsafe(..)]`
(Some(AttributeSafety::Unsafe { unsafe_since }), Safety::Default) => {
let path_span = attr_path.span;

// If the `attr_item`'s span is not from a macro, then just suggest
// wrapping it in `unsafe(...)`. Otherwise, we suggest putting the
// `unsafe(`, `)` right after and right before the opening and closing
// square bracket respectively.
let diag_span = attr_span;

// Attributes can be safe in earlier editions, and become unsafe in later ones.
//
// Use the span of the attribute's name to determine the edition: the span of the
// attribute as a whole may be inaccurate if it was emitted by a macro.
//
// See https://github.com/rust-lang/rust/issues/142182.
let emit_error = match unsafe_since {
None => true,
Some(unsafe_since) => path_span.edition() >= unsafe_since,
};

if emit_error {
self.stage.emit_err(
self.sess,
crate::session_diagnostics::UnsafeAttrOutsideUnsafe {
span: path_span,
suggestion:
crate::session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion {
left: diag_span.shrink_to_lo(),
right: diag_span.shrink_to_hi(),
},
},
);
} else {
emit_lint(AttributeLint {
id: target_id,
span: path_span,
kind: AttributeLintKind::UnsafeAttrOutsideUnsafe {
attribute_name_span: path_span,
sugg_spans: (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()),
},
})
}
}

// - Normal builtin attribute
// - Writing `#[unsafe(..)]` is not permitted on normal builtin attributes
(None | Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => {
self.stage.emit_err(
self.sess,
crate::session_diagnostics::InvalidAttrUnsafe {
span: unsafe_span,
name: attr_path.clone(),
},
);
}

// - Normal builtin attribute
// - No explicit `#[unsafe(..)]` written.
(None | Some(AttributeSafety::Normal), Safety::Default) => {
// OK
}

(
Some(AttributeSafety::Unsafe { .. } | AttributeSafety::Normal) | None,
Safety::Safe(..),
) => {
self.sess.dcx().span_delayed_bug(
attr_span,
"`check_attribute_safety` does not expect `Safety::Safe` on attributes",
);
}
}
}
}
13 changes: 11 additions & 2 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::num::IntErrorKind;

use rustc_ast::{self as ast, Path};
use rustc_ast::{self as ast};
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
Expand Down Expand Up @@ -790,7 +790,7 @@ pub(crate) struct InvalidAttrUnsafe {
#[primary_span]
#[label]
pub span: Span,
pub name: Path,
pub name: AttrPath,
}

#[derive(Diagnostic)]
Expand All @@ -803,6 +803,15 @@ pub(crate) struct UnsafeAttrOutsideUnsafe {
pub suggestion: UnsafeAttrOutsideUnsafeSuggestion,
}

#[derive(LintDiagnostic)]
#[diag(attr_parsing_unsafe_attr_outside_unsafe)]
pub(crate) struct UnsafeAttrOutsideUnsafeLint {
#[label]
pub span: Span,
#[subdiagnostic]
pub suggestion: UnsafeAttrOutsideUnsafeSuggestion,
}

#[derive(Subdiagnostic)]
#[multipart_suggestion(
attr_parsing_unsafe_attr_outside_unsafe_suggestion,
Expand Down
Loading
Loading