Auto merge of #48524 - abonander:check-macro-stability, r=petrochenkov

check stability of macro invocations

I haven't implemented tests yet but this should be a pretty solid prototype. I think as-implemented it will also stability-check macro invocations in the same crate, dunno if we want that or not.

I don't know if we want this to go through `rustc::middle::stability` or not, considering the information there wouldn't be available at the time of macro expansion (even for external crates, right?).

r? @nrc
closes #34079
cc @petrochenkov @durka @jseyfried #38356
This commit is contained in:
bors 2018-03-16 02:46:23 +00:00
commit a7170b0412
10 changed files with 141 additions and 19 deletions

View File

@ -106,14 +106,16 @@ pub fn register_syntax_extension(&mut self, name: ast::Name, extension: SyntaxEx
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 @@ pub fn register_macro(&mut self, name: &str, expander: MacroExpanderFn) {
def_info: None,
allow_internal_unstable: false,
allow_internal_unsafe: false,
unstable_feature: None,
});
}

View File

@ -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<ModuleData>,
pub directory_ownership: DirectoryOwnership,
pub crate_span: Option<Span>,
}
/// One of these is made during expansion and incrementally updated as we go;
@ -701,6 +704,7 @@ pub fn new(parse_sess: &'a parse::ParseSess,
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(),
}

View File

@ -18,7 +18,7 @@
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 @@ pub fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate {
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;
@ -533,11 +534,36 @@ fn expand_bang_invoc(&mut self,
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<Span>,
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,
@ -553,11 +579,9 @@ fn expand_bang_invoc(&mut self,
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()))
}
@ -567,14 +591,14 @@ fn expand_bang_invoc(&mut self,
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()))
}

View File

@ -283,11 +283,22 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> Syntax
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)))

View File

@ -67,6 +67,7 @@ macro_rules! register {
def_info: None,
allow_internal_unstable: false,
allow_internal_unsafe: false,
unstable_feature: None,
});
)* }
}
@ -120,6 +121,7 @@ macro_rules! register {
def_info: None,
allow_internal_unstable: true,
allow_internal_unsafe: false,
unstable_feature: None
});
for (name, ext) in user_exts {

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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{ () => () }

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
}

View File

@ -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,
});
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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{ () => () }

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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!();
}