Skip to content

Commit

Permalink
Auto merge of #48524 - abonander:check-macro-stability, r=petrochenkov
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bors committed Mar 16, 2018
2 parents 36b6687 + 69035f2 commit a7170b0
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 19 deletions.
7 changes: 5 additions & 2 deletions src/librustc_plugin/registry.rs
Expand Up @@ -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) => {
Expand Down Expand Up @@ -149,6 +151,7 @@ impl<'a> Registry<'a> {
def_info: None,
allow_internal_unstable: false,
allow_internal_unsafe: false,
unstable_feature: None,
});
}

Expand Down
4 changes: 4 additions & 0 deletions src/libsyntax/ext/base.rs
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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(),
}
Expand Down
56 changes: 40 additions & 16 deletions src/libsyntax/ext/expand.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -533,11 +534,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<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,
Expand All @@ -553,11 +579,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()))
}
Expand All @@ -567,14 +591,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()))
}
Expand Down
13 changes: 12 additions & 1 deletion src/libsyntax/ext/tt/macro_rules.rs
Expand Up @@ -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)))
Expand Down
2 changes: 2 additions & 0 deletions src/libsyntax_ext/lib.rs
Expand Up @@ -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,
});
)* }
}
Expand Down Expand Up @@ -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 {
Expand Down
16 changes: 16 additions & 0 deletions 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 <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 changes: 22 additions & 0 deletions 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 <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
}
1 change: 1 addition & 0 deletions src/test/run-pass-fulldeps/auxiliary/plugin_args.rs
Expand Up @@ -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 changes: 16 additions & 0 deletions 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 <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 changes: 23 additions & 0 deletions 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 <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!();
}

0 comments on commit a7170b0

Please sign in to comment.