Skip to content

Commit 72f3e7d

Browse files
committed
Auto merge of #48524 - abonander:check-macro-stability, r=<try>
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 3eeb5a6 + 52c52bd commit 72f3e7d

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
@@ -554,6 +554,8 @@ pub enum SyntaxExtension {
554554
/// Whether the contents of the macro can use `unsafe`
555555
/// without triggering the `unsafe_code` lint.
556556
allow_internal_unsafe: bool,
557+
/// The macro's feature name if it is unstable, and the stability feature
558+
unstable_feature: Option<(Symbol, u32)>,
557559
},
558560

559561
/// A function-like syntax extension that has an extra ident before
@@ -669,6 +671,7 @@ pub struct ExpansionData {
669671
pub depth: usize,
670672
pub module: Rc<ModuleData>,
671673
pub directory_ownership: DirectoryOwnership,
674+
pub crate_span: Option<Span>,
672675
}
673676

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

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
@@ -283,11 +283,22 @@ pub fn compile(sess: &ParseSess, features: &RefCell<Features>, def: &ast::Item)
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
@@ -66,6 +66,7 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver,
6666
def_info: None,
6767
allow_internal_unstable: false,
6868
allow_internal_unsafe: false,
69+
unstable_feature: None,
6970
});
7071
)* }
7172
}
@@ -119,6 +120,7 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver,
119120
def_info: None,
120121
allow_internal_unstable: true,
121122
allow_internal_unsafe: false,
123+
unstable_feature: None
122124
});
123125

124126
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)