Skip to content

Commit 8492ad2

Browse files
committed
"soft" (warn instead of error) feature-gate for #[must_use] on functions
Before `#[must_use]` for functions was implemented, a `#[must_use]` attribute on a function was a no-op. To avoid a breaking change in this behavior, we add an option for "this-and-such feature is experimental" feature-gate messages to be a mere warning rather than a compilation-halting failure (so old code that used to have a useless no-op `#[must_use]` attribute now warns rather than breaking). When we're on stable, we add a help note to clarify that the feature isn't "on." This is in support of #43302.
1 parent 7b6e9b4 commit 8492ad2

File tree

3 files changed

+63
-13
lines changed

3 files changed

+63
-13
lines changed

src/libsyntax/feature_gate.rs

+50-10
Original file line numberDiff line numberDiff line change
@@ -918,20 +918,27 @@ struct Context<'a> {
918918
}
919919

920920
macro_rules! gate_feature_fn {
921-
($cx: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
922-
let (cx, has_feature, span, name, explain) = ($cx, $has_feature, $span, $name, $explain);
921+
($cx: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr, $level: expr) => {{
922+
let (cx, has_feature, span,
923+
name, explain, level) = ($cx, $has_feature, $span, $name, $explain, $level);
923924
let has_feature: bool = has_feature(&$cx.features);
924925
debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
925926
if !has_feature && !span.allows_unstable() {
926-
emit_feature_err(cx.parse_sess, name, span, GateIssue::Language, explain);
927+
leveled_feature_err(cx.parse_sess, name, span, GateIssue::Language, explain, level)
928+
.emit();
927929
}
928930
}}
929931
}
930932

931933
macro_rules! gate_feature {
932934
($cx: expr, $feature: ident, $span: expr, $explain: expr) => {
933-
gate_feature_fn!($cx, |x:&Features| x.$feature, $span, stringify!($feature), $explain)
934-
}
935+
gate_feature_fn!($cx, |x:&Features| x.$feature, $span,
936+
stringify!($feature), $explain, GateStrength::Hard)
937+
};
938+
($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {
939+
gate_feature_fn!($cx, |x:&Features| x.$feature, $span,
940+
stringify!($feature), $explain, $level)
941+
};
935942
}
936943

937944
impl<'a> Context<'a> {
@@ -941,7 +948,7 @@ impl<'a> Context<'a> {
941948
for &(n, ty, ref gateage) in BUILTIN_ATTRIBUTES {
942949
if name == n {
943950
if let Gated(_, name, desc, ref has_feature) = *gateage {
944-
gate_feature_fn!(self, has_feature, attr.span, name, desc);
951+
gate_feature_fn!(self, has_feature, attr.span, name, desc, GateStrength::Hard);
945952
}
946953
debug!("check_attribute: {:?} is builtin, {:?}, {:?}", attr.path, ty, gateage);
947954
return;
@@ -1011,24 +1018,42 @@ pub enum GateIssue {
10111018
Library(Option<u32>)
10121019
}
10131020

1021+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1022+
pub enum GateStrength {
1023+
/// A hard error. (Most feature gates should use this.)
1024+
Hard,
1025+
/// Only a warning. (Use this only as backwards-compatibility demands.)
1026+
Soft,
1027+
}
1028+
10141029
pub fn emit_feature_err(sess: &ParseSess, feature: &str, span: Span, issue: GateIssue,
10151030
explain: &str) {
10161031
feature_err(sess, feature, span, issue, explain).emit();
10171032
}
10181033

10191034
pub fn feature_err<'a>(sess: &'a ParseSess, feature: &str, span: Span, issue: GateIssue,
10201035
explain: &str) -> DiagnosticBuilder<'a> {
1036+
leveled_feature_err(sess, feature, span, issue, explain, GateStrength::Hard)
1037+
}
1038+
1039+
fn leveled_feature_err<'a>(sess: &'a ParseSess, feature: &str, span: Span, issue: GateIssue,
1040+
explain: &str, level: GateStrength) -> DiagnosticBuilder<'a> {
10211041
let diag = &sess.span_diagnostic;
10221042

10231043
let issue = match issue {
10241044
GateIssue::Language => find_lang_feature_issue(feature),
10251045
GateIssue::Library(lib) => lib,
10261046
};
10271047

1028-
let mut err = if let Some(n) = issue {
1029-
diag.struct_span_err(span, &format!("{} (see issue #{})", explain, n))
1048+
let explanation = if let Some(n) = issue {
1049+
format!("{} (see issue #{})", explain, n)
10301050
} else {
1031-
diag.struct_span_err(span, explain)
1051+
explain.to_owned()
1052+
};
1053+
1054+
let mut err = match level {
1055+
GateStrength::Hard => diag.struct_span_err(span, &explanation),
1056+
GateStrength::Soft => diag.struct_span_warn(span, &explanation),
10321057
};
10331058

10341059
// #23973: do not suggest `#![feature(...)]` if we are in beta/stable
@@ -1038,7 +1063,15 @@ pub fn feature_err<'a>(sess: &'a ParseSess, feature: &str, span: Span, issue: Ga
10381063
feature));
10391064
}
10401065

