Skip to content

Commit a7170b0

Browse files
committed
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
2 parents 36b6687 + 69035f2 commit a7170b0

File tree

10 files changed

+141
-19
lines changed

10 files changed

+141
-19
lines changed

src/librustc_plugin/registry.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,16 @@ impl<'a> Registry<'a> {
106106
expander,
107107
def_info: _,
108108
allow_internal_unstable,
109-
allow_internal_unsafe
109+
allow_internal_unsafe,
110+
unstable_feature
110111
} => {
111112
let nid = ast::CRATE_NODE_ID;
112113
NormalTT {
113114
expander,
114115
def_info: Some((nid, self.krate_span)),
115116
allow_internal_unstable,
116-
allow_internal_unsafe
117+
allow_internal_unsafe,
118+
unstable_feature
117119
}
118120
}
119121
IdentTT(ext, _, allow_internal_unstable) => {
@@ -149,6 +151,7 @@ impl<'a> Registry<'a> {
149151
def_info: None,
150152
allow_internal_unstable: false,
151153
allow_internal_unsafe: false,
154+
unstable_feature: None,
152155
});
153156
}
154157

src/libsyntax/ext/base.rs

+4
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,8 @@ pub enum SyntaxExtension {
555555
/// Whether the contents of the macro can use `unsafe`
556556
/// without triggering the `unsafe_code` lint.
557557
allow_internal_unsafe: bool,
558+
/// The macro's feature name if it is unstable, and the stability feature
559+
unstable_feature: Option<(Symbol, u32)>,
558560
},
559561

560562
/// A function-like syntax extension that has an extra ident before
@@ -670,6 +672,7 @@ pub struct ExpansionData {
670672
pub depth: usize,
671673
pub module: Rc<ModuleData>,
672674
pub directory_ownership: DirectoryOwnership,
675+
pub crate_span: Option<Span>,
673676
}
674677

675678
/// One of these is made during expansion and incrementally updated as we go;
@@ -701,6 +704,7 @@ impl<'a> ExtCtxt<'a> {
701704
depth: 0,
702705
module: Rc::new(ModuleData { mod_path: Vec::new(), directory: PathBuf::new() }),
703706
directory_ownership: DirectoryOwnership::Owned { relative: None },
707+
crate_span: None,
704708
},
705709
expansions: HashMap::new(),
706710
}

src/libsyntax/ext/expand.rs

+40-16
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use ext::base::*;
1818
use ext::derive::{add_derived_markers, collect_derives};
1919
use ext::hygiene::{Mark, SyntaxContext};
2020
use ext::placeholders::{placeholder, PlaceholderExpander};
21-
use feature_gate::{self, Features, is_builtin_attr};
21+
use feature_gate::{self, Features, GateIssue, is_builtin_attr, emit_feature_err};
2222
use fold;
2323
use fold::*;
2424
use parse::{DirectoryOwnership, PResult};
@@ -229,6 +229,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
229229
module.directory.pop();
230230
self.cx.root_path = module.directory.clone();
231231
self.cx.current_expansion.module = Rc::new(module);
232+
self.cx.current_expansion.crate_span = Some(krate.span);
232233

233234
let orig_mod_span = krate.module.inner;
234235

@@ -533,11 +534,36 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
533534
let path = &mac.node.path;
534535

