Skip to content

Commit 69035f2

Browse files
committed
check stability of macro invocations
1 parent 4a72063 commit 69035f2

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

@@ -531,11 +532,36 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
531532
let path = &mac.node.path;
532533

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

552578
let opt_expanded = match *ext {
553579
DeclMacro(ref expand, def_span) => {
554-
if let Err(msg) = validate_and_set_expn_info(def_span.map(|(_, s)| s),
555-
false, false) {
556-
self.cx.span_err(path.span, &msg);
557-
self.cx.trace_macros_diag();
558-
kind.dummy(span)
580+
if let Err(dummy_span) = validate_and_set_expn_info(self, def_span.map(|(_, s)| s),
581+
false, false, None) {
582+
dummy_span
559583
} else {
560584
kind.make_from(expand.expand(self.cx, span, mac.node.stream()))
561585
}
@@ -565,14 +589,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
565589
ref expander,
566590
def_info,
567591
allow_internal_unstable,
568-
allow_internal_unsafe
592+
allow_internal_unsafe,
593+
unstable_feature,
569594
} => {
570-
if let Err(msg) = validate_and_set_expn_info(def_info.map(|(_, s)| s),
571-
allow_internal_unstable,
572-
allow_internal_unsafe) {
573-
self.cx.span_err(path.span, &msg);
574-
self.cx.trace_macros_diag();
575-
kind.dummy(span)
595+
if let Err(dummy_span) = validate_and_set_expn_info(self, def_info.map(|(_, s)| s),
596+
allow_internal_unstable,
597+
allow_internal_unsafe,
598+
unstable_feature) {
599+
dummy_span
576600
} else {
577601
kind.make_from(expander.expand(self.cx, span, mac.node.stream()))
578602
}

src/libsyntax/ext/tt/macro_rules.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -284,11 +284,22 @@ pub fn compile(sess: &ParseSess, features: &RefCell<Features>, def: &ast::Item)
284284
if body.legacy {
285285
let allow_internal_unstable = attr::contains_name(&def.attrs, "allow_internal_unstable");
286286
let allow_internal_unsafe = attr::contains_name(&def.attrs, "allow_internal_unsafe");
287+
288+
let unstable_feature = attr::find_stability(&sess.span_diagnostic,
289+
&def.attrs, def.span).and_then(|stability| {
290+
if let attr::StabilityLevel::Unstable { issue, .. } = stability.level {
291+
Some((stability.feature, issue))
292+
} else {
293+
None
294+
}
295+
});
296+
287297
NormalTT {
288298
expander,
289299
def_info: Some((def.id, def.span)),
290300
allow_internal_unstable,
291-
allow_internal_unsafe
301+
allow_internal_unsafe,
302+
unstable_feature
292303
}
293304
} else {
294305
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)