Skip to content

Commit 767239f

Browse files
committed
Reenable early feature-gates as future-compat warnings
1 parent e9e46c9 commit 767239f

File tree

11 files changed

+224
-34
lines changed

11 files changed

+224
-34
lines changed

compiler/rustc_ast_passes/src/feature_gate.rs

+35-23
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ use rustc_ast as ast;
22
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
33
use rustc_ast::{AssocConstraint, AssocConstraintKind, NodeId};
44
use rustc_ast::{PatKind, RangeEnd, VariantData};
5-
use rustc_errors::{struct_span_err, Applicability};
5+
use rustc_errors::{struct_span_err, Applicability, StashKey};
6+
use rustc_feature::Features;
67
use rustc_feature::{AttributeGate, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
7-
use rustc_feature::{Features, GateIssue};
8-
use rustc_session::parse::{feature_err, feature_err_issue};
8+
use rustc_session::parse::{feature_err, feature_warn};
99
use rustc_session::Session;
1010
use rustc_span::source_map::Spanned;
1111
use rustc_span::symbol::sym;
@@ -20,9 +20,7 @@ macro_rules! gate_feature_fn {
2020
let has_feature: bool = has_feature(visitor.features);
2121
debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
2222
if !has_feature && !span.allows_unstable($name) {
23-
feature_err_issue(&visitor.sess.parse_sess, name, span, GateIssue::Language, explain)
24-
.help(help)
25-
.emit();
23+
feature_err(&visitor.sess.parse_sess, name, span, explain).help(help).emit();
2624
}
2725
}};
2826
($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
@@ -31,8 +29,19 @@ macro_rules! gate_feature_fn {
3129
let has_feature: bool = has_feature(visitor.features);
3230
debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
3331
if !has_feature && !span.allows_unstable($name) {
34-
feature_err_issue(&visitor.sess.parse_sess, name, span, GateIssue::Language, explain)
35-
.emit();
32+
feature_err(&visitor.sess.parse_sess, name, span, explain).emit();
33+
}
34+
}};
35+
(future_incompatible; $visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
36+
let (visitor, has_feature, span, name, explain) =
37+
(&*$visitor, $has_feature, $span, $name, $explain);
38+
let has_feature: bool = has_feature(visitor.features);
39+
debug!(
40+
"gate_feature(feature = {:?}, span = {:?}); has? {} (future_incompatible)",
41+
name, span, has_feature
42+
);
43+
if !has_feature && !span.allows_unstable($name) {
44+
feature_warn(&visitor.sess.parse_sess, name, span, explain);
3645
}
3746
}};
3847
}
@@ -44,6 +53,9 @@ macro_rules! gate_feature_post {
4453
($visitor: expr, $feature: ident, $span: expr, $explain: expr) => {
4554
gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain)
4655
};
56+
(future_incompatible; $visitor: expr, $feature: ident, $span: expr, $explain: expr) => {
57+
gate_feature_fn!(future_incompatible; $visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain)
58+
};
4759
}
4860

