Skip to content

Commit 096c064

Browse files
committed
Simplify has_debug_impl
1 parent 516f648 commit 096c064

File tree

3 files changed

+27
-66
lines changed

3 files changed

+27
-66
lines changed

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
8585
reg.register_late_lint_pass(box unicode::Unicode);
8686
reg.register_late_lint_pass(box strings::StringAdd);
8787
reg.register_early_lint_pass(box returns::ReturnPass);
88-
reg.register_late_lint_pass(box methods::MethodsPass::new());
88+
reg.register_late_lint_pass(box methods::MethodsPass);
8989
reg.register_late_lint_pass(box shadow::ShadowPass);
9090
reg.register_late_lint_pass(box types::LetPass);
9191
reg.register_late_lint_pass(box types::UnitCmp);

src/methods.rs

Lines changed: 23 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use rustc::middle::ty;
44
use rustc::middle::subst::{Subst, TypeSpace};
55
use std::iter;
66
use std::borrow::Cow;
7-
use std::collections::HashSet;
87

98
use utils::{snippet, span_lint, match_path, match_type, walk_ptrs_ty_depth,
109
walk_ptrs_ty};
@@ -13,69 +12,8 @@ use utils::{OPTION_PATH, RESULT_PATH, STRING_PATH};
1312
use self::SelfKind::*;
1413
use self::OutType::*;
1514

16-
use rustc::middle::def_id::DefId;
17-
18-
use rustc::middle::ty::TypeFlags;
19-
2015
#[derive(Clone)]
21-
pub struct MethodsPass { types_implementing_debug: Option<HashSet<DefId>> }
22-
23-
impl MethodsPass {
24-
pub fn new() -> MethodsPass {
25-
MethodsPass { types_implementing_debug: None }
26-
}
27-
28-
fn get_debug_impls(&mut self, cx: &LateContext) -> Option<&HashSet<DefId>> {
29-
if self.types_implementing_debug.is_none() {
30-
let debug = match cx.tcx.lang_items.debug_trait() {
31-
Some(debug) => debug,
32-
None => return None
33-
};
34-
let debug_def = cx.tcx.lookup_trait_def(debug);
35-
let mut impls = HashSet::new();
36-
debug_def.for_each_impl(cx.tcx, |d| {
37-
let o_self_ty = &cx.tcx.impl_trait_ref(d)
38-
.map(|x| x.substs)
39-
.and_then(|x| x.self_ty());
40-
let self_ty = match *o_self_ty {
41-
Some(self_type) => self_type,
42-
None => return
43-
};
44-
let self_ty_def_id = self_ty.ty_to_def_id();
45-
if let Some(self_ty_def_id) = self_ty_def_id {
46-
let has_params = self_ty.flags.get().contains(TypeFlags::HAS_PARAMS);
47-
if !has_params {
48-
impls.insert(self_ty_def_id);
49-
}
50-
}
51-
});
52-
self.types_implementing_debug = Some(impls);
53-
}
54-
self.types_implementing_debug.as_ref()
55-
}
56-
57-
// This checks whether a given type is known to implement Debug. It's
58-
// conservative, i.e. it should not return false positives, but will return
59-
// false negatives.
60-
fn has_debug_impl(&mut self, ty: ty::Ty, cx: &LateContext) -> bool {
61-
let debug_impls = match self.get_debug_impls(cx) {
62-
Some(debug_impls) => debug_impls,
63-
None => return false
64-
};
65-
match walk_ptrs_ty(ty).sty {
66-
ty::TyBool | ty::TyChar | ty::TyInt(..) | ty::TyUint(..)
67-
| ty::TyFloat(..) | ty::TyStr => true,
68-
ty::TyTuple(ref v) if v.is_empty() => true,
69-
ty::TyStruct(..) | ty::TyEnum(..) => {
70-
match ty.ty_to_def_id() {
71-
Some(ref ty_def_id) => debug_impls.contains(ty_def_id),
72-
None => false
73-
}
74-
},
75-
_ => false
76-
}
77-
}
78-
}
16+
pub struct MethodsPass;
7917

8018
declare_lint!(pub OPTION_UNWRAP_USED, Allow,
8119
"using `Option.unwrap()`, which should at least get a better message using `expect()`");
@@ -144,7 +82,7 @@ impl LateLintPass for MethodsPass {
14482
&& match_type(cx, cx.tcx.expr_ty(&inner_args[0]), &RESULT_PATH) {
14583
let result_type = cx.tcx.expr_ty(&inner_args[0]);
14684
if let Some(error_type) = get_error_type(cx, result_type) {
147-
if self.has_debug_impl(error_type, cx) {
85+
if has_debug_impl(error_type, cx) {
14886
span_lint(cx, OK_EXPECT, expr.span,
14987
"called `ok().expect()` on a Result \
15088
value. You can call `expect` directly
@@ -212,6 +150,27 @@ fn get_error_type<'a>(cx: &LateContext, ty: ty::Ty<'a>) -> Option<ty::Ty<'a>> {
212150
None
213151
}
214152

153+
// This checks whether a given type is known to implement Debug. It's
154+
// conservative, i.e. it should not return false positives, but will return
155+
// false negatives.
156+
fn has_debug_impl<'a, 'b>(ty: ty::Ty<'a>, cx: &LateContext<'b, 'a>) -> bool {
157+
let ty = walk_ptrs_ty(ty);
158+
let debug = match cx.tcx.lang_items.debug_trait() {
159+
Some(debug) => debug,
160+
None => return false
161+
};
162+
let debug_def = cx.tcx.lookup_trait_def(debug);
163+
let mut debug_impl_exists = false;
164+
debug_def.for_each_relevant_impl(cx.tcx, ty, |d| {
165+
let self_ty = &cx.tcx.impl_trait_ref(d).and_then(|im| im.substs.self_ty());
166+
if let Some(self_ty) = *self_ty {
167+
if !self_ty.flags.get().contains(ty::TypeFlags::HAS_PARAMS) {
168+
debug_impl_exists = true;
169+
}
170+
}
171+
});
172+
debug_impl_exists
173+
}
215174

216175
const CONVENTIONS: [(&'static str, &'static [SelfKind]); 5] = [
217176
("into_", &[ValueSelf]),

tests/compile-fail/methods.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,16 @@ fn main() {
5454
// the error type implements `Debug`
5555
let res2: Result<i32, MyError> = Ok(0);
5656
res2.ok().expect("oh noes!");
57-
// we're currently don't warn if the error type has a type parameter
57+
// we currently don't warn if the error type has a type parameter
5858
// (but it would be nice if we did)
5959
let res3: Result<u32, MyErrorWithParam<u8>>= Ok(0);
6060
res3.ok().expect("whoof");
6161
let res4: Result<u32, io::Error> = Ok(0);
6262
res4.ok().expect("argh"); //~ERROR called `ok().expect()`
6363
let res5: io::Result<u32> = Ok(0);
6464
res5.ok().expect("oops"); //~ERROR called `ok().expect()`
65+
let res6: Result<u32, &str> = Ok(0);
66+
res6.ok().expect("meh"); //~ERROR called `ok().expect()`
6567
}
6668

6769
struct MyError(()); // doesn't implement Debug

0 commit comments

Comments
 (0)