Skip to content

Commit 39d7402

Browse files
committed
auto merge of #20190 : cmr/rust/gate-macro-args, r=alexcrichton
Uses the same approach as #17286 (and subsequent changes making it more correct), where the visitor will skip any pieces of the AST that are from "foreign code", where the spans don't line up, indicating that that piece of code is due to a macro expansion. If this breaks your code, read the error message to determine which feature gate you should add to your crate. Closes #18102 [breaking-change]
2 parents c594959 + 41da99d commit 39d7402

File tree

6 files changed

+172
-78
lines changed

6 files changed

+172
-78
lines changed

src/librand/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,8 @@ pub trait Rng {
271271
/// let choices = [1i, 2, 4, 8, 16, 32];
272272
/// let mut rng = thread_rng();
273273
/// println!("{}", rng.choose(&choices));
274-
/// assert_eq!(rng.choose(choices[..0]), None);
274+
/// # // replace with slicing syntax when it's stable!
275+
/// assert_eq!(rng.choose(choices.slice_to(0)), None);
275276
/// ```
276277
fn choose<'a, T>(&mut self, values: &'a [T]) -> Option<&'a T> {
277278
if values.is_empty() {

src/librustc/lint/builtin.rs

+1-27
Original file line numberDiff line numberDiff line change
@@ -1682,33 +1682,7 @@ impl Stability {
16821682
}
16831683

16841684
fn is_internal(&self, cx: &Context, span: Span) -> bool {
1685-
// first, check if the given expression was generated by a macro or not
1686-
// we need to go back the expn_info tree to check only the arguments
1687-
// of the initial macro call, not the nested ones.
1688-
let mut expnid = span.expn_id;
1689-
let mut is_internal = false;
1690-
while cx.tcx.sess.codemap().with_expn_info(expnid, |expninfo| {
1691-
match expninfo {
1692-
Some(ref info) => {
1693-
// save the parent expn_id for next loop iteration
1694-
expnid = info.call_site.expn_id;
1695-
if info.callee.span.is_none() {
1696-
// it's a compiler built-in, we *really* don't want to mess with it
1697-
// so we skip it, unless it was called by a regular macro, in which case
1698-
// we will handle the caller macro next turn
1699-
is_internal = true;
1700-
true // continue looping
1701-
} else {
1702-
// was this expression from the current macro arguments ?
1703-
is_internal = !( span.lo > info.call_site.lo &&
1704-
span.hi < info.call_site.hi );
1705-
true // continue looping
1706-
}
1707-
},
1708-
_ => false // stop looping
1709-
}
1710-
}) { /* empty while loop body */ }
1711-
return is_internal;
1685+
cx.tcx.sess.codemap().span_is_internal(span)
17121686
}
17131687
}
17141688

src/librustc_driver/driver.rs

