From 69035f20b92870a7ad5dbc22c65aee971d8f8698 Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Sat, 24 Feb 2018 19:11:06 -0800 Subject: [PATCH] check stability of macro invocations --- src/librustc_plugin/registry.rs | 7 ++- src/libsyntax/ext/base.rs | 4 ++ src/libsyntax/ext/expand.rs | 56 +++++++++++++------ src/libsyntax/ext/tt/macro_rules.rs | 13 ++++- src/libsyntax_ext/lib.rs | 2 + .../compile-fail/auxiliary/unstable-macros.rs | 16 ++++++ src/test/compile-fail/macro-stability.rs | 22 ++++++++ .../auxiliary/plugin_args.rs | 1 + .../run-pass/auxiliary/unstable-macros.rs | 16 ++++++ src/test/run-pass/macro-stability.rs | 23 ++++++++ 10 files changed, 141 insertions(+), 19 deletions(-) create mode 100644 src/test/compile-fail/auxiliary/unstable-macros.rs create mode 100644 src/test/compile-fail/macro-stability.rs create mode 100644 src/test/run-pass/auxiliary/unstable-macros.rs create mode 100644 src/test/run-pass/macro-stability.rs diff --git a/src/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs index 3f74093241d29..ebfd8785a0a0c 100644 --- a/src/librustc_plugin/registry.rs +++ b/src/librustc_plugin/registry.rs @@ -106,14 +106,16 @@ impl<'a> Registry<'a> { expander, def_info: _, allow_internal_unstable, - allow_internal_unsafe + allow_internal_unsafe, + unstable_feature } => { let nid = ast::CRATE_NODE_ID; NormalTT { expander, def_info: Some((nid, self.krate_span)), allow_internal_unstable, - allow_internal_unsafe + allow_internal_unsafe, + unstable_feature } } IdentTT(ext, _, allow_internal_unstable) => { @@ -149,6 +151,7 @@ impl<'a> Registry<'a> { def_info: None, allow_internal_unstable: false, allow_internal_unsafe: false, + unstable_feature: None, }); } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 7b333270d041c..23c42972912a1 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -555,6 +555,8 @@ pub enum SyntaxExtension { /// Whether the contents of the macro can use `unsafe` /// without triggering the `unsafe_code` lint. allow_internal_unsafe: bool, + /// The macro's feature name if it is unstable, and the stability feature + unstable_feature: Option<(Symbol, u32)>, }, /// A function-like syntax extension that has an extra ident before @@ -670,6 +672,7 @@ pub struct ExpansionData { pub depth: usize, pub module: Rc, pub directory_ownership: DirectoryOwnership, + pub crate_span: Option, } /// One of these is made during expansion and incrementally updated as we go; @@ -701,6 +704,7 @@ impl<'a> ExtCtxt<'a> { depth: 0, module: Rc::new(ModuleData { mod_path: Vec::new(), directory: PathBuf::new() }), directory_ownership: DirectoryOwnership::Owned { relative: None }, + crate_span: None, }, expansions: HashMap::new(), } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 377f47a3ea5a1..2cca37a3c9394 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -18,7 +18,7 @@ use ext::base::*; use ext::derive::{add_derived_markers, collect_derives}; use ext::hygiene::{Mark, SyntaxContext}; use ext::placeholders::{placeholder, PlaceholderExpander}; -use feature_gate::{self, Features, is_builtin_attr}; +use feature_gate::{self, Features, GateIssue, is_builtin_attr, emit_feature_err}; use fold; use fold::*; use parse::{DirectoryOwnership, PResult}; @@ -229,6 +229,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { module.directory.pop(); self.cx.root_path = module.directory.clone(); self.cx.current_expansion.module = Rc::new(module); + self.cx.current_expansion.crate_span = Some(krate.span); let orig_mod_span = krate.module.inner; @@ -531,11 +532,36 @@ 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, + let validate_and_set_expn_info = |this: &mut Self, // arg instead of capture + def_site_span: Option, allow_internal_unstable, - allow_internal_unsafe| { + allow_internal_unsafe, + // can't infer this type + unstable_feature: Option<(Symbol, u32)>| { + + // feature-gate the macro invocation + if let Some((feature, issue)) = unstable_feature { + let crate_span = this.cx.current_expansion.crate_span.unwrap(); + // don't stability-check macros in the same crate + // (the only time this is null is for syntax extensions registered as macros) + if def_site_span.map_or(false, |def_span| !crate_span.contains(def_span)) + && !span.allows_unstable() && this.cx.ecfg.features.map_or(true, |feats| { + // macro features will count as lib features + !feats.declared_lib_features.iter().any(|&(feat, _)| feat == feature) + }) { + let explain = format!("macro {}! is unstable", path); + emit_feature_err(this.cx.parse_sess, &*feature.as_str(), span, + GateIssue::Library(Some(issue)), &explain); + this.cx.trace_macros_diag(); + return Err(kind.dummy(span)); + } + } + if ident.name != keywords::Invalid.name() { - return Err(format!("macro {}! expects no ident argument, given '{}'", path, ident)); + let msg = format!("macro {}! expects no ident argument, given '{}'", path, ident); + this.cx.span_err(path.span, &msg); + this.cx.trace_macros_diag(); + return Err(kind.dummy(span)); } mark.set_expn_info(ExpnInfo { call_site: span, @@ -551,11 +577,9 @@ 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) { - self.cx.span_err(path.span, &msg); - self.cx.trace_macros_diag(); - kind.dummy(span) + if let Err(dummy_span) = validate_and_set_expn_info(self, def_span.map(|(_, s)| s), + false, false, None) { + dummy_span } else { kind.make_from(expand.expand(self.cx, span, mac.node.stream())) } @@ -565,14 +589,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> { ref expander, def_info, allow_internal_unstable, - allow_internal_unsafe + allow_internal_unsafe, + unstable_feature, } => { - if let Err(msg) = validate_and_set_expn_info(def_info.map(|(_, s)| s), - allow_internal_unstable, - allow_internal_unsafe) { - self.cx.span_err(path.span, &msg); - self.cx.trace_macros_diag(); - kind.dummy(span) + if let Err(dummy_span) = validate_and_set_expn_info(self, def_info.map(|(_, s)| s), + allow_internal_unstable, + allow_internal_unsafe, + unstable_feature) { + dummy_span } else { kind.make_from(expander.expand(self.cx, span, mac.node.stream())) } diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 728b3e4076d1b..3b0d1d498994a 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -284,11 +284,22 @@ 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"); let allow_internal_unsafe = attr::contains_name(&def.attrs, "allow_internal_unsafe"); + + let unstable_feature = attr::find_stability(&sess.span_diagnostic, + &def.attrs, def.span).and_then(|stability| { + if let attr::StabilityLevel::Unstable { issue, .. } = stability.level { + Some((stability.feature, issue)) + } else { + None + } + }); + NormalTT { expander, def_info: Some((def.id, def.span)), allow_internal_unstable, - allow_internal_unsafe + allow_internal_unsafe, + unstable_feature } } else { SyntaxExtension::DeclMacro(expander, Some((def.id, def.span))) diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index 772dec72ab98e..5b078535852f4 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -67,6 +67,7 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, def_info: None, allow_internal_unstable: false, allow_internal_unsafe: false, + unstable_feature: None, }); )* } } @@ -120,6 +121,7 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, def_info: None, allow_internal_unstable: true, allow_internal_unsafe: false, + unstable_feature: None }); for (name, ext) in user_exts { diff --git a/src/test/compile-fail/auxiliary/unstable-macros.rs b/src/test/compile-fail/auxiliary/unstable-macros.rs new file mode 100644 index 0000000000000..6462c11af481f --- /dev/null +++ b/src/test/compile-fail/auxiliary/unstable-macros.rs @@ -0,0 +1,16 @@ +// Copyright 2018 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. + +#![feature(staged_api)] +#![stable(feature = "unit_test", since = "0.0.0")] + +#[unstable(feature = "unstable_macros", issue = "0")] +#[macro_export] +macro_rules! unstable_macro{ () => () } diff --git a/src/test/compile-fail/macro-stability.rs b/src/test/compile-fail/macro-stability.rs new file mode 100644 index 0000000000000..a4b922c0fe19c --- /dev/null +++ b/src/test/compile-fail/macro-stability.rs @@ -0,0 +1,22 @@ +// Copyright 2018 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. + +// aux-build:unstable-macros.rs + +#![feature(staged_api)] +#[macro_use] extern crate unstable_macros; + +#[unstable(feature = "local_unstable", issue = "0")] +macro_rules! local_unstable { () => () } + +fn main() { + local_unstable!(); + unstable_macro!(); //~ ERROR: macro unstable_macro! is unstable +} diff --git a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs index 8da2ae8b29abe..231ed2898f1db 100644 --- a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs +++ b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs @@ -53,5 +53,6 @@ pub fn plugin_registrar(reg: &mut Registry) { def_info: None, allow_internal_unstable: false, allow_internal_unsafe: false, + unstable_feature: None, }); } diff --git a/src/test/run-pass/auxiliary/unstable-macros.rs b/src/test/run-pass/auxiliary/unstable-macros.rs new file mode 100644 index 0000000000000..6462c11af481f --- /dev/null +++ b/src/test/run-pass/auxiliary/unstable-macros.rs @@ -0,0 +1,16 @@ +// Copyright 2018 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. + +#![feature(staged_api)] +#![stable(feature = "unit_test", since = "0.0.0")] + +#[unstable(feature = "unstable_macros", issue = "0")] +#[macro_export] +macro_rules! unstable_macro{ () => () } diff --git a/src/test/run-pass/macro-stability.rs b/src/test/run-pass/macro-stability.rs new file mode 100644 index 0000000000000..9afcd51aa85af --- /dev/null +++ b/src/test/run-pass/macro-stability.rs @@ -0,0 +1,23 @@ +// Copyright 2018 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. + +// aux-build:unstable-macros.rs + +#![feature(unstable_macros)] + +#[macro_use] extern crate unstable_macros; + +#[unstable(feature = "local_unstable", issue = "0")] +macro_rules! local_unstable { () => () } + +fn main() { + unstable_macro!(); + local_unstable!(); +}