Skip to content

Commit 9896beb

Browse files
committed
Implement an unused_result lint
I attempted to implement the lint in two steps. My first attempt was a default-warn lint about *all* unused results. While this attempt did indeed find many possible bugs, I felt that the false-positive rate was too high to be turned on by default for all of Rust. My second attempt was to make unused-result a default-allow lint, but allow certain types to opt-in to the notion of "you must use this". For example, the Result type is now flagged with #[must_use]. This lint about "must use" types is warn by default (it's different from unused-result). The unused_must_use lint had a 100% hit rate in the compiler, but there's not that many places that return Result right now. I believe that this lint is a crucial step towards moving away from conditions for I/O (because all I/O will return Result by default). I'm worried that this lint is a little too specific to Result itself, but I believe that the false positive rate for the unused_result lint is too high to make it useful when turned on by default.
1 parent edfb546 commit 9896beb

File tree

2 files changed

+113
-7
lines changed

2 files changed

+113
-7
lines changed

src/librustc/middle/lint.rs

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ pub enum Lint {
105105
Experimental,
106106
Unstable,
107107

108+
UnusedMustUse,
109+
UnusedResult,
110+
108111
Warnings,
109112
}
110113

@@ -356,12 +359,26 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[
356359
desc: "unknown features found in crate-level #[feature] directives",
357360
default: deny,
358361
}),
359-
("unknown_crate_type",
360-
LintSpec {
361-
lint: UnknownCrateType,
362-
desc: "unknown crate type found in #[crate_type] directive",
363-
default: deny,
364-
}),
362+
("unknown_crate_type",
363+
LintSpec {
364+
lint: UnknownCrateType,
365+
desc: "unknown crate type found in #[crate_type] directive",
366+
default: deny,
367+
}),
368+
369+
("unused_must_use",
370+
LintSpec {
371+
lint: UnusedMustUse,
372+
desc: "unused result of an type flagged as #[must_use]",
373+
default: warn,
374+
}),
375+
376+
("unused_result",
377+
LintSpec {
378+
lint: UnusedResult,
379+
desc: "unused result of an expression in a statement",
380+
default: allow,
381+
}),
365382
];
366383

367384
/*
@@ -934,7 +951,7 @@ static other_attrs: &'static [&'static str] = &[
934951
"crate_map", "cfg", "doc", "export_name", "link_section", "no_freeze",
935952
"no_mangle", "no_send", "static_assert", "unsafe_no_drop_flag", "packed",
936953
"simd", "repr", "deriving", "unsafe_destructor", "link", "phase",
937-
"macro_export",
954+
"macro_export", "must_use",
938955

939956
//mod-level
940957
"path", "link_name", "link_args", "nolink", "macro_escape", "no_implicit_prelude",
@@ -1016,6 +1033,54 @@ fn check_path_statement(cx: &Context, s: &ast::Stmt) {
10161033
}
10171034
}
10181035

1036+
fn check_unused_result(cx: &Context, s: &ast::Stmt) {
1037+
let expr = match s.node {
1038+
ast::StmtSemi(expr, _) => expr,
1039+
_ => return
1040+
};
1041+
let t = ty::expr_ty(cx.tcx, expr);
1042+
match ty::get(t).sty {
1043+
ty::ty_nil | ty::ty_bot | ty::ty_bool => return,
1044+
_ => {}
1045+
}
1046+
match expr.node {
1047+
ast::ExprRet(..) => return,
1048+
_ => {}
1049+
}
1050+
1051+
let t = ty::expr_ty(cx.tcx, expr);
1052+
let mut warned = false;
1053+
match ty::get(t).sty {
1054+
ty::ty_struct(did, _) |
1055+
ty::ty_enum(did, _) => {
1056+
if ast_util::is_local(did) {
1057+
match cx.tcx.items.get(did.node) {
1058+
ast_map::NodeItem(it, _) => {
1059+
if attr::contains_name(it.attrs, "must_use") {
1060+
cx.span_lint(UnusedMustUse, s.span,
1061+
"unused result which must be used");
1062+
warned = true;
1063+
}
1064+
}
1065+
_ => {}
1066+
}
1067+
} else {
1068+
csearch::get_item_attrs(cx.tcx.sess.cstore, did, |attrs| {
1069+
if attr::contains_name(attrs, "must_use") {
1070+
cx.span_lint(UnusedMustUse, s.span,
1071+
"unused result which must be used");
1072+
warned = true;
1073+
}
1074+
});
1075+
}
1076+
}
1077+
_ => {}
1078+
}
1079+
if !warned {
1080+
cx.span_lint(UnusedResult, s.span, "unused result");
1081+
}
1082+
}
1083+
10191084
fn check_item_non_camel_case_types(cx: &Context, it: &ast::Item) {
10201085
fn is_camel_case(cx: ty::ctxt, ident: ast::Ident) -> bool {
10211086
let ident = cx.sess.str_of(ident);
@@ -1478,6 +1543,7 @@ impl<'a> Visitor<()> for Context<'a> {
14781543

14791544
fn visit_stmt(&mut self, s: &ast::Stmt, _: ()) {
14801545
check_path_statement(self, s);
1546+
check_unused_result(self, s);
14811547

14821548
visit::walk_stmt(self, s, ());
14831549
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2014 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+
#[deny(unused_result, unused_must_use)];
12+
#[allow(dead_code)];
13+
14+
#[must_use]
15+
enum MustUse { Test }
16+
17+
fn foo<T>() -> T { fail!() }
18+
19+
fn bar() -> int { return foo::<int>(); }
20+
fn baz() -> MustUse { return foo::<MustUse>(); }
21+
22+
#[allow(unused_result)]
23+
fn test() {
24+
foo::<int>();
25+
foo::<MustUse>(); //~ ERROR: unused result which must be used
26+
}
27+
28+
#[allow(unused_result, unused_must_use)]
29+
fn test2() {
30+
foo::<int>();
31+
foo::<MustUse>();
32+
}
33+
34+
fn main() {
35+
foo::<int>(); //~ ERROR: unused result
36+
foo::<MustUse>(); //~ ERROR: unused result which must be used
37+
38+
let _ = foo::<int>();
39+
let _ = foo::<MustUse>();
40+
}

0 commit comments

Comments
 (0)