Skip to content

Commit 1412ff5

Browse files
authored
Rollup merge of rust-lang#43776 - zackmdavis:feature_gate_fn_must_use, r=alexcrichton
feature-gate #[must_use] for functions as `fn_must_use` @eddyb I [was](rust-lang#43728 (comment)) [dithering](rust-lang#43728 (comment)) on this, but [your comment](rust-lang#43302 (comment)) makes it sound like we do want a feature gate for this? Please advise. r? @eddyb
2 parents e7070dd + 35c4494 commit 1412ff5

File tree

6 files changed

+128
-34
lines changed

6 files changed

+128
-34
lines changed

src/librustc_lint/unused.rs

+19-15
Original file line numberDiff line numberDiff line change
@@ -160,21 +160,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
160160
};
161161

162162
let mut fn_warned = false;
163-
let maybe_def = match expr.node {
164-
hir::ExprCall(ref callee, _) => {
165-
match callee.node {
166-
hir::ExprPath(ref qpath) => Some(cx.tables.qpath_def(qpath, callee.hir_id)),
167-
_ => None
168-
}
169-
},
170-
hir::ExprMethodCall(..) => {
171-
cx.tables.type_dependent_defs().get(expr.hir_id).cloned()
172-
},
173-
_ => { None }
174-
};
175-
if let Some(def) = maybe_def {
176-
let def_id = def.def_id();
177-
fn_warned = check_must_use(cx, def_id, s.span, "return value of ");
163+
if cx.tcx.sess.features.borrow().fn_must_use {
164+
let maybe_def = match expr.node {
165+
hir::ExprCall(ref callee, _) => {
166+
match callee.node {
167+
hir::ExprPath(ref qpath) => {
168+
Some(cx.tables.qpath_def(qpath, callee.hir_id))
169+
},
170+
_ => None
171+
}
172+
},
173+
hir::ExprMethodCall(..) => {
174+
cx.tables.type_dependent_defs().get(expr.hir_id).cloned()
175+
},
176+
_ => None
177+
};
178+
if let Some(def) = maybe_def {
179+
let def_id = def.def_id();
180+
fn_warned = check_must_use(cx, def_id, s.span, "return value of ");
181+
}
178182
}
179183

180184
if !(ty_warned || fn_warned) {

src/libsyntax/feature_gate.rs

+70-13
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ macro_rules! declare_features {
112112
// was set. This is most important for knowing when a particular feature became
113113
// stable (active).
114114
//
115-
// NB: The featureck.py script parses this information directly out of the source
116-
// so take care when modifying it.
115+
// NB: tools/tidy/src/features.rs parses this information directly out of the
116+
// source, so take care when modifying it.
117117

118118
declare_features! (
119119
(active, asm, "1.0.0", Some(29722)),
@@ -372,6 +372,9 @@ declare_features! (
372372

373373
// #[doc(cfg(...))]
374374
(active, doc_cfg, "1.21.0", Some(43781)),
375+
376+
// allow `#[must_use]` on functions (RFC 1940)
377+
(active, fn_must_use, "1.21.0", Some(43302)),
375378
);
376379

377380
declare_features! (
@@ -915,20 +918,27 @@ struct Context<'a> {
915918
}
916919

917920
macro_rules! gate_feature_fn {
918-
($cx: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
919-
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);
920924
let has_feature: bool = has_feature(&$cx.features);
921925
debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
922926
if !has_feature && !span.allows_unstable() {
923-
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();
924929
}
925930
}}
926931
}
927932

928933
macro_rules! gate_feature {
929934
($cx: expr, $feature: ident, $span: expr, $explain: expr) => {
930-
gate_feature_fn!($cx, |x:&Features| x.$feature, $span, stringify!($feature), $explain)
931-
}
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+
};
932942
}
933943

934944
impl<'a> Context<'a> {
@@ -938,7 +948,7 @@ impl<'a> Context<'a> {
938948
for &(n, ty, ref gateage) in BUILTIN_ATTRIBUTES {
939949
if name == n {
940950
if let Gated(_, name, desc, ref has_feature) = *gateage {
941-
gate_feature_fn!(self, has_feature, attr.span, name, desc);
951+
gate_feature_fn!(self, has_feature, attr.span, name, desc, GateStrength::Hard);
942952
}
943953
debug!("check_attribute: {:?} is builtin, {:?}, {:?}", attr.path, ty, gateage);
944954
return;
@@ -1008,24 +1018,42 @@ pub enum GateIssue {
10081018
Library(Option<u32>)
10091019
}
10101020

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+
10111029
pub fn emit_feature_err(sess: &ParseSess, feature: &str, span: Span, issue: GateIssue,
10121030
explain: &str) {
10131031
feature_err(sess, feature, span, issue, explain).emit();
10141032
}
10151033

10161034
pub fn feature_err<'a>(sess: &'a ParseSess, feature: &str, span: Span, issue: GateIssue,
1017-
explain: &str) -> DiagnosticBuilder<'a> {
1035+
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> {
10181041
let diag = &sess.span_diagnostic;
10191042

10201043
let issue = match issue {
10211044
GateIssue::Language => find_lang_feature_issue(feature),
10221045
GateIssue::Library(lib) => lib,
10231046
};
10241047

1025-
let mut err = if let Some(n) = issue {
1026-
diag.struct_span_err(span, &format!("{} (see issue #{})", explain, n))
1048+
let explanation = if let Some(n) = issue {
1049+
format!("{} (see issue #{})", explain, n)
10271050
} else {
1028-
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),
10291057
};
10301058

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

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+
10381073
err
1074+
10391075
}
10401076

10411077
const EXPLAIN_BOX_SYNTAX: &'static str =
@@ -1092,6 +1128,12 @@ macro_rules! gate_feature_post {
10921128
if !span.allows_unstable() {
10931129
gate_feature!(cx.context, $feature, span, $explain)
10941130
}
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+
}
10951137
}}
10961138
}
10971139

@@ -1234,6 +1276,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
12341276
function may change over time, for now \
12351277
a top-level `fn main()` is required");
12361278
}
1279+
if attr::contains_name(&i.attrs[..], "must_use") {
1280+
gate_feature_post!(&self, fn_must_use, i.span,
1281+
"`#[must_use]` on functions is experimental",
1282+
GateStrength::Soft);
1283+
}
12371284
}
12381285

