From 31310f5b65232ce9714f8f4c9ad066a97f7f20f4 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Fri, 4 Sep 2015 16:37:22 -0700 Subject: [PATCH] Allow tracking issues for lang features. This is similar to the libs version, which allow an `issue` field in the `#[unstable]` attribute. cc #28244 --- src/etc/featureck.py | 14 +- src/librustc/middle/check_static_recursion.rs | 4 +- src/librustc/middle/stability.rs | 10 +- src/librustc_typeck/astconv.rs | 6 +- src/libsyntax/ext/asm.rs | 4 +- src/libsyntax/ext/concat_idents.rs | 1 + src/libsyntax/ext/deriving/mod.rs | 1 + src/libsyntax/ext/expand.rs | 4 +- src/libsyntax/ext/log_syntax.rs | 1 + src/libsyntax/ext/trace_macros.rs | 1 + src/libsyntax/feature_gate.rs | 207 ++++++++++-------- src/test/compile-fail/type-macros-fail.rs | 2 +- 12 files changed, 145 insertions(+), 110 deletions(-) diff --git a/src/etc/featureck.py b/src/etc/featureck.py index e82f00f3e7df5..c6f3bcdf9ec0b 100644 --- a/src/etc/featureck.py +++ b/src/etc/featureck.py @@ -47,20 +47,24 @@ is_feature_line = True if is_feature_line: - line = line.replace("(", "").replace("),", "").replace(")", "") + # turn ` ("foo", "1.0.0", Some(10), Active)` into + # `"foo", "1.0.0", Some(10), Active` + line = line.strip(' ,()') parts = line.split(",") - if len(parts) != 3: + if len(parts) != 4: print("error: unexpected number of components in line: " + original_line) sys.exit(1) feature_name = parts[0].strip().replace('"', "") since = parts[1].strip().replace('"', "") - status = parts[2].strip() + issue = parts[2].strip() + status = parts[3].strip() assert len(feature_name) > 0 assert len(since) > 0 + assert len(issue) > 0 assert len(status) > 0 language_feature_names += [feature_name] - language_features += [(feature_name, since, status)] + language_features += [(feature_name, since, issue, status)] assert len(language_features) > 0 @@ -158,7 +162,7 @@ status = "unstable" stable_since = None - if f[2] == "Accepted": + if f[3] == "Accepted": status = "stable" if status == "stable": stable_since = f[1] diff --git a/src/librustc/middle/check_static_recursion.rs b/src/librustc/middle/check_static_recursion.rs index 84ea6902ca54a..942d8313ec24e 100644 --- a/src/librustc/middle/check_static_recursion.rs +++ b/src/librustc/middle/check_static_recursion.rs @@ -18,7 +18,7 @@ use util::nodemap::NodeMap; use syntax::{ast}; use syntax::codemap::Span; -use syntax::feature_gate::emit_feature_err; +use syntax::feature_gate::{GateIssue, emit_feature_err}; use rustc_front::visit::Visitor; use rustc_front::visit; use rustc_front::hir; @@ -143,7 +143,7 @@ impl<'a, 'ast: 'a> CheckItemRecursionVisitor<'a, 'ast> { if !self.sess.features.borrow().static_recursion { emit_feature_err(&self.sess.parse_sess.span_diagnostic, "static_recursion", - *self.root_span, "recursive static"); + *self.root_span, GateIssue::Language, "recursive static"); } } else { span_err!(self.sess, *self.root_span, E0265, "recursive constant"); diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index 08f8eb5685e82..9c4697404201e 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -22,7 +22,7 @@ use syntax::parse::token::InternedString; use syntax::codemap::{Span, DUMMY_SP}; use syntax::ast; use syntax::ast::NodeId; -use syntax::feature_gate::emit_feature_err; +use syntax::feature_gate::{GateIssue, emit_feature_err}; use util::nodemap::{DefIdMap, FnvHashSet, FnvHashMap}; use rustc_front::hir; @@ -294,18 +294,14 @@ impl<'a, 'tcx> Checker<'a, 'tcx> { self.used_features.insert(feature.clone(), attr::Unstable); if !self.active_features.contains(feature) { - let mut msg = match *reason { + let msg = match *reason { Some(ref r) => format!("use of unstable library feature '{}': {}", &feature, &r), None => format!("use of unstable library feature '{}'", &feature) }; - if let Some(n) = issue { - use std::fmt::Write; - write!(&mut msg, " (see issue #{})", n).unwrap(); - } emit_feature_err(&self.tcx.sess.parse_sess.span_diagnostic, - &feature, span, &msg); + &feature, span, GateIssue::Library(issue), &msg); } } Some(&Stability { level, ref feature, .. }) => { diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index e4c3926fba18b..6f2d8345142be 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -70,7 +70,7 @@ use util::nodemap::FnvHashSet; use std::slice; use syntax::{abi, ast}; use syntax::codemap::{Span, Pos}; -use syntax::feature_gate::emit_feature_err; +use syntax::feature_gate::{GateIssue, emit_feature_err}; use syntax::parse::token; use rustc_front::print::pprust; @@ -797,7 +797,7 @@ fn create_substs_for_ast_trait_ref<'a,'tcx>(this: &AstConv<'tcx>, // only with `Fn()` etc. if !this.tcx().sess.features.borrow().unboxed_closures && trait_def.paren_sugar { emit_feature_err(&this.tcx().sess.parse_sess.span_diagnostic, - "unboxed_closures", span, + "unboxed_closures", span, GateIssue::Language, "\ the precise format of `Fn`-family traits' type parameters is \ subject to change. Use parenthetical notation (Fn(Foo, Bar) -> Baz) instead"); @@ -810,7 +810,7 @@ fn create_substs_for_ast_trait_ref<'a,'tcx>(this: &AstConv<'tcx>, // only with `Fn()` etc. if !this.tcx().sess.features.borrow().unboxed_closures && !trait_def.paren_sugar { emit_feature_err(&this.tcx().sess.parse_sess.span_diagnostic, - "unboxed_closures", span, + "unboxed_closures", span, GateIssue::Language, "\ parenthetical notation is only stable when used with `Fn`-family traits"); } diff --git a/src/libsyntax/ext/asm.rs b/src/libsyntax/ext/asm.rs index c48b740d83ae5..f1aa8139ec1df 100644 --- a/src/libsyntax/ext/asm.rs +++ b/src/libsyntax/ext/asm.rs @@ -51,7 +51,9 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> Box { if !cx.ecfg.enable_asm() { feature_gate::emit_feature_err( - &cx.parse_sess.span_diagnostic, "asm", sp, feature_gate::EXPLAIN_ASM); + &cx.parse_sess.span_diagnostic, "asm", sp, + feature_gate::GateIssue::Language, + feature_gate::EXPLAIN_ASM); return DummyResult::expr(sp); } diff --git a/src/libsyntax/ext/concat_idents.rs b/src/libsyntax/ext/concat_idents.rs index 24436c4520db3..c31a767300cf4 100644 --- a/src/libsyntax/ext/concat_idents.rs +++ b/src/libsyntax/ext/concat_idents.rs @@ -23,6 +23,7 @@ pub fn expand_syntax_ext<'cx>(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree] feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic, "concat_idents", sp, + feature_gate::GateIssue::Language, feature_gate::EXPLAIN_CONCAT_IDENTS); return base::DummyResult::expr(sp); } diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs index 36deaf488e15e..c7f582854aeef 100644 --- a/src/libsyntax/ext/deriving/mod.rs +++ b/src/libsyntax/ext/deriving/mod.rs @@ -105,6 +105,7 @@ fn expand_derive(cx: &mut ExtCtxt, feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic, "custom_derive", titem.span, + feature_gate::GateIssue::Language, feature_gate::EXPLAIN_CUSTOM_DERIVE); continue; } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index fd70dd175fc48..1991124ae2671 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -778,6 +778,7 @@ pub fn expand_item_mac(it: P, &fld.cx.parse_sess.span_diagnostic, "allow_internal_unstable", it.span, + feature_gate::GateIssue::Language, feature_gate::EXPLAIN_ALLOW_INTERNAL_UNSTABLE) } @@ -1469,7 +1470,8 @@ pub fn expand_type(t: P, fld: &mut MacroExpander) -> P { &fld.cx.parse_sess.span_diagnostic, "type_macros", t.span, - "type macros are experimental (see issue: #27336)"); + feature_gate::GateIssue::Language, + "type macros are experimental"); DummyResult::raw_ty(t.span) } diff --git a/src/libsyntax/ext/log_syntax.rs b/src/libsyntax/ext/log_syntax.rs index 9869108952c5e..5f7ce8d994172 100644 --- a/src/libsyntax/ext/log_syntax.rs +++ b/src/libsyntax/ext/log_syntax.rs @@ -22,6 +22,7 @@ pub fn expand_syntax_ext<'cx>(cx: &'cx mut base::ExtCtxt, feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic, "log_syntax", sp, + feature_gate::GateIssue::Language, feature_gate::EXPLAIN_LOG_SYNTAX); return base::DummyResult::any(sp); } diff --git a/src/libsyntax/ext/trace_macros.rs b/src/libsyntax/ext/trace_macros.rs index 646e6fec40553..ab34f41d932d5 100644 --- a/src/libsyntax/ext/trace_macros.rs +++ b/src/libsyntax/ext/trace_macros.rs @@ -24,6 +24,7 @@ pub fn expand_trace_macros(cx: &mut ExtCtxt, feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic, "trace_macros", sp, + feature_gate::GateIssue::Language, feature_gate::EXPLAIN_TRACE_MACROS); return base::DummyResult::any(sp); } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 0c0c68c89a10d..b3004d65f0297 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -47,147 +47,147 @@ use std::cmp; // stable (active). // NB: The featureck.py script parses this information directly out of the source // so take care when modifying it. -const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[ - ("globs", "1.0.0", Accepted), - ("macro_rules", "1.0.0", Accepted), - ("struct_variant", "1.0.0", Accepted), - ("asm", "1.0.0", Active), - ("managed_boxes", "1.0.0", Removed), - ("non_ascii_idents", "1.0.0", Active), - ("thread_local", "1.0.0", Active), - ("link_args", "1.0.0", Active), - ("plugin_registrar", "1.0.0", Active), - ("log_syntax", "1.0.0", Active), - ("trace_macros", "1.0.0", Active), - ("concat_idents", "1.0.0", Active), - ("intrinsics", "1.0.0", Active), - ("lang_items", "1.0.0", Active), - - ("simd", "1.0.0", Active), - ("default_type_params", "1.0.0", Accepted), - ("quote", "1.0.0", Active), - ("link_llvm_intrinsics", "1.0.0", Active), - ("linkage", "1.0.0", Active), - ("struct_inherit", "1.0.0", Removed), - - ("quad_precision_float", "1.0.0", Removed), - - ("rustc_diagnostic_macros", "1.0.0", Active), - ("unboxed_closures", "1.0.0", Active), - ("reflect", "1.0.0", Active), - ("import_shadowing", "1.0.0", Removed), - ("advanced_slice_patterns", "1.0.0", Active), - ("tuple_indexing", "1.0.0", Accepted), - ("associated_types", "1.0.0", Accepted), - ("visible_private_types", "1.0.0", Active), - ("slicing_syntax", "1.0.0", Accepted), - ("box_syntax", "1.0.0", Active), - ("placement_in_syntax", "1.0.0", Active), - ("pushpop_unsafe", "1.2.0", Active), - ("on_unimplemented", "1.0.0", Active), - ("simd_ffi", "1.0.0", Active), - ("allocator", "1.0.0", Active), - ("needs_allocator", "1.4.0", Active), - ("linked_from", "1.3.0", Active), - - ("if_let", "1.0.0", Accepted), - ("while_let", "1.0.0", Accepted), - - ("plugin", "1.0.0", Active), - ("start", "1.0.0", Active), - ("main", "1.0.0", Active), - - ("fundamental", "1.0.0", Active), +const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status)] = &[ + ("globs", "1.0.0", None, Accepted), + ("macro_rules", "1.0.0", None, Accepted), + ("struct_variant", "1.0.0", None, Accepted), + ("asm", "1.0.0", None, Active), + ("managed_boxes", "1.0.0", None, Removed), + ("non_ascii_idents", "1.0.0", None, Active), + ("thread_local", "1.0.0", None, Active), + ("link_args", "1.0.0", None, Active), + ("plugin_registrar", "1.0.0", None, Active), + ("log_syntax", "1.0.0", None, Active), + ("trace_macros", "1.0.0", None, Active), + ("concat_idents", "1.0.0", None, Active), + ("intrinsics", "1.0.0", None, Active), + ("lang_items", "1.0.0", None, Active), + + ("simd", "1.0.0", Some(27731), Active), + ("default_type_params", "1.0.0", None, Accepted), + ("quote", "1.0.0", None, Active), + ("link_llvm_intrinsics", "1.0.0", None, Active), + ("linkage", "1.0.0", None, Active), + ("struct_inherit", "1.0.0", None, Removed), + + ("quad_precision_float", "1.0.0", None, Removed), + + ("rustc_diagnostic_macros", "1.0.0", None, Active), + ("unboxed_closures", "1.0.0", None, Active), + ("reflect", "1.0.0", None, Active), + ("import_shadowing", "1.0.0", None, Removed), + ("advanced_slice_patterns", "1.0.0", None, Active), + ("tuple_indexing", "1.0.0", None, Accepted), + ("associated_types", "1.0.0", None, Accepted), + ("visible_private_types", "1.0.0", None, Active), + ("slicing_syntax", "1.0.0", None, Accepted), + ("box_syntax", "1.0.0", None, Active), + ("placement_in_syntax", "1.0.0", None, Active), + ("pushpop_unsafe", "1.2.0", None, Active), + ("on_unimplemented", "1.0.0", None, Active), + ("simd_ffi", "1.0.0", None, Active), + ("allocator", "1.0.0", None, Active), + ("needs_allocator", "1.4.0", None, Active), + ("linked_from", "1.3.0", None, Active), + + ("if_let", "1.0.0", None, Accepted), + ("while_let", "1.0.0", None, Accepted), + + ("plugin", "1.0.0", None, Active), + ("start", "1.0.0", None, Active), + ("main", "1.0.0", None, Active), + + ("fundamental", "1.0.0", None, Active), // A temporary feature gate used to enable parser extensions needed // to bootstrap fix for #5723. - ("issue_5723_bootstrap", "1.0.0", Accepted), + ("issue_5723_bootstrap", "1.0.0", None, Accepted), // A way to temporarily opt out of opt in copy. This will *never* be accepted. - ("opt_out_copy", "1.0.0", Removed), + ("opt_out_copy", "1.0.0", None, Removed), // OIBIT specific features - ("optin_builtin_traits", "1.0.0", Active), + ("optin_builtin_traits", "1.0.0", None, Active), // macro reexport needs more discussion and stabilization - ("macro_reexport", "1.0.0", Active), + ("macro_reexport", "1.0.0", None, Active), // These are used to test this portion of the compiler, they don't actually // mean anything - ("test_accepted_feature", "1.0.0", Accepted), - ("test_removed_feature", "1.0.0", Removed), + ("test_accepted_feature", "1.0.0", None, Accepted), + ("test_removed_feature", "1.0.0", None, Removed), // Allows use of #[staged_api] - ("staged_api", "1.0.0", Active), + ("staged_api", "1.0.0", None, Active), // Allows using items which are missing stability attributes - ("unmarked_api", "1.0.0", Active), + ("unmarked_api", "1.0.0", None, Active), // Allows using #![no_std] - ("no_std", "1.0.0", Active), + ("no_std", "1.0.0", None, Active), // Allows using #![no_core] - ("no_core", "1.3.0", Active), + ("no_core", "1.3.0", None, Active), // Allows using `box` in patterns; RFC 469 - ("box_patterns", "1.0.0", Active), + ("box_patterns", "1.0.0", None, Active), // Allows using the unsafe_no_drop_flag attribute (unlikely to // switch to Accepted; see RFC 320) - ("unsafe_no_drop_flag", "1.0.0", Active), + ("unsafe_no_drop_flag", "1.0.0", None, Active), // Allows the use of custom attributes; RFC 572 - ("custom_attribute", "1.0.0", Active), + ("custom_attribute", "1.0.0", None, Active), // Allows the use of #[derive(Anything)] as sugar for // #[derive_Anything]. - ("custom_derive", "1.0.0", Active), + ("custom_derive", "1.0.0", None, Active), // Allows the use of rustc_* attributes; RFC 572 - ("rustc_attrs", "1.0.0", Active), + ("rustc_attrs", "1.0.0", None, Active), // Allows the use of #[allow_internal_unstable]. This is an // attribute on macro_rules! and can't use the attribute handling // below (it has to be checked before expansion possibly makes // macros disappear). - ("allow_internal_unstable", "1.0.0", Active), + ("allow_internal_unstable", "1.0.0", None, Active), // #23121. Array patterns have some hazards yet. - ("slice_patterns", "1.0.0", Active), + ("slice_patterns", "1.0.0", None, Active), // Allows use of unary negate on unsigned integers, e.g. -e for e: u8 - ("negate_unsigned", "1.0.0", Active), + ("negate_unsigned", "1.0.0", None, Active), // Allows the definition of associated constants in `trait` or `impl` // blocks. - ("associated_consts", "1.0.0", Active), + ("associated_consts", "1.0.0", None, Active), // Allows the definition of `const fn` functions. - ("const_fn", "1.2.0", Active), + ("const_fn", "1.2.0", None, Active), // Allows using #[prelude_import] on glob `use` items. - ("prelude_import", "1.2.0", Active), + ("prelude_import", "1.2.0", None, Active), // Allows the definition recursive static items. - ("static_recursion", "1.3.0", Active), + ("static_recursion", "1.3.0", None, Active), // Allows default type parameters to influence type inference. - ("default_type_parameter_fallback", "1.3.0", Active), + ("default_type_parameter_fallback", "1.3.0", None, Active), // Allows associated type defaults - ("associated_type_defaults", "1.2.0", Active), + ("associated_type_defaults", "1.2.0", None, Active), // Allows macros to appear in the type position. - ("type_macros", "1.3.0", Active), + ("type_macros", "1.3.0", Some(27336), Active), // allow `repr(simd)`, and importing the various simd intrinsics - ("repr_simd", "1.4.0", Active), + ("repr_simd", "1.4.0", Some(27731), Active), // Allows cfg(target_feature = "..."). - ("cfg_target_feature", "1.4.0", Active), + ("cfg_target_feature", "1.4.0", None, Active), // allow `extern "platform-intrinsic" { ... }` - ("platform_intrinsics", "1.4.0", Active), + ("platform_intrinsics", "1.4.0", Some(27731), Active), ]; // (changing above list without updating src/doc/reference.md makes @cmr sad) @@ -386,7 +386,7 @@ impl GatedCfg { let (cfg, feature, has_feature) = GATED_CFGS[self.index]; if !has_feature(features) { let explain = format!("`cfg({})` is experimental and subject to change", cfg); - emit_feature_err(diagnostic, feature, self.span, &explain); + emit_feature_err(diagnostic, feature, self.span, GateIssue::Language, &explain); } } } @@ -488,21 +488,21 @@ pub fn check_for_box_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span if let Some(&Features { allow_box: true, .. }) = f { return; } - emit_feature_err(diag, "box_syntax", span, EXPLAIN_BOX_SYNTAX); + emit_feature_err(diag, "box_syntax", span, GateIssue::Language, EXPLAIN_BOX_SYNTAX); } pub fn check_for_placement_in(f: Option<&Features>, diag: &SpanHandler, span: Span) { if let Some(&Features { allow_placement_in: true, .. }) = f { return; } - emit_feature_err(diag, "placement_in_syntax", span, EXPLAIN_PLACEMENT_IN); + emit_feature_err(diag, "placement_in_syntax", span, GateIssue::Language, EXPLAIN_PLACEMENT_IN); } pub fn check_for_pushpop_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) { if let Some(&Features { allow_pushpop_unsafe: true, .. }) = f { return; } - emit_feature_err(diag, "pushpop_unsafe", span, EXPLAIN_PUSHPOP_UNSAFE); + emit_feature_err(diag, "pushpop_unsafe", span, GateIssue::Language, EXPLAIN_PUSHPOP_UNSAFE); } struct Context<'a> { @@ -522,7 +522,7 @@ impl<'a> Context<'a> { let has_feature = self.has_feature(feature); debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", feature, span, has_feature); if !has_feature { - emit_feature_err(self.span_handler, feature, span, explain); + emit_feature_err(self.span_handler, feature, span, GateIssue::Language, explain); } } fn has_feature(&self, feature: &str) -> bool { @@ -576,8 +576,35 @@ impl<'a> Context<'a> { } } -pub fn emit_feature_err(diag: &SpanHandler, feature: &str, span: Span, explain: &str) { - diag.span_err(span, explain); +fn find_lang_feature_issue(feature: &str) -> Option { + let info = KNOWN_FEATURES.iter() + .find(|t| t.0 == feature) + .unwrap(); + let issue = info.2; + if let Active = info.3 { + // FIXME (#28244): enforce that active features have issue numbers + // assert!(issue.is_some()) + } + issue +} + +pub enum GateIssue { + Language, + Library(Option) +} + +pub fn emit_feature_err(diag: &SpanHandler, feature: &str, span: Span, issue: GateIssue, + explain: &str) { + let issue = match issue { + GateIssue::Language => find_lang_feature_issue(feature), + GateIssue::Library(lib) => lib, + }; + + if let Some(n) = issue { + diag.span_err(span, &format!("{} (see issue #{})", explain, n)); + } else { + diag.span_err(span, explain); + } // #23973: do not suggest `#![feature(...)]` if we are in beta/stable if option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some() { return; } @@ -948,14 +975,14 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &SpanHandler, } }; match KNOWN_FEATURES.iter() - .find(|& &(n, _, _)| name == n) { - Some(&(name, _, Active)) => { + .find(|& &(n, _, _, _)| name == n) { + Some(&(name, _, _, Active)) => { cx.enable_feature(name); } - Some(&(_, _, Removed)) => { + Some(&(_, _, _, Removed)) => { span_handler.span_err(mi.span, "feature has been removed"); } - Some(&(_, _, Accepted)) => { + Some(&(_, _, _, Accepted)) => { accepted_features.push(mi.span); } None => { diff --git a/src/test/compile-fail/type-macros-fail.rs b/src/test/compile-fail/type-macros-fail.rs index f854e540ee83d..d51176a925d07 100644 --- a/src/test/compile-fail/type-macros-fail.rs +++ b/src/test/compile-fail/type-macros-fail.rs @@ -14,7 +14,7 @@ macro_rules! Id { struct Foo { x: Id!(T) - //~^ ERROR: type macros are experimental (see issue: #27336) + //~^ ERROR: type macros are experimental (see issue #27336) } fn main() {