diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 421a81c0d23..adf7d901be9 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -404,6 +404,7 @@ impl<'a> LoweringContext<'a> { format: codemap::CompilerDesugaring(Symbol::intern(reason)), span: Some(span), allow_internal_unstable: true, + allow_internal_unsafe: false, }, }); span.ctxt = SyntaxContext::empty().apply_mark(mark); diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs index 676c3c51ea2..78b07a33389 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -79,6 +79,7 @@ impl<'a> Folder for ExpandAllocatorDirectives<'a> { format: MacroAttribute(Symbol::intern(name)), span: None, allow_internal_unstable: true, + allow_internal_unsafe: false, } }); let span = Span { diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 88432e64290..7767cf43403 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -195,12 +195,23 @@ impl LintPass for UnsafeCode { } } +impl UnsafeCode { + fn report_unsafe(&self, cx: &LateContext, span: Span, desc: &'static str) { + // This comes from a macro that has #[allow_internal_unsafe]. + if span.allows_unsafe() { + return; + } + + cx.span_lint(UNSAFE_CODE, span, desc); + } +} + impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) { if let hir::ExprBlock(ref blk) = e.node { // Don't warn about generated blocks, that'll just pollute the output. if blk.rules == hir::UnsafeBlock(hir::UserProvided) { - cx.span_lint(UNSAFE_CODE, blk.span, "usage of an `unsafe` block"); + self.report_unsafe(cx, blk.span, "usage of an `unsafe` block"); } } } @@ -208,11 +219,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { match it.node { hir::ItemTrait(hir::Unsafety::Unsafe, ..) => { - cx.span_lint(UNSAFE_CODE, it.span, "declaration of an `unsafe` trait") + self.report_unsafe(cx, it.span, "declaration of an `unsafe` trait") } hir::ItemImpl(hir::Unsafety::Unsafe, ..) => { - cx.span_lint(UNSAFE_CODE, it.span, "implementation of an `unsafe` trait") + self.report_unsafe(cx, it.span, "implementation of an `unsafe` trait") } _ => return, @@ -228,12 +239,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { _: ast::NodeId) { match fk { FnKind::ItemFn(_, _, hir::Unsafety::Unsafe, ..) => { - cx.span_lint(UNSAFE_CODE, span, "declaration of an `unsafe` function") + self.report_unsafe(cx, span, "declaration of an `unsafe` function") } FnKind::Method(_, sig, ..) => { if sig.unsafety == hir::Unsafety::Unsafe { - cx.span_lint(UNSAFE_CODE, span, "implementation of an `unsafe` method") + self.report_unsafe(cx, span, "implementation of an `unsafe` method") } } @@ -244,9 +255,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnsafeCode { fn check_trait_item(&mut self, cx: &LateContext, item: &hir::TraitItem) { if let hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Required(_)) = item.node { if sig.unsafety == hir::Unsafety::Unsafe { - cx.span_lint(UNSAFE_CODE, - item.span, - "declaration of an `unsafe` method") + self.report_unsafe(cx, item.span, "declaration of an `unsafe` method") } } } diff --git a/src/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs index 3027489d65b..aac21f2af0d 100644 --- a/src/librustc_plugin/registry.rs +++ b/src/librustc_plugin/registry.rs @@ -102,9 +102,19 @@ impl<'a> Registry<'a> { panic!("user-defined macros may not be named `macro_rules`"); } self.syntax_exts.push((name, match extension { - NormalTT(ext, _, allow_internal_unstable) => { + NormalTT { + expander, + def_info: _, + allow_internal_unstable, + allow_internal_unsafe + } => { let nid = ast::CRATE_NODE_ID; - NormalTT(ext, Some((nid, self.krate_span)), allow_internal_unstable) + NormalTT { + expander, + def_info: Some((nid, self.krate_span)), + allow_internal_unstable, + allow_internal_unsafe + } } IdentTT(ext, _, allow_internal_unstable) => { IdentTT(ext, Some(self.krate_span), allow_internal_unstable) @@ -134,8 +144,12 @@ impl<'a> Registry<'a> { /// It builds for you a `NormalTT` that calls `expander`, /// and also takes care of interning the macro's name. pub fn register_macro(&mut self, name: &str, expander: MacroExpanderFn) { - self.register_syntax_extension(Symbol::intern(name), - NormalTT(Box::new(expander), None, false)); + self.register_syntax_extension(Symbol::intern(name), NormalTT { + expander: Box::new(expander), + def_info: None, + allow_internal_unstable: false, + allow_internal_unsafe: false, + }); } /// Register a compiler lint pass. diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 98eaa056177..7572c4aa680 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -313,7 +313,7 @@ impl<'a> base::Resolver for Resolver<'a> { fn check_unused_macros(&self) { for did in self.unused_macros.iter() { let id_span = match *self.macro_map[did] { - SyntaxExtension::NormalTT(_, isp, _) => isp, + SyntaxExtension::NormalTT { def_info, .. } => def_info, SyntaxExtension::DeclMacro(.., osp) => osp, _ => None, }; diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 194d30e25d4..72b2552f64f 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -532,10 +532,16 @@ pub enum SyntaxExtension { /// A normal, function-like syntax extension. /// /// `bytes!` is a `NormalTT`. - /// - /// The `bool` dictates whether the contents of the macro can - /// directly use `#[unstable]` things (true == yes). - NormalTT(Box, Option<(ast::NodeId, Span)>, bool), + NormalTT { + expander: Box, + def_info: Option<(ast::NodeId, Span)>, + /// Whether the contents of the macro can + /// directly use `#[unstable]` things (true == yes). + allow_internal_unstable: bool, + /// Whether the contents of the macro can use `unsafe` + /// without triggering the `unsafe_code` lint. + allow_internal_unsafe: bool, + }, /// A function-like syntax extension that has an extra ident before /// the block. @@ -562,7 +568,7 @@ impl SyntaxExtension { pub fn kind(&self) -> MacroKind { match *self { SyntaxExtension::DeclMacro(..) | - SyntaxExtension::NormalTT(..) | + SyntaxExtension::NormalTT { .. } | SyntaxExtension::IdentTT(..) | SyntaxExtension::ProcMacro(..) => MacroKind::Bang, diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index e7c5d8278d9..38715f7275d 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -64,6 +64,7 @@ pub fn add_derived_markers(cx: &mut ExtCtxt, span: Span, traits: &[ast::Path] format: ExpnFormat::MacroAttribute(Symbol::intern(&pretty_name)), span: None, allow_internal_unstable: true, + allow_internal_unsafe: false, }, }); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 4843a66a750..9625602fa4a 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -411,6 +411,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { format: MacroAttribute(Symbol::intern(&format!("{}", attr.path))), span: None, allow_internal_unstable: false, + allow_internal_unsafe: false, } }); @@ -458,7 +459,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let path = &mac.node.path; let ident = ident.unwrap_or_else(|| keywords::Invalid.ident()); - let validate_and_set_expn_info = |def_site_span, allow_internal_unstable| { + let validate_and_set_expn_info = |def_site_span, + allow_internal_unstable, + allow_internal_unsafe| { if ident.name != keywords::Invalid.name() { return Err(format!("macro {}! expects no ident argument, given '{}'", path, ident)); } @@ -467,7 +470,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { callee: NameAndSpan { format: MacroBang(Symbol::intern(&format!("{}", path))), span: def_site_span, - allow_internal_unstable: allow_internal_unstable, + allow_internal_unstable, + allow_internal_unsafe, }, }); Ok(()) @@ -476,20 +480,26 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let opt_expanded = match *ext { DeclMacro(ref expand, def_span) => { if let Err(msg) = validate_and_set_expn_info(def_span.map(|(_, s)| s), - false) { + false, false) { self.cx.span_err(path.span, &msg); return kind.dummy(span); } kind.make_from(expand.expand(self.cx, span, mac.node.stream())) } - NormalTT(ref expandfun, def_info, allow_internal_unstable) => { + NormalTT { + ref expander, + def_info, + allow_internal_unstable, + allow_internal_unsafe + } => { if let Err(msg) = validate_and_set_expn_info(def_info.map(|(_, s)| s), - allow_internal_unstable) { + allow_internal_unstable, + allow_internal_unsafe) { self.cx.span_err(path.span, &msg); return kind.dummy(span); } - kind.make_from(expandfun.expand(self.cx, span, mac.node.stream())) + kind.make_from(expander.expand(self.cx, span, mac.node.stream())) } IdentTT(ref expander, tt_span, allow_internal_unstable) => { @@ -504,7 +514,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { callee: NameAndSpan { format: MacroBang(Symbol::intern(&format!("{}", path))), span: tt_span, - allow_internal_unstable: allow_internal_unstable, + allow_internal_unstable, + allow_internal_unsafe: false, } }); @@ -540,6 +551,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { span: None, // FIXME probably want to follow macro_rules macros here. allow_internal_unstable: false, + allow_internal_unsafe: false, }, }); @@ -578,6 +590,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { format: MacroAttribute(pretty_name), span: None, allow_internal_unstable: false, + allow_internal_unsafe: false, } }; diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 80b6794d1e3..7b3fe2bd993 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -269,7 +269,7 @@ pub fn compile(sess: &ParseSess, features: &RefCell, def: &ast::Item) valid &= check_lhs_no_empty_seq(sess, &[lhs.clone()]) } - let exp: Box<_> = Box::new(MacroRulesMacroExpander { + let expander: Box<_> = Box::new(MacroRulesMacroExpander { name: def.ident, lhses: lhses, rhses: rhses, @@ -278,9 +278,15 @@ pub fn compile(sess: &ParseSess, features: &RefCell, def: &ast::Item) if body.legacy { let allow_internal_unstable = attr::contains_name(&def.attrs, "allow_internal_unstable"); - NormalTT(exp, Some((def.id, def.span)), allow_internal_unstable) + let allow_internal_unsafe = attr::contains_name(&def.attrs, "allow_internal_unsafe"); + NormalTT { + expander, + def_info: Some((def.id, def.span)), + allow_internal_unstable, + allow_internal_unsafe + } } else { - SyntaxExtension::DeclMacro(exp, Some((def.id, def.span))) + SyntaxExtension::DeclMacro(expander, Some((def.id, def.span))) } } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index aeb574bc3af..46ee126b9d9 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -194,6 +194,14 @@ declare_features! ( // rustc internal (active, allow_internal_unstable, "1.0.0", None), + // Allows the use of #[allow_internal_unsafe]. This is an + // attribute on macro_rules! and can't use the attribute handling + // below (it has to be checked before expansion possibly makes + // macros disappear). + // + // rustc internal + (active, allow_internal_unsafe, "1.0.0", None), + // #23121. Array patterns have some hazards yet. (active, slice_patterns, "1.0.0", Some(23121)), @@ -735,6 +743,11 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG EXPLAIN_ALLOW_INTERNAL_UNSTABLE, cfg_fn!(allow_internal_unstable))), + ("allow_internal_unsafe", Normal, Gated(Stability::Unstable, + "allow_internal_unsafe", + EXPLAIN_ALLOW_INTERNAL_UNSAFE, + cfg_fn!(allow_internal_unsafe))), + ("fundamental", Whitelisted, Gated(Stability::Unstable, "fundamental", "the `#[fundamental]` attribute \ @@ -1045,6 +1058,8 @@ pub const EXPLAIN_TRACE_MACROS: &'static str = "`trace_macros` is not stable enough for use and is subject to change"; pub const EXPLAIN_ALLOW_INTERNAL_UNSTABLE: &'static str = "allow_internal_unstable side-steps feature gating and stability checks"; +pub const EXPLAIN_ALLOW_INTERNAL_UNSAFE: &'static str = + "allow_internal_unsafe side-steps the unsafe_code lint"; pub const EXPLAIN_CUSTOM_DERIVE: &'static str = "`#[derive]` for custom traits is deprecated and will be removed in the future."; diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index d9ed96f293a..430976e7d3c 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -28,6 +28,7 @@ fn ignored_span(sp: Span) -> Span { format: MacroAttribute(Symbol::intern("std_inject")), span: None, allow_internal_unstable: true, + allow_internal_unsafe: false, } }); Span { ctxt: SyntaxContext::empty().apply_mark(mark), ..sp } diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 887479a2472..c05e166e013 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -291,6 +291,7 @@ fn generate_test_harness(sess: &ParseSess, format: MacroAttribute(Symbol::intern("test")), span: None, allow_internal_unstable: true, + allow_internal_unsafe: false, } }); diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index 5eab81dd28f..439538a8b5e 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -64,7 +64,12 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, macro_rules! register { ($( $name:ident: $f:expr, )*) => { $( register(Symbol::intern(stringify!($name)), - NormalTT(Box::new($f as MacroExpanderFn), None, false)); + NormalTT { + expander: Box::new($f as MacroExpanderFn), + def_info: None, + allow_internal_unstable: false, + allow_internal_unsafe: false, + }); )* } } @@ -112,7 +117,12 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, // format_args uses `unstable` things internally. register(Symbol::intern("format_args"), - NormalTT(Box::new(format::expand_format_args), None, true)); + NormalTT { + expander: Box::new(format::expand_format_args), + def_info: None, + allow_internal_unstable: true, + allow_internal_unsafe: false, + }); for (name, ext) in user_exts { register(name, ext); diff --git a/src/libsyntax_ext/proc_macro_registrar.rs b/src/libsyntax_ext/proc_macro_registrar.rs index ab6d73e5061..700386f68fe 100644 --- a/src/libsyntax_ext/proc_macro_registrar.rs +++ b/src/libsyntax_ext/proc_macro_registrar.rs @@ -368,6 +368,7 @@ fn mk_registrar(cx: &mut ExtCtxt, format: MacroAttribute(Symbol::intern("proc_macro")), span: None, allow_internal_unstable: true, + allow_internal_unsafe: false, } }); let span = Span { ctxt: SyntaxContext::empty().apply_mark(mark), ..DUMMY_SP }; diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 804b91ab09e..514cc26666e 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -310,6 +310,9 @@ pub struct NameAndSpan { /// features internally without forcing the whole crate to opt-in /// to them. pub allow_internal_unstable: bool, + /// Whether the macro is allowed to use `unsafe` internally + /// even if the user crate has `#![forbid(unsafe_code)]`. + pub allow_internal_unsafe: bool, /// The span of the macro definition itself. The macro may not /// have a sensible definition span (e.g. something defined /// completely inside libsyntax) in which case this is None. diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 7006f45455e..e162bc26412 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -153,6 +153,16 @@ impl Span { } } + /// Check if a span is "internal" to a macro in which `unsafe` + /// can be used without triggering the `unsafe_code` lint + // (that is, a macro marked with `#[allow_internal_unsafe]`). + pub fn allows_unsafe(&self) -> bool { + match self.ctxt.outer().expn_info() { + Some(info) => info.callee.allow_internal_unsafe, + None => false, + } + } + pub fn macro_backtrace(mut self) -> Vec { let mut prev_span = DUMMY_SP; let mut result = vec![]; diff --git a/src/test/compile-fail/feature-gate-allow-internal-unsafe-nested-macro.rs b/src/test/compile-fail/feature-gate-allow-internal-unsafe-nested-macro.rs new file mode 100644 index 00000000000..590dc619f2f --- /dev/null +++ b/src/test/compile-fail/feature-gate-allow-internal-unsafe-nested-macro.rs @@ -0,0 +1,27 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// gate-test-allow_internal_unsafe + +#![allow(unused_macros)] + +macro_rules! bar { + () => { + // more layers don't help: + #[allow_internal_unsafe] //~ ERROR allow_internal_unsafe side-steps + macro_rules! baz { + () => {} + } + } +} + +bar!(); + +fn main() {} diff --git a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs index 134e36c587b..8da2ae8b29a 100644 --- a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs +++ b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs @@ -48,5 +48,10 @@ impl TTMacroExpander for Expander { pub fn plugin_registrar(reg: &mut Registry) { let args = reg.args().to_owned(); reg.register_syntax_extension(Symbol::intern("plugin_args"), - NormalTT(Box::new(Expander { args: args, }), None, false)); + NormalTT { + expander: Box::new(Expander { args: args, }), + def_info: None, + allow_internal_unstable: false, + allow_internal_unsafe: false, + }); }