+25-15
Original file line numberDiff line numberDiff line change
@@ -178,21 +178,6 @@ pub fn phase_2_configure_and_expand(sess: &Session,
178178
*sess.crate_metadata.borrow_mut() =
179179
collect_crate_metadata(sess, krate.attrs[]);
180180

181-
time(time_passes, "gated feature checking", (), |_| {
182-
let (features, unknown_features) =
183-
syntax::feature_gate::check_crate(&sess.parse_sess.span_diagnostic, &krate);
184-
185-
for uf in unknown_features.iter() {
186-
sess.add_lint(lint::builtin::UNKNOWN_FEATURES,
187-
ast::CRATE_NODE_ID,
188-
*uf,
189-
"unknown feature".to_string());
190-
}
191-
192-
sess.abort_if_errors();
193-
*sess.features.borrow_mut() = features;
194-
});
195-
196181
time(time_passes, "recursion limit", (), |_| {
197182
middle::recursion_limit::update_recursion_limit(sess, &krate);
198183
});
@@ -205,6 +190,23 @@ pub fn phase_2_configure_and_expand(sess: &Session,
205190
//
206191
// baz! should not use this definition unless foo is enabled.
207192

193+
time(time_passes, "gated macro checking", (), |_| {
194+
let (features, unknown_features) =
195+
syntax::feature_gate::check_crate_macros(sess.codemap(),
196+
&sess.parse_sess.span_diagnostic,
197+
&krate);
198+
for uf in unknown_features.iter() {
199+
sess.add_lint(lint::builtin::UNKNOWN_FEATURES,
200+
ast::CRATE_NODE_ID,
201+
*uf,
202+
"unknown feature".to_string());
203+
}
204+
205+
// these need to be set "early" so that expansion sees `quote` if enabled.
206+
*sess.features.borrow_mut() = features;
207+
sess.abort_if_errors();
208+
});
209+
208210
krate = time(time_passes, "configuration 1", krate, |krate|
209211
syntax::config::strip_unconfigured_items(sess.diagnostic(), krate));
210212

@@ -289,6 +291,14 @@ pub fn phase_2_configure_and_expand(sess: &Session,
289291
}
290292
);
291293

294+
// Needs to go *after* expansion to be able to check the results of macro expansion.
295+
time(time_passes, "complete gated feature checking", (), |_| {
296+
syntax::feature_gate::check_crate(sess.codemap(),
297+
&sess.parse_sess.span_diagnostic,
298+
&krate);
299+
sess.abort_if_errors();
300+
});
301+
292302
// JBC: make CFG processing part of expansion to avoid this problem:
293303

294304
// strip again, in case expansion added anything with a #[cfg].

src/libsyntax/codemap.rs

+32
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,38 @@ impl CodeMap {
563563
ExpnId(i) => f(Some(&(*self.expansions.borrow())[i as uint]))
564564
}
565565
}
566+
567+
/// Check if a span is "internal" to a macro. This means that it is entirely generated by a
568+
/// macro expansion and contains no code that was passed in as an argument.
569+
pub fn span_is_internal(&self, span: Span) -> bool {
570+
// first, check if the given expression was generated by a macro or not
571+
// we need to go back the expn_info tree to check only the arguments
572+
// of the initial macro call, not the nested ones.
573+
let mut is_internal = false;
574+
let mut expnid = span.expn_id;
575+
while self.with_expn_info(expnid, |expninfo| {
576+
match expninfo {
577+
Some(ref info) => {
578+
// save the parent expn_id for next loop iteration
579+
expnid = info.call_site.expn_id;
580+
if info.callee.span.is_none() {
581+
// it's a compiler built-in, we *really* don't want to mess with it
582+
// so we skip it, unless it was called by a regular macro, in which case
583+
// we will handle the caller macro next turn
584+
is_internal = true;
585+
true // continue looping
586+
} else {
587+
// was this expression from the current macro arguments ?
588+
is_internal = !( span.lo > info.call_site.lo &&
589+
span.hi < info.call_site.hi );
590+
true // continue looping
591+
}
592+
},
593+
_ => false // stop looping
594+
}
595+
}) { /* empty while loop body */ }
596+
return is_internal;
597+
}
566598
}
567599

568600
#[cfg(test)]

src/libsyntax/feature_gate.rs

+88-35
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use ast::NodeId;
2424
use ast;
2525
use attr;
2626
use attr::AttrMetaMethods;
27-
use codemap::Span;
27+
use codemap::{CodeMap, Span};
2828
use diagnostic::SpanHandler;
2929
use visit;
3030
use visit::Visitor;
@@ -127,6 +127,7 @@ impl Features {
127127
struct Context<'a> {
128128
features: Vec<&'static str>,
129129
span_handler: &'a SpanHandler,
130+
cm: &'a CodeMap,
130131
}
131132

132133
impl<'a> Context<'a> {
@@ -144,7 +145,71 @@ impl<'a> Context<'a> {
144145
}
145146
}
146147

