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:
commit
a7170b0412
@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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(),
|
||||
}
|
||||
|
@ -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()))
|
||||
}
|
||||
|
@ -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)))
|
||||
|
@ -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 {
|
||||
|
16
src/test/compile-fail/auxiliary/unstable-macros.rs
Normal file
16
src/test/compile-fail/auxiliary/unstable-macros.rs
Normal 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{ () => () }
|
22
src/test/compile-fail/macro-stability.rs
Normal file
22
src/test/compile-fail/macro-stability.rs
Normal 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
|
||||
}
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
16
src/test/run-pass/auxiliary/unstable-macros.rs
Normal file
16
src/test/run-pass/auxiliary/unstable-macros.rs
Normal 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{ () => () }
|
23
src/test/run-pass/macro-stability.rs
Normal file
23
src/test/run-pass/macro-stability.rs
Normal 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!();
|
||||
}
|
Loading…
Reference in New Issue
Block a user