Merge pull request #2299 from mikerite/option_option_pr

Implemented option_option lint
This commit is contained in:
Oliver Schneider 2018-01-19 08:25:17 +01:00 committed by GitHub
commit 8a53d90241
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 181 additions and 20 deletions

View File

@ -605,6 +605,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
types::IMPLICIT_HASHER,
types::LET_UNIT_VALUE,
types::LINKEDLIST,
types::OPTION_OPTION,
types::TYPE_COMPLEXITY,
types::UNIT_CMP,
types::UNNECESSARY_CAST,

View File

@ -50,6 +50,26 @@ declare_lint! {
"usage of `Box<Vec<T>>`, vector elements are already on the heap"
}
/// **What it does:** Checks for use of `Option<Option<_>>` in function signatures and type
/// definitions
///
/// **Why is this bad?** `Option<_>` represents an optional value. `Option<Option<_>>`
/// represents an optional optional value which is logically the same thing as an optional
/// value but has an unneeded extra level of wrapping.
///
/// **Known problems:** None.
///
/// **Example**
/// ```rust
/// fn x() -> Option<Option<u32>> {
/// None
/// }
declare_lint! {
pub OPTION_OPTION,
Warn,
"usage of `Option<Option<T>>`"
}
/// **What it does:** Checks for usage of any `LinkedList`, suggesting to use a
/// `Vec` or a `VecDeque` (formerly called `RingBuf`).
///
@ -111,7 +131,7 @@ declare_lint! {
impl LintPass for TypePass {
fn get_lints(&self) -> LintArray {
lint_array!(BOX_VEC, LINKEDLIST, BORROWED_BOX)
lint_array!(BOX_VEC, OPTION_OPTION, LINKEDLIST, BORROWED_BOX)
}
}
@ -156,6 +176,23 @@ fn check_fn_decl(cx: &LateContext, decl: &FnDecl) {
}
}
/// Check if `qpath` has last segment with type parameter matching `path`
fn match_type_parameter(cx: &LateContext, qpath: &QPath, path: &[&str]) -> bool {
let last = last_path_segment(qpath);
if_chain! {
if let Some(ref params) = last.parameters;
if !params.parenthesized;
if let Some(ty) = params.types.get(0);
if let TyPath(ref qpath) = ty.node;
if let Some(did) = opt_def_id(cx.tables.qpath_def(qpath, cx.tcx.hir.node_to_hir_id(ty.id)));
if match_def_path(cx.tcx, did, path);
then {
return true;
}
}
false
}
/// Recursively check for `TypePass` lints in the given type. Stop at the first
/// lint found.
///
@ -171,15 +208,7 @@ fn check_ty(cx: &LateContext, ast_ty: &hir::Ty, is_local: bool) {
let def = cx.tables.qpath_def(qpath, hir_id);
if let Some(def_id) = opt_def_id(def) {
if Some(def_id) == cx.tcx.lang_items().owned_box() {
let last = last_path_segment(qpath);
if_chain! {
if let Some(ref params) = last.parameters;
if !params.parenthesized;
if let Some(vec) = params.types.get(0);
if let TyPath(ref qpath) = vec.node;
if let Some(did) = opt_def_id(cx.tables.qpath_def(qpath, cx.tcx.hir.node_to_hir_id(vec.id)));
if match_def_path(cx.tcx, did, &paths::VEC);
then {
if match_type_parameter(cx, qpath, &paths::VEC) {
span_help_and_lint(
cx,
BOX_VEC,
@ -189,6 +218,16 @@ fn check_ty(cx: &LateContext, ast_ty: &hir::Ty, is_local: bool) {
);
return; // don't recurse into the type
}
} else if match_def_path(cx.tcx, def_id, &paths::OPTION) {
if match_type_parameter(cx, qpath, &paths::OPTION) {
span_lint(
cx,
OPTION_OPTION,
ast_ty.span,
"consider using `Option<T>` instead of `Option<Option<T>>` or a custom \
enum if you need to distinguish all 3 cases",
);
return; // don't recurse into the type
}
} else if match_def_path(cx.tcx, def_id, &paths::LINKED_LIST) {
span_help_and_lint(

View File

@ -2,7 +2,7 @@
#![warn(needless_pass_by_value)]
#![allow(dead_code, single_match, if_let_redundant_pattern_matching, many_single_char_names)]
#![allow(dead_code, single_match, if_let_redundant_pattern_matching, many_single_char_names, option_option)]
#![feature(collections_range)]

63
tests/ui/option_option.rs Normal file
View File

@ -0,0 +1,63 @@
fn input(_: Option<Option<u8>>) {
}
fn output() -> Option<Option<u8>> {
None
}
fn output_nested() -> Vec<Option<Option<u8>>> {
vec![None]
}
// The lint only generates one warning for this
fn output_nested_nested() -> Option<Option<Option<u8>>> {
None
}
struct Struct {
x: Option<Option<u8>>,
}
impl Struct {
fn struct_fn() -> Option<Option<u8>> {
None
}
}
trait Trait {
fn trait_fn() -> Option<Option<u8>>;
}
enum Enum {
Tuple(Option<Option<u8>>),
Struct{x: Option<Option<u8>>},
}
// The lint allows this
type OptionOption = Option<Option<u32>>;
// The lint allows this
fn output_type_alias() -> OptionOption {
None
}
// The line allows this
impl Trait for Struct {
fn trait_fn() -> Option<Option<u8>> {
None
}
}
fn main() {
input(None);
output();
output_nested();
// The lint allows this
let local: Option<Option<u8>> = None;
// The lint allows this
let expr = Some(Some(true));
}

View File

@ -0,0 +1,58 @@
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
--> $DIR/option_option.rs:1:13
|
1 | fn input(_: Option<Option<u8>>) {
| ^^^^^^^^^^^^^^^^^^
|
= note: `-D option-option` implied by `-D warnings`
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
--> $DIR/option_option.rs:4:16
|
4 | fn output() -> Option<Option<u8>> {
| ^^^^^^^^^^^^^^^^^^
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
--> $DIR/option_option.rs:8:27
|
8 | fn output_nested() -> Vec<Option<Option<u8>>> {
| ^^^^^^^^^^^^^^^^^^
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
--> $DIR/option_option.rs:13:30
|
13 | fn output_nested_nested() -> Option<Option<Option<u8>>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
--> $DIR/option_option.rs:18:8
|
18 | x: Option<Option<u8>>,
| ^^^^^^^^^^^^^^^^^^
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
--> $DIR/option_option.rs:22:23
|
22 | fn struct_fn() -> Option<Option<u8>> {
| ^^^^^^^^^^^^^^^^^^
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
--> $DIR/option_option.rs:28:22
|
28 | fn trait_fn() -> Option<Option<u8>>;
| ^^^^^^^^^^^^^^^^^^
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
--> $DIR/option_option.rs:32:11
|
32 | Tuple(Option<Option<u8>>),
| ^^^^^^^^^^^^^^^^^^
error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
--> $DIR/option_option.rs:33:15
|
33 | Struct{x: Option<Option<u8>>},
| ^^^^^^^^^^^^^^^^^^
error: aborting due to 9 previous errors