147-
impl<'a, 'v> Visitor<'v> for Context<'a> {
148+
struct MacroVisitor<'a> {
149+
context: &'a Context<'a>
150+
}
151+
152+
impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
153+
fn visit_view_item(&mut self, i: &ast::ViewItem) {
154+
match i.node {
155+
ast::ViewItemExternCrate(..) => {
156+
for attr in i.attrs.iter() {
157+
if attr.name().get() == "phase"{
158+
self.context.gate_feature("phase", attr.span,
159+
"compile time crate loading is \
160+
experimental and possibly buggy");
161+
}
162+
}
163+
},
164+
_ => { }
165+
}
166+
visit::walk_view_item(self, i)
167+
}
168+
169+
fn visit_mac(&mut self, macro: &ast::Mac) {
170+
let ast::MacInvocTT(ref path, _, _) = macro.node;
171+
let id = path.segments.last().unwrap().identifier;
172+
173+
if id == token::str_to_ident("macro_rules") {
174+
self.context.gate_feature("macro_rules", path.span, "macro definitions are \
175+
not stable enough for use and are subject to change");
176+
}
177+
178+
else if id == token::str_to_ident("asm") {
179+
self.context.gate_feature("asm", path.span, "inline assembly is not \
180+
stable enough for use and is subject to change");
181+
}
182+
183+
else if id == token::str_to_ident("log_syntax") {
184+
self.context.gate_feature("log_syntax", path.span, "`log_syntax!` is not \
185+
stable enough for use and is subject to change");
186+
}
187+
188+
else if id == token::str_to_ident("trace_macros") {
189+
self.context.gate_feature("trace_macros", path.span, "`trace_macros` is not \
190+
stable enough for use and is subject to change");
191+
}
192+
193+
else if id == token::str_to_ident("concat_idents") {
194+
self.context.gate_feature("concat_idents", path.span, "`concat_idents` is not \
195+
stable enough for use and is subject to change");
196+
}
197+
}
198+
}
199+
200+
struct PostExpansionVisitor<'a> {
201+
context: &'a Context<'a>
202+
}
203+
204+
impl<'a> PostExpansionVisitor<'a> {
205+
fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
206+
if !self.context.cm.span_is_internal(span) {
207+
self.context.gate_feature(feature, span, explain)
208+
}
209+
}
210+
}
211+
212+
impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
148213
fn visit_name(&mut self, sp: Span, name: ast::Name) {
149214
if !token::get_name(name).get().is_ascii() {
150215
self.gate_feature("non_ascii_idents", sp,
@@ -217,7 +282,7 @@ impl<'a, 'v> Visitor<'v> for Context<'a> {
217282
}
218283

219284
ast::ItemImpl(_, _, _, _, ref items) => {
220-
if attr::contains_name(i.attrs.as_slice(),
285+
if attr::contains_name(i.attrs[],
221286
"unsafe_destructor") {
222287
self.gate_feature("unsafe_destructor",
223288
i.span,
@@ -256,36 +321,6 @@ impl<'a, 'v> Visitor<'v> for Context<'a> {
256321
}
257322
}
258323

259-
fn visit_mac(&mut self, macro: &ast::Mac) {
260-
let ast::MacInvocTT(ref path, _, _) = macro.node;
261-
let id = path.segments.last().unwrap().identifier;
262-
263-
if id == token::str_to_ident("macro_rules") {
264-
self.gate_feature("macro_rules", path.span, "macro definitions are \
265-
not stable enough for use and are subject to change");
266-
}
267-
268-
else if id == token::str_to_ident("asm") {
269-
self.gate_feature("asm", path.span, "inline assembly is not \
270-
stable enough for use and is subject to change");
271-
}
272-
273-
else if id == token::str_to_ident("log_syntax") {
274-
self.gate_feature("log_syntax", path.span, "`log_syntax!` is not \
275-
stable enough for use and is subject to change");
276-
}
277-
278-
else if id == token::str_to_ident("trace_macros") {
279-
self.gate_feature("trace_macros", path.span, "`trace_macros` is not \
280-
stable enough for use and is subject to change");
281-
}
282-
283-
else if id == token::str_to_ident("concat_idents") {
284-
self.gate_feature("concat_idents", path.span, "`concat_idents` is not \
285-
stable enough for use and is subject to change");
286-
}
287-
}
288-
289324
fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
290325
if attr::contains_name(i.attrs[], "linkage") {
291326
self.gate_feature("linkage", i.span,
@@ -371,10 +406,15 @@ impl<'a, 'v> Visitor<'v> for Context<'a> {
371406
}
372407
}
373408

374-
pub fn check_crate(span_handler: &SpanHandler, krate: &ast::Crate) -> (Features, Vec<Span>) {
409+
fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate,
410+
check: F)
411+
-> (Features, Vec<Span>)
412+
where F: FnOnce(&mut Context, &ast::Crate)
413+
{
375414
let mut cx = Context {
376415
features: Vec::new(),
377416
span_handler: span_handler,
417+
cm: cm,
378418
};
379419

380420
let mut unknown_features = Vec::new();
@@ -419,7 +459,7 @@ pub fn check_crate(span_handler: &SpanHandler, krate: &ast::Crate) -> (Features,
419459
}
420460
}
421461

422-
visit::walk_crate(&mut cx, krate);
462+
check(&mut cx, krate);
423463

424464
(Features {
425465
default_type_params: cx.has_feature("default_type_params"),
@@ -432,3 +472,16 @@ pub fn check_crate(span_handler: &SpanHandler, krate: &ast::Crate) -> (Features,
432472
},
433473
unknown_features)
434474
}
475+
476+
pub fn check_crate_macros(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
477+
-> (Features, Vec<Span>) {
478+
check_crate_inner(cm, span_handler, krate,
479+
|ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate))
480+
}
481+
482+
pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
483+
-> (Features, Vec<Span>) {
484+
check_crate_inner(cm, span_handler, krate,
485+
|ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx },
486+
krate))
487+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2014 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+
// tests that input to a macro is checked for use of gated features. If this
12+
// test succeeds due to the acceptance of a feature, pick a new feature to
13+
// test. Not ideal, but oh well :(
14+
15+
fn main() {
16+
let a = &[1i32, 2, 3];
17+
println!("{}", {
18+
extern "rust-intrinsic" { //~ ERROR intrinsics are subject to change
19+
fn atomic_fence();
20+
}
21+
atomic_fence();
22+
42
23+
});
24+
}

0 commit comments

Comments
 (0)