1066+
// If we're on stable and only emitting a "soft" warning, add a note to
1067+
// clarify that the feature isn't "on" (rather than being on but
1068+
// warning-worthy).
1069+
if !sess.unstable_features.is_nightly_build() && level == GateStrength::Soft {
1070+
err.help("a nightly build of the compiler is required to enable this feature");
1071+
}
1072+
10411073
err
1074+
10421075
}
10431076

10441077
const EXPLAIN_BOX_SYNTAX: &'static str =
@@ -1095,6 +1128,12 @@ macro_rules! gate_feature_post {
10951128
if !span.allows_unstable() {
10961129
gate_feature!(cx.context, $feature, span, $explain)
10971130
}
1131+
}};
1132+
($cx: expr, $feature: ident, $span: expr, $explain: expr, $level: expr) => {{
1133+
let (cx, span) = ($cx, $span);
1134+
if !span.allows_unstable() {
1135+
gate_feature!(cx.context, $feature, span, $explain, $level)
1136+
}
10981137
}}
10991138
}
11001139

@@ -1239,7 +1278,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
12391278
}
12401279
if attr::contains_name(&i.attrs[..], "must_use") {
12411280
gate_feature_post!(&self, fn_must_use, i.span,
1242-
"`#[must_use]` on functions is experimental");
1281+
"`#[must_use]` on functions is experimental",
1282+
GateStrength::Soft);
12431283
}
12441284
}
12451285

src/test/compile-fail/feature-gate-fn_must_use.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,17 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#![feature(rustc_attrs)]
12+
1113
#[must_use]
12-
fn need_to_use_it() -> bool { true } //~ ERROR `#[must_use]` on functions is experimental
14+
fn need_to_use_it() -> bool { true } //~ WARN `#[must_use]` on functions is experimental
15+
1316

14-
fn main() {}
17+
// Feature gates are tidy-required to have a specially named (or
18+
// comment-annotated) compile-fail test (which MUST fail), but for
19+
// backwards-compatibility reasons, we want `#[must_use]` on functions to be
20+
// compilable even if the `fn_must_use` feature is absent, thus necessitating
21+
// the usage of `#[rustc_error]` here, pragmatically if awkwardly solving this
22+
// dilemma until a superior solution can be devised.
23+
#[rustc_error]
24+
fn main() {} //~ ERROR compilation successful

src/test/compile-fail/feature-gate/issue-43106-gating-of-builtin-attrs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,7 @@ mod must_use {
680680
mod inner { #![must_use="1400"] }
681681

682682
#[must_use = "1400"] fn f() { }
683-
//~^ ERROR `#[must_use]` on functions is experimental
683+
//~^ WARN `#[must_use]` on functions is experimental
684684

685685
#[must_use = "1400"] struct S;
686686

0 commit comments

Comments
 (0)