535536
let ident = ident.unwrap_or_else(|| keywords::Invalid.ident());
536-
let validate_and_set_expn_info = |def_site_span,
537+
let validate_and_set_expn_info = |this: &mut Self, // arg instead of capture
538+
def_site_span: Option<Span>,
537539
allow_internal_unstable,
538-
allow_internal_unsafe| {
540+
allow_internal_unsafe,
541+
// can't infer this type
542+
unstable_feature: Option<(Symbol, u32)>| {
543+
544+
// feature-gate the macro invocation
545+
if let Some((feature, issue)) = unstable_feature {
546+
let crate_span = this.cx.current_expansion.crate_span.unwrap();
547+
// don't stability-check macros in the same crate
548+
// (the only time this is null is for syntax extensions registered as macros)
549+
if def_site_span.map_or(false, |def_span| !crate_span.contains(def_span))
550+
&& !span.allows_unstable() && this.cx.ecfg.features.map_or(true, |feats| {
551+
// macro features will count as lib features
552+
!feats.declared_lib_features.iter().any(|&(feat, _)| feat == feature)
553+
}) {
554+
let explain = format!("macro {}! is unstable", path);
555+
emit_feature_err(this.cx.parse_sess, &*feature.as_str(), span,
556+
GateIssue::Library(Some(issue)), &explain);
557+
this.cx.trace_macros_diag();
558+
return Err(kind.dummy(span));
559+
}
560+
}
561+
539562
if ident.name != keywords::Invalid.name() {
540-
return Err(format!("macro {}! expects no ident argument, given '{}'", path, ident));
563+
let msg = format!("macro {}! expects no ident argument, given '{}'", path, ident);
564+
this.cx.span_err(path.span, &msg);
565+
this.cx.trace_macros_diag();
566+
return Err(kind.dummy(span));
541567
}
542568
mark.set_expn_info(ExpnInfo {
543569
call_site: span,
@@ -553,11 +579,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
553579

554580
let opt_expanded = match *ext {
555581
DeclMacro(ref expand, def_span) => {
556-
if let Err(msg) = validate_and_set_expn_info(def_span.map(|(_, s)| s),
557-
false, false) {
558-
self.cx.span_err(path.span, &msg);
559-
self.cx.trace_macros_diag();
560-
kind.dummy(span)
582+
if let Err(dummy_span) = validate_and_set_expn_info(self, def_span.map(|(_, s)| s),
583+
false, false, None) {
584+
dummy_span
561585
} else {
562586
kind.make_from(expand.expand(self.cx, span, mac.node.stream()))
563587
}
@@ -567,14 +591,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
567591
ref expander,
568592
def_info,
569593
allow_internal_unstable,
570-
allow_internal_unsafe
594+
allow_internal_unsafe,
595+
unstable_feature,
571596
} => {
572-
if let Err(msg) = validate_and_set_expn_info(def_info.map(|(_, s)| s),
573-
allow_internal_unstable,
574-
allow_internal_unsafe) {
575-
self.cx.span_err(path.span, &msg);
576-
self.cx.trace_macros_diag();
577-
kind.dummy(span)
597+
if let Err(dummy_span) = validate_and_set_expn_info(self, def_info.map(|(_, s)| s),
598+
allow_internal_unstable,
599+
allow_internal_unsafe,
600+
unstable_feature) {
601+
dummy_span
578602
} else {
579603
kind.make_from(expander.expand(self.cx, span, mac.node.stream()))
580604
}

src/libsyntax/ext/tt/macro_rules.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -283,11 +283,22 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item) -> Syntax
283283
if body.legacy {
284284
let allow_internal_unstable = attr::contains_name(&def.attrs, "allow_internal_unstable");
285285
let allow_internal_unsafe = attr::contains_name(&def.attrs, "allow_internal_unsafe");
286+
287+
let unstable_feature = attr::find_stability(&sess.span_diagnostic,
288+
&def.attrs, def.span).and_then(|stability| {
289+
if let attr::StabilityLevel::Unstable { issue, .. } = stability.level {
290+
Some((stability.feature, issue))
291+
} else {
292+
None
293+
}
294+
});
295+
286296
NormalTT {
287297
expander,
288298
def_info: Some((def.id, def.span)),
289299
allow_internal_unstable,
290-
allow_internal_unsafe
300+
allow_internal_unsafe,
301+
unstable_feature
291302
}
292303
} else {
293304
SyntaxExtension::DeclMacro(expander, Some((def.id, def.span)))

src/libsyntax_ext/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver,
6767
def_info: None,
6868
allow_internal_unstable: false,
6969
allow_internal_unsafe: false,
70+
unstable_feature: None,
7071
});
7172
)* }
7273
}
@@ -120,6 +121,7 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver,
120121
def_info: None,
121122
allow_internal_unstable: true,
122123
allow_internal_unsafe: false,
124+
unstable_feature: None
123125
});
124126

125127
for (name, ext) in user_exts {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(staged_api)]
12+
#![stable(feature = "unit_test", since = "0.0.0")]
13+
14+
#[unstable(feature = "unstable_macros", issue = "0")]
15+
#[macro_export]
16+
macro_rules! unstable_macro{ () => () }
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:unstable-macros.rs
12+
13+
#![feature(staged_api)]
14+
#[macro_use] extern crate unstable_macros;
15+
16+
#[unstable(feature = "local_unstable", issue = "0")]
17+
macro_rules! local_unstable { () => () }
18+
19+
fn main() {
20+
local_unstable!();
21+
unstable_macro!(); //~ ERROR: macro unstable_macro! is unstable
22+
}

src/test/run-pass-fulldeps/auxiliary/plugin_args.rs

+1
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,6 @@ pub fn plugin_registrar(reg: &mut Registry) {
5353
def_info: None,
5454
allow_internal_unstable: false,
5555
allow_internal_unsafe: false,
56+
unstable_feature: None,
5657
});
5758
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(staged_api)]
12+
#![stable(feature = "unit_test", since = "0.0.0")]
13+
14+
#[unstable(feature = "unstable_macros", issue = "0")]
15+
#[macro_export]
16+
macro_rules! unstable_macro{ () => () }

src/test/run-pass/macro-stability.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:unstable-macros.rs
12+
13+
#![feature(unstable_macros)]
14+
15+
#[macro_use] extern crate unstable_macros;
16+
17+
#[unstable(feature = "local_unstable", issue = "0")]
18+
macro_rules! local_unstable { () => () }
19+
20+
fn main() {
21+
unstable_macro!();
22+
local_unstable!();
23+
}

0 commit comments

Comments
 (0)