Skip to content

Commit 0784253

Browse files
committed
mbe: Support unsafe attribute rules
1 parent 258f8db commit 0784253

File tree

8 files changed

+75
-22
lines changed

8 files changed

+75
-22
lines changed

compiler/rustc_attr_parsing/src/validate_attr.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,9 @@ pub fn check_attribute_safety(
207207
}
208208
}
209209

210-
// - Normal builtin attribute, or any non-builtin attribute
211-
// - All non-builtin attributes are currently considered safe; writing `#[unsafe(..)]` is
212-
// not permitted on non-builtin attributes or normal builtin attributes
213-
(Some(AttributeSafety::Normal) | None, Safety::Unsafe(unsafe_span)) => {
210+
// - Normal builtin attribute
211+
// - Writing `#[unsafe(..)]` is not permitted on normal builtin attributes
212+
(Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => {
214213
psess.dcx().emit_err(errors::InvalidAttrUnsafe {
215214
span: unsafe_span,
216215
name: attr_item.path.clone(),
@@ -224,9 +223,8 @@ pub fn check_attribute_safety(
224223
}
225224

226225
// - Non-builtin attribute
227-
// - No explicit `#[unsafe(..)]` written.
228-
(None, Safety::Default) => {
229-
// OK
226+
(None, Safety::Unsafe(_) | Safety::Default) => {
227+
// OK (not checked here)
230228
}
231229

232230
(

compiler/rustc_expand/src/base.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_ast::attr::{AttributeExt, MarkedAttrs};
1010
use rustc_ast::token::MetaVarKind;
1111
use rustc_ast::tokenstream::TokenStream;
1212
use rustc_ast::visit::{AssocCtxt, Visitor};
13-
use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind};
13+
use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind, Safety};
1414
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
1515
use rustc_data_structures::sync;
1616
use rustc_errors::{BufferedEarlyLint, DiagCtxtHandle, ErrorGuaranteed, PResult};
@@ -345,6 +345,21 @@ pub trait AttrProcMacro {
345345
annotation: TokenStream,
346346
annotated: TokenStream,
347347
) -> Result<TokenStream, ErrorGuaranteed>;
348+
349+
// Default implementation for safe attributes; override if the attribute can be unsafe.
350+
fn expand_with_safety<'cx>(
351+
&self,
352+
ecx: &'cx mut ExtCtxt<'_>,
353+
safety: Safety,
354+
span: Span,
355+
annotation: TokenStream,
356+
annotated: TokenStream,
357+
) -> Result<TokenStream, ErrorGuaranteed> {
358+
if let Safety::Unsafe(span) = safety {
359+
ecx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute");
360+
}
361+
self.expand(ecx, span, annotation, annotated)
362+
}
348363
}
349364

350365
impl<F> AttrProcMacro for F

compiler/rustc_expand/src/expand.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -813,11 +813,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
813813
_ => item.to_tokens(),
814814
};
815815
let attr_item = attr.get_normal_item();
816+
let safety = attr_item.unsafety;
816817
if let AttrArgs::Eq { .. } = attr_item.args {
817818
self.cx.dcx().emit_err(UnsupportedKeyValue { span });
818819
}
819820
let inner_tokens = attr_item.args.inner_tokens();
820-
match expander.expand(self.cx, span, inner_tokens, tokens) {
821+
match expander.expand_with_safety(self.cx, safety, span, inner_tokens, tokens) {
821822
Ok(tok_result) => {
822823
let fragment = self.parse_ast_fragment(
823824
tok_result,
@@ -883,6 +884,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
883884
}
884885
}
885886
} else if let SyntaxExtensionKind::NonMacroAttr = ext {
887+
if let ast::Safety::Unsafe(span) = attr.get_normal_item().unsafety {
888+
self.cx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute");
889+
}
886890
// `-Zmacro-stats` ignores these because they don't do any real expansion.
887891
self.cx.expanded_inert_attrs.mark(&attr);
888892
item.visit_attrs(|attrs| attrs.insert(pos, attr));

compiler/rustc_expand/src/mbe/macro_rules.rs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_ast::token::NtPatKind::*;
88
use rustc_ast::token::TokenKind::*;
99
use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind};
1010
use rustc_ast::tokenstream::{self, DelimSpan, TokenStream};
11-
use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId};
11+
use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId, Safety};
1212
use rustc_ast_pretty::pprust;
1313
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
1414
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan};
@@ -132,6 +132,7 @@ pub(super) enum MacroRule {
132132
Func { lhs: Vec<MatcherLoc>, lhs_span: Span, rhs: mbe::TokenTree },
133133
/// An attr rule, for use with `#[m]`
134134
Attr {
135+
unsafe_rule: bool,
135136
args: Vec<MatcherLoc>,
136137
args_span: Span,
137138
body: Vec<MatcherLoc>,
@@ -248,8 +249,19 @@ impl TTMacroExpander for MacroRulesMacroExpander {
248249

249250
impl AttrProcMacro for MacroRulesMacroExpander {
250251
fn expand(
252+
&self,
253+
_cx: &mut ExtCtxt<'_>,
254+
_sp: Span,
255+
_args: TokenStream,
256+
_body: TokenStream,
257+
) -> Result<TokenStream, ErrorGuaranteed> {
258+
unreachable!("`expand` called on `MacroRulesMacroExpander`, expected `expand_with_safety`")
259+
}
260+
261+
fn expand_with_safety(
251262
&self,
252263
cx: &mut ExtCtxt<'_>,
264+
safety: Safety,
253265
sp: Span,
254266
args: TokenStream,
255267
body: TokenStream,
@@ -261,6 +273,7 @@ impl AttrProcMacro for MacroRulesMacroExpander {
261273
self.node_id,
262274
self.name,
263275
self.transparency,
276+
safety,
264277
args,
265278
body,
266279
&self.rules,
@@ -409,6 +422,7 @@ fn expand_macro_attr(
409422
node_id: NodeId,
410423
name: Ident,
411424
transparency: Transparency,
425+
safety: Safety,
412426
args: TokenStream,
413427
body: TokenStream,
414428
rules: &[MacroRule],
@@ -430,13 +444,26 @@ fn expand_macro_attr(
430444
// Track nothing for the best performance.
431445
match try_match_macro_attr(psess, name, &args, &body, rules, &mut NoopTracker) {
432446
Ok((i, rule, named_matches)) => {
433-
let MacroRule::Attr { rhs, .. } = rule else {
447+
let MacroRule::Attr { rhs, unsafe_rule, .. } = rule else {
434448
panic!("try_macro_match_attr returned non-attr rule");
435449
};
436450
let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else {
437451
cx.dcx().span_bug(sp, "malformed macro rhs");
438452
};
439453

454+
match (safety, unsafe_rule) {
455+
(Safety::Default, false) | (Safety::Unsafe(_), true) => {}
456+
(Safety::Default, true) => {
457+
cx.dcx().span_err(sp, "unsafe attribute invocation requires `unsafe`");
458+
}
459+
(Safety::Unsafe(span), false) => {
460+
cx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute invocation");
461+
}
462+
(Safety::Safe(span), _) => {
463+
cx.dcx().span_bug(span, "unexpected `safe` keyword");
464+
}
465+
}
466+
440467
let id = cx.current_expansion.id;
441468
let tts = transcribe(psess, &named_matches, rhs, *rhs_span, transparency, id)
442469
.map_err(|e| e.emit())?;
@@ -682,6 +709,11 @@ pub fn compile_declarative_macro(
682709
let mut rules = Vec::new();
683710

684711
while p.token != token::Eof {
712+
let unsafe_rule = p.eat_keyword_noexpect(kw::Unsafe);
713+
let unsafe_keyword_span = p.prev_token.span;
714+
if unsafe_rule && let Some(guar) = check_no_eof(sess, &p, "expected `attr`") {
715+
return dummy_syn_ext(guar);
716+
}
685717
let (args, is_derive) = if p.eat_keyword_noexpect(sym::attr) {
686718
kinds |= MacroKinds::ATTR;
687719
if !features.macro_attr() {
@@ -706,6 +738,10 @@ pub fn compile_declarative_macro(
706738
feature_err(sess, sym::macro_attr, span, "`macro_rules!` derives are unstable")
707739
.emit();
708740
}
741+
if unsafe_rule {
742+
sess.dcx()
743+
.span_err(unsafe_keyword_span, "`unsafe` is only supported on `attr` rules");
744+
}
709745
if let Some(guar) = check_no_eof(sess, &p, "expected `()` after `derive`") {
710746
return dummy_syn_ext(guar);
711747
}
@@ -731,6 +767,10 @@ pub fn compile_declarative_macro(
731767
(None, true)
732768
} else {
733769
kinds |= MacroKinds::BANG;
770+
if unsafe_rule {
771+
sess.dcx()
772+
.span_err(unsafe_keyword_span, "`unsafe` is only supported on `attr` rules");
773+
}
734774
(None, false)
735775
};
736776
let lhs_tt = p.parse_token_tree();
@@ -761,7 +801,7 @@ pub fn compile_declarative_macro(
761801
};
762802
let args = mbe::macro_parser::compute_locs(&delimited.tts);
763803
let body_span = lhs_span;
764-
rules.push(MacroRule::Attr { args, args_span, body: lhs, body_span, rhs });
804+
rules.push(MacroRule::Attr { unsafe_rule, args, args_span, body: lhs, body_span, rhs });
765805
} else if is_derive {
766806
rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs });
767807
} else {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#[unsafe(unsafe(no_mangle))]
22
//~^ ERROR expected identifier, found keyword `unsafe`
33
//~| ERROR cannot find attribute `r#unsafe` in this scope
4-
//~| ERROR `r#unsafe` is not an unsafe attribute
4+
//~| ERROR unnecessary `unsafe`
55
fn a() {}
66

77
fn main() {}

tests/ui/attributes/unsafe/double-unsafe-attributes.stderr

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@ help: escape `unsafe` to use it as an identifier
99
LL | #[unsafe(r#unsafe(no_mangle))]
1010
| ++
1111

12-
error: `r#unsafe` is not an unsafe attribute
12+
error: unnecessary `unsafe` on safe attribute
1313
--> $DIR/double-unsafe-attributes.rs:1:3
1414
|
1515
LL | #[unsafe(unsafe(no_mangle))]
16-
| ^^^^^^ this is not an unsafe attribute
17-
|
18-
= note: extraneous unsafe is not allowed in attributes
16+
| ^^^^^^
1917

2018
error: cannot find attribute `r#unsafe` in this scope
2119
--> $DIR/double-unsafe-attributes.rs:1:10

tests/ui/attributes/unsafe/unsafe-safe-attribute_diagnostic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#[unsafe(diagnostic::on_unimplemented( //~ ERROR: is not an unsafe attribute
1+
#[unsafe(diagnostic::on_unimplemented( //~ ERROR: unnecessary `unsafe`
22
message = "testing",
33
))]
44
trait Foo {}
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
error: `diagnostic::on_unimplemented` is not an unsafe attribute
1+
error: unnecessary `unsafe` on safe attribute
22
--> $DIR/unsafe-safe-attribute_diagnostic.rs:1:3
33
|
44
LL | #[unsafe(diagnostic::on_unimplemented(
5-
| ^^^^^^ this is not an unsafe attribute
6-
|
7-
= note: extraneous unsafe is not allowed in attributes
5+
| ^^^^^^
86

97
error: aborting due to 1 previous error
108

0 commit comments

Comments
 (0)