12391286
ast::ItemKind::Struct(..) => {
@@ -1271,7 +1318,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
12711318
and possibly buggy");
12721319
}
12731320

1274-
ast::ItemKind::Impl(_, polarity, defaultness, _, _, _, _) => {
1321+
ast::ItemKind::Impl(_, polarity, defaultness, _, _, _, ref impl_items) => {
12751322
if polarity == ast::ImplPolarity::Negative {
12761323
gate_feature_post!(&self, optin_builtin_traits,
12771324
i.span,
@@ -1284,6 +1331,16 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
12841331
i.span,
12851332
"specialization is unstable");
12861333
}
1334+
1335+
for impl_item in impl_items {
1336+
if let ast::ImplItemKind::Method(..) = impl_item.node {
1337+
if attr::contains_name(&impl_item.attrs[..], "must_use") {
1338+
gate_feature_post!(&self, fn_must_use, impl_item.span,
1339+
"`#[must_use]` on methods is experimental",
1340+
GateStrength::Soft);
1341+
}
1342+
}
1343+
}
12871344
}
12881345

12891346
ast::ItemKind::MacroDef(ast::MacroDef { legacy: false, .. }) => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2017 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+
#![feature(rustc_attrs)]
12+
13+
struct MyStruct;
14+
15+
impl MyStruct {
16+
#[must_use]
17+
fn need_to_use_method() -> bool { true } //~ WARN `#[must_use]` on methods is experimental
18+
}
19+
20+
#[must_use]
21+
fn need_to_use_it() -> bool { true } //~ WARN `#[must_use]` on functions is experimental
22+
23+
24+
// Feature gates are tidy-required to have a specially named (or
25+
// comment-annotated) compile-fail test (which MUST fail), but for
26+
// backwards-compatibility reasons, we want `#[must_use]` on functions to be
27+
// compilable even if the `fn_must_use` feature is absent, thus necessitating
28+
// the usage of `#[rustc_error]` here, pragmatically if awkwardly solving this
29+
// dilemma until a superior solution can be devised.
30+
#[rustc_error]
31+
fn main() {} //~ ERROR compilation successful

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

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

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

684685
#[must_use = "1400"] struct S;
685686

src/test/ui/lint/fn_must_use.rs

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

11+
#![feature(fn_must_use)]
1112
#![warn(unused_must_use)]
1213

1314
struct MyStruct {

src/test/ui/lint/fn_must_use.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
warning: unused return value of `need_to_use_this_value` which must be used: it's important
2-
--> $DIR/fn_must_use.rs:30:5
2+
--> $DIR/fn_must_use.rs:31:5
33
|
4-
30 | need_to_use_this_value();
4+
31 | need_to_use_this_value();
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
note: lint level defined here
8-
--> $DIR/fn_must_use.rs:11:9
8+
--> $DIR/fn_must_use.rs:12:9
99
|
10-
11 | #![warn(unused_must_use)]
10+
12 | #![warn(unused_must_use)]
1111
| ^^^^^^^^^^^^^^^
1212

1313
warning: unused return value of `MyStruct::need_to_use_this_method_value` which must be used
14-
--> $DIR/fn_must_use.rs:33:5
14+
--> $DIR/fn_must_use.rs:34:5
1515
|
16-
33 | m.need_to_use_this_method_value();
16+
34 | m.need_to_use_this_method_value();
1717
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1818

0 commit comments

Comments
 (0)