4961
pub fn check_attribute(attr: &ast::Attribute, sess: &Session, features: &Features) {
@@ -588,11 +600,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
588600
{
589601
// When we encounter a statement of the form `foo: Ty = val;`, this will emit a type
590602
// ascription error, but the likely intention was to write a `let` statement. (#78907).
591-
feature_err_issue(
603+
feature_err(
592604
&self.sess.parse_sess,
593605
sym::type_ascription,
594606
lhs.span,
595-
GateIssue::Language,
596607
"type ascription is experimental",
597608
).span_suggestion_verbose(
598609
lhs.span.shrink_to_lo(),
@@ -615,15 +626,22 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
615626
);
616627
}
617628
ast::ExprKind::Type(..) => {
618-
// To avoid noise about type ascription in common syntax errors, only emit if it
619-
// is the *only* error.
620629
if self.sess.parse_sess.span_diagnostic.err_count() == 0 {
630+
// To avoid noise about type ascription in common syntax errors,
631+
// only emit if it is the *only* error.
621632
gate_feature_post!(
622633
&self,
623634
type_ascription,
624635
e.span,
625636
"type ascription is experimental"
626637
);
638+
} else {
639+
// And if it isn't, cancel the early-pass warning.
640+
self.sess
641+
.parse_sess
642+
.span_diagnostic
643+
.steal_diagnostic(e.span, StashKey::EarlySyntaxWarning)
644+
.map(|err| err.cancel());
627645
}
628646
}
629647
ast::ExprKind::TryBlock(_) => {
@@ -789,14 +807,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
789807

790808
// All uses of `gate_all!` below this point were added in #65742,
791809
// and subsequently disabled (with the non-early gating readded).
810+
// We emit an early future-incompatible warning for these.
811+
// New syntax gates should go above here to get a hard error gate.
792812
macro_rules! gate_all {
793813
($gate:ident, $msg:literal) => {
794-
// FIXME(eddyb) do something more useful than always
795-
// disabling these uses of early feature-gatings.
796-
if false {
797-
for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
798-
gate_feature_post!(&visitor, $gate, *span, $msg);
799-
}
814+
for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
815+
gate_feature_post!(future_incompatible; &visitor, $gate, *span, $msg);
800816
}
801817
};
802818
}
@@ -809,11 +825,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
809825
gate_all!(try_blocks, "`try` blocks are unstable");
810826
gate_all!(label_break_value, "labels on blocks are unstable");
811827
gate_all!(box_syntax, "box expression syntax is experimental; you can call `Box::new` instead");
812-
// To avoid noise about type ascription in common syntax errors,
813-
// only emit if it is the *only* error. (Also check it last.)
814-
if sess.parse_sess.span_diagnostic.err_count() == 0 {
815-
gate_all!(type_ascription, "type ascription is experimental");
816-
}
828+
gate_all!(type_ascription, "type ascription is experimental");
817829

818830
visit::walk_crate(&mut visitor, krate);
819831
}

compiler/rustc_errors/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,7 @@ struct HandlerInner {
458458
pub enum StashKey {
459459
ItemNoType,
460460
UnderscoreForArrayLengths,
461+
EarlySyntaxWarning,
461462
}
462463

463464
fn default_track_diagnostic(_: &Diagnostic) {}

compiler/rustc_lint_defs/src/builtin.rs

+51
Original file line numberDiff line numberDiff line change
@@ -3212,6 +3212,56 @@ declare_lint! {
32123212
};
32133213
}
32143214

3215+
declare_lint! {
3216+
/// The `unstable_syntax_pre_expansion` lint detects the use of unstable
3217+
/// syntax that is discarded during attribute expansion.
3218+
///
3219+
/// ### Example
3220+
///
3221+
/// ```rust
3222+
/// #[cfg(FALSE)]
3223+
/// macro foo() {}
3224+
/// ```
3225+
///
3226+
/// {{produces}}
3227+
///
3228+
/// ### Explanation
3229+
///
3230+
/// The input to active attributes such as `#[cfg]` or procedural macro
3231+
/// attributes is required to be valid syntax. Previously, the compiler only
3232+
/// gated the use of unstable syntax features after resolving `#[cfg]` gates
3233+
/// and expanding procedural macros.
3234+
///
3235+
/// To avoid relying on unstable syntax, move the use of unstable syntax
3236+
/// into a position where the compiler does not parse the syntax, such as a
3237+
/// functionlike macro.
3238+
///
3239+
/// ```rust
3240+
/// # #![deny(unstable_syntax_pre_expansion)]
3241+
///
3242+
/// macro_rules! identity {
3243+
/// ( $($tokens:tt)* ) => { $($tokens)* }
3244+
/// }
3245+
///
3246+
/// #[cfg(FALSE)]
3247+
/// identity! {
3248+
/// macro foo() {}
3249+
/// }
3250+
/// ```
3251+
///
3252+
/// This is a [future-incompatible] lint to transition this
3253+
/// to a hard error in the future. See [issue #65860] for more details.
3254+
///
3255+
/// [issue #65860]: https://github.com/rust-lang/rust/issues/65860
3256+
/// [future-incompatible]: ../index.md#future-incompatible-lints
3257+
pub UNSTABLE_SYNTAX_PRE_EXPANSION,
3258+
Warn,
3259+
"unstable syntax can change at any point in the future, causing a hard error!",
3260+
@future_incompatible = FutureIncompatibleInfo {
3261+
reference: "issue #65860 <https://github.com/rust-lang/rust/issues/65860>",
3262+
};
3263+
}
3264+
32153265
declare_lint_pass! {
32163266
/// Does nothing as a lint pass, but registers some `Lint`s
32173267
/// that are used by other parts of the compiler.
@@ -3280,6 +3330,7 @@ declare_lint_pass! {
32803330
POINTER_STRUCTURAL_MATCH,
32813331
NONTRIVIAL_STRUCTURAL_MATCH,
32823332
SOFT_UNSTABLE,
3333+
UNSTABLE_SYNTAX_PRE_EXPANSION,
32833334
INLINE_NO_SANITIZE,
32843335
BAD_ASM_STYLE,
32853336
ASM_SUB_REGISTER,

compiler/rustc_session/src/parse.rs

+52-3
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22
//! It also serves as an input to the parser itself.
33
44
use crate::config::CheckCfg;
5-
use crate::lint::{BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId};
5+
use crate::lint::{
6+
builtin::UNSTABLE_SYNTAX_PRE_EXPANSION, BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId,
7+
};
68
use crate::SessionDiagnostic;
79
use rustc_ast::node_id::NodeId;
810
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
911
use rustc_data_structures::sync::{Lock, Lrc};
1012
use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler};
1113
use rustc_errors::{
12-
error_code, fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder,
13-
DiagnosticMessage, ErrorGuaranteed, MultiSpan,
14+
error_code, fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId,
15+
DiagnosticMessage, ErrorGuaranteed, MultiSpan, StashKey,
1416
};
1517
use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
1618
use rustc_span::edition::Edition;
@@ -101,11 +103,58 @@ pub fn feature_err_issue<'a>(
101103
issue: GateIssue,
102104
explain: &str,
103105
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
106+
let span = span.into();
107+
108+
// Cancel an earlier warning for this same error, if it exists.
109+
if let Some(span) = span.primary_span() {
110+
sess.span_diagnostic
111+
.steal_diagnostic(span, StashKey::EarlySyntaxWarning)
112+
.map(|err| err.cancel());
113+
}
114+
104115
let mut err = sess.span_diagnostic.struct_span_err_with_code(span, explain, error_code!(E0658));
105116
add_feature_diagnostics_for_issue(&mut err, sess, feature, issue);
106117
err
107118
}
108119

120+
/// Construct a future incompatibility diagnostic for a feature gate.
121+
///
122+
/// This diagnostic is only a warning and *does not cause compilation to fail*.
123+
pub fn feature_warn<'a>(sess: &'a ParseSess, feature: Symbol, span: Span, explain: &str) {
124+
feature_warn_issue(sess, feature, span, GateIssue::Language, explain);
125+
}
126+
127+
/// Construct a future incompatibility diagnostic for a feature gate.
128+
///
129+
/// This diagnostic is only a warning and *does not cause compilation to fail*.
130+
///
131+
/// This variant allows you to control whether it is a library or language feature.
132+
/// Almost always, you want to use this for a language feature. If so, prefer `feature_warn`.
133+
pub fn feature_warn_issue<'a>(
134+
sess: &'a ParseSess,
135+
feature: Symbol,
136+
span: Span,
137+
issue: GateIssue,
138+
explain: &str,
139+
) {
140+
let mut err = sess.span_diagnostic.struct_span_warn(span, explain);
141+
add_feature_diagnostics_for_issue(&mut err, sess, feature, issue);
142+
143+
// Decorate this as a future-incompatibility lint as in rustc_middle::lint::struct_lint_level
144+
let lint = UNSTABLE_SYNTAX_PRE_EXPANSION;
145+
let future_incompatible = lint.future_incompatible.as_ref().unwrap();
146+
err.code(DiagnosticId::Lint {
147+
name: lint.name_lower(),
148+
has_future_breakage: false,
149+
is_force_warn: false,
150+
});
151+
err.warn(lint.desc);
152+
err.note(format!("for more information, see {}", future_incompatible.reference));
153+
154+
// A later feature_err call can steal and cancel this warning.
155+
err.stash(span, StashKey::EarlySyntaxWarning);
156+
}
157+
109158
/// Adds the diagnostics for a feature to an existing error.
110159
pub fn add_feature_diagnostics<'a>(err: &mut Diagnostic, sess: &'a ParseSess, feature: Symbol) {
111160
add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language);

src/test/ui/macros/stringify.rs

+7
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@
33
// compile-flags: --test
44

55
#![feature(async_closure)]
6+
#![feature(box_patterns)]
7+
#![feature(box_syntax)]
68
#![feature(const_trait_impl)]
9+
#![feature(decl_macro)]
710
#![feature(generators)]
811
#![feature(half_open_range_patterns)]
12+
#![feature(label_break_value)]
913
#![feature(more_qualified_paths)]
1014
#![feature(raw_ref_op)]
15+
#![feature(trait_alias)]
16+
#![feature(try_blocks)]
17+
#![feature(type_ascription)]
1118
#![deny(unused_macros)]
1219

1320
macro_rules! stringify_block {

src/test/ui/or-patterns/or-patterns-syntactic-pass.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ fn main() {}
77

88
// Test the `pat` macro fragment parser:
99
macro_rules! accept_pat {
10-
($p:pat) => {}
10+
($p:pat) => {};
1111
}
1212

1313
accept_pat!((p | q));
@@ -21,28 +21,28 @@ accept_pat!([p | q]);
2121
#[cfg(FALSE)]
2222
fn or_patterns() {
2323
// Top level of `let`:
24-
let (| A | B);
24+
let (A | B);
2525
let (A | B);
2626
let (A | B): u8;
2727
let (A | B) = 0;
2828
let (A | B): u8 = 0;
2929

3030
// Top level of `for`:
31-
for | A | B in 0 {}
31+
for A | B in 0 {}
3232
for A | B in 0 {}
3333

3434
// Top level of `while`:
35-
while let | A | B = 0 {}
35+
while let A | B = 0 {}
3636
while let A | B = 0 {}
3737

3838
// Top level of `if`:
39-
if let | A | B = 0 {}
39+
if let A | B = 0 {}
4040
if let A | B = 0 {}
4141

4242
// Top level of `match` arms:
4343
match 0 {
44-
| A | B => {},
45-
A | B => {},
44+
A | B => {}
45+
A | B => {}
4646
}
4747

4848
// Functions:
@@ -68,6 +68,8 @@ fn or_patterns() {
6868

6969
// These bind as `(prefix p) | q` as opposed to `prefix (p | q)`:
7070
let (box 0 | 1); // Unstable; we *can* change the precedence if we want.
71+
//~^ WARN box pattern syntax is experimental
72+
//~| WARN unstable syntax
7173
let (&0 | 1);
7274
let (&mut 0 | 1);
7375
let (x @ 0 | 1);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
warning: box pattern syntax is experimental
2+
--> $DIR/or-patterns-syntactic-pass.rs:70:10
3+
|
4+
LL | let (box 0 | 1); // Unstable; we *can* change the precedence if we want.
5+
| ^^^^^
6+
|
7+
= note: see issue #29641 <https://github.com/rust-lang/rust/issues/29641> for more information
8+
= help: add `#![feature(box_patterns)]` to the crate attributes to enable
9+
= warning: unstable syntax can change at any point in the future, causing a hard error!
10+
= note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
11+
12+
warning: 1 warning emitted
13+

src/test/ui/parser/constraints-before-generic-args-syntactic-pass.rs

+4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
#[cfg(FALSE)]
44
fn syntax() {
55
foo::<T = u8, T: Ord, String>();
6+
//~^ WARN associated type bounds are unstable
7+
//~| WARN unstable syntax
68
foo::<T = u8, 'a, T: Ord>();
9+
//~^ WARN associated type bounds are unstable
10+
//~| WARN unstable syntax
711
}
812

913
fn main() {}

0 commit comments

Comments
 (0)