}
fn is_internal(&self, cx: &Context, span: Span) -> bool {
- // first, check if the given expression was generated by a macro or not
- // we need to go back the expn_info tree to check only the arguments
- // of the initial macro call, not the nested ones.
- let mut expnid = span.expn_id;
- let mut is_internal = false;
- while cx.tcx.sess.codemap().with_expn_info(expnid, |expninfo| {
- match expninfo {
- Some(ref info) => {
- // save the parent expn_id for next loop iteration
- expnid = info.call_site.expn_id;
- if info.callee.span.is_none() {
- // it's a compiler built-in, we *really* don't want to mess with it
- // so we skip it, unless it was called by a regular macro, in which case
- // we will handle the caller macro next turn
- is_internal = true;
- true // continue looping
- } else {
- // was this expression from the current macro arguments ?
- is_internal = !( span.lo > info.call_site.lo &&
- span.hi < info.call_site.hi );
- true // continue looping
- }
- },
- _ => false // stop looping
- }
- }) { /* empty while loop body */ }
- return is_internal;
+ cx.tcx.sess.codemap().span_is_internal(span)
}
}
*sess.crate_metadata.borrow_mut() =
collect_crate_metadata(sess, krate.attrs[]);
- time(time_passes, "gated feature checking", (), |_| {
- let (features, unknown_features) =
- syntax::feature_gate::check_crate(&sess.parse_sess.span_diagnostic, &krate);
-
- for uf in unknown_features.iter() {
- sess.add_lint(lint::builtin::UNKNOWN_FEATURES,
- ast::CRATE_NODE_ID,
- *uf,
- "unknown feature".to_string());
- }
-
- sess.abort_if_errors();
- *sess.features.borrow_mut() = features;
- });
-
time(time_passes, "recursion limit", (), |_| {
middle::recursion_limit::update_recursion_limit(sess, &krate);
});
//
// baz! should not use this definition unless foo is enabled.
+ time(time_passes, "gated macro checking", (), |_| {
+ let (features, unknown_features) =
+ syntax::feature_gate::check_crate_macros(sess.codemap(),
+ &sess.parse_sess.span_diagnostic,
+ &krate);
+ for uf in unknown_features.iter() {
+ sess.add_lint(lint::builtin::UNKNOWN_FEATURES,
+ ast::CRATE_NODE_ID,
+ *uf,
+ "unknown feature".to_string());
+ }
+
+ // these need to be set "early" so that expansion sees `quote` if enabled.
+ *sess.features.borrow_mut() = features;
+ sess.abort_if_errors();
+ });
+
krate = time(time_passes, "configuration 1", krate, |krate|
syntax::config::strip_unconfigured_items(sess.diagnostic(), krate));
}
);
+ // Needs to go *after* expansion to be able to check the results of macro expansion.
+ time(time_passes, "complete gated feature checking", (), |_| {
+ syntax::feature_gate::check_crate(sess.codemap(),
+ &sess.parse_sess.span_diagnostic,
+ &krate);
+ sess.abort_if_errors();
+ });
+
// JBC: make CFG processing part of expansion to avoid this problem:
// strip again, in case expansion added anything with a #[cfg].
ExpnId(i) => f(Some(&(*self.expansions.borrow())[i as uint]))
}
}
+
+ /// Check if a span is "internal" to a macro. This means that it is entirely generated by a
+ /// macro expansion and contains no code that was passed in as an argument.
+ pub fn span_is_internal(&self, span: Span) -> bool {
+ // first, check if the given expression was generated by a macro or not
+ // we need to go back the expn_info tree to check only the arguments
+ // of the initial macro call, not the nested ones.
+ let mut is_internal = false;
+ let mut expnid = span.expn_id;
+ while self.with_expn_info(expnid, |expninfo| {
+ match expninfo {
+ Some(ref info) => {
+ // save the parent expn_id for next loop iteration
+ expnid = info.call_site.expn_id;
+ if info.callee.span.is_none() {
+ // it's a compiler built-in, we *really* don't want to mess with it
+ // so we skip it, unless it was called by a regular macro, in which case
+ // we will handle the caller macro next turn
+ is_internal = true;
+ true // continue looping
+ } else {
+ // was this expression from the current macro arguments ?
+ is_internal = !( span.lo > info.call_site.lo &&
+ span.hi < info.call_site.hi );
+ true // continue looping
+ }
+ },
+ _ => false // stop looping
+ }
+ }) { /* empty while loop body */ }
+ return is_internal;
+ }
}
#[cfg(test)]
use ast;
use attr;
use attr::AttrMetaMethods;
-use codemap::Span;
+use codemap::{CodeMap, Span};
use diagnostic::SpanHandler;
use visit;
use visit::Visitor;
struct Context<'a> {
features: Vec<&'static str>,
span_handler: &'a SpanHandler,
+ cm: &'a CodeMap,
}
impl<'a> Context<'a> {
}
}
-impl<'a, 'v> Visitor<'v> for Context<'a> {
+struct MacroVisitor<'a> {
+ context: &'a Context<'a>
+}
+
+impl<'a, 'v> Visitor<'v> for MacroVisitor<'a> {
+ fn visit_view_item(&mut self, i: &ast::ViewItem) {
+ match i.node {
+ ast::ViewItemExternCrate(..) => {
+ for attr in i.attrs.iter() {
+ if attr.name().get() == "phase"{
+ self.context.gate_feature("phase", attr.span,
+ "compile time crate loading is \
+ experimental and possibly buggy");
+ }
+ }
+ },
+ _ => { }
+ }
+ visit::walk_view_item(self, i)
+ }
+
+ fn visit_mac(&mut self, macro: &ast::Mac) {
+ let ast::MacInvocTT(ref path, _, _) = macro.node;
+ let id = path.segments.last().unwrap().identifier;
+
+ if id == token::str_to_ident("macro_rules") {
+ self.context.gate_feature("macro_rules", path.span, "macro definitions are \
+ not stable enough for use and are subject to change");
+ }
+
+ else if id == token::str_to_ident("asm") {
+ self.context.gate_feature("asm", path.span, "inline assembly is not \
+ stable enough for use and is subject to change");
+ }
+
+ else if id == token::str_to_ident("log_syntax") {
+ self.context.gate_feature("log_syntax", path.span, "`log_syntax!` is not \
+ stable enough for use and is subject to change");
+ }
+
+ else if id == token::str_to_ident("trace_macros") {
+ self.context.gate_feature("trace_macros", path.span, "`trace_macros` is not \
+ stable enough for use and is subject to change");
+ }
+
+ else if id == token::str_to_ident("concat_idents") {
+ self.context.gate_feature("concat_idents", path.span, "`concat_idents` is not \
+ stable enough for use and is subject to change");
+ }
+ }
+}
+
+struct PostExpansionVisitor<'a> {
+ context: &'a Context<'a>
+}
+
+impl<'a> PostExpansionVisitor<'a> {
+ fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
+ if !self.context.cm.span_is_internal(span) {
+ self.context.gate_feature(feature, span, explain)
+ }
+ }
+}
+
+impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
fn visit_name(&mut self, sp: Span, name: ast::Name) {
if !token::get_name(name).get().is_ascii() {
self.gate_feature("non_ascii_idents", sp,
}
ast::ItemImpl(_, _, _, _, ref items) => {
- if attr::contains_name(i.attrs.as_slice(),
+ if attr::contains_name(i.attrs[],
"unsafe_destructor") {
self.gate_feature("unsafe_destructor",
i.span,
}
}
- fn visit_mac(&mut self, macro: &ast::Mac) {
- let ast::MacInvocTT(ref path, _, _) = macro.node;
- let id = path.segments.last().unwrap().identifier;
-
- if id == token::str_to_ident("macro_rules") {
- self.gate_feature("macro_rules", path.span, "macro definitions are \
- not stable enough for use and are subject to change");
- }
-
- else if id == token::str_to_ident("asm") {
- self.gate_feature("asm", path.span, "inline assembly is not \
- stable enough for use and is subject to change");
- }
-
- else if id == token::str_to_ident("log_syntax") {
- self.gate_feature("log_syntax", path.span, "`log_syntax!` is not \
- stable enough for use and is subject to change");
- }
-
- else if id == token::str_to_ident("trace_macros") {
- self.gate_feature("trace_macros", path.span, "`trace_macros` is not \
- stable enough for use and is subject to change");
- }
-
- else if id == token::str_to_ident("concat_idents") {
- self.gate_feature("concat_idents", path.span, "`concat_idents` is not \
- stable enough for use and is subject to change");
- }
- }
-
fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
if attr::contains_name(i.attrs[], "linkage") {
self.gate_feature("linkage", i.span,
}
}
-pub fn check_crate(span_handler: &SpanHandler, krate: &ast::Crate) -> (Features, Vec<Span>) {
+fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate,
+ check: F)
+ -> (Features, Vec<Span>)
+ where F: FnOnce(&mut Context, &ast::Crate)
+{
let mut cx = Context {
features: Vec::new(),
span_handler: span_handler,
+ cm: cm,
};
let mut unknown_features = Vec::new();
}
}
- visit::walk_crate(&mut cx, krate);
+ check(&mut cx, krate);
(Features {
default_type_params: cx.has_feature("default_type_params"),
},
unknown_features)
}
+
+pub fn check_crate_macros(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
+-> (Features, Vec<Span>) {
+ check_crate_inner(cm, span_handler, krate,
+ |ctx, krate| visit::walk_crate(&mut MacroVisitor { context: ctx }, krate))
+}
+
+pub fn check_crate(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::Crate)
+-> (Features, Vec<Span>) {
+ check_crate_inner(cm, span_handler, krate,
+ |ctx, krate| visit::walk_crate(&mut PostExpansionVisitor { context: ctx },
+ krate))
+}