Simplify has_debug_impl

This commit is contained in:
Florian Hartwig 2015-11-19 20:13:36 +01:00
parent 516f648460
commit 096c064d43
3 changed files with 27 additions and 66 deletions

View file

@ -85,7 +85,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
reg.register_late_lint_pass(box unicode::Unicode);
reg.register_late_lint_pass(box strings::StringAdd);
reg.register_early_lint_pass(box returns::ReturnPass);
reg.register_late_lint_pass(box methods::MethodsPass::new());
reg.register_late_lint_pass(box methods::MethodsPass);
reg.register_late_lint_pass(box shadow::ShadowPass);
reg.register_late_lint_pass(box types::LetPass);
reg.register_late_lint_pass(box types::UnitCmp);

View file

@ -4,7 +4,6 @@ use rustc::middle::ty;
use rustc::middle::subst::{Subst, TypeSpace};
use std::iter;
use std::borrow::Cow;
use std::collections::HashSet;
use utils::{snippet, span_lint, match_path, match_type, walk_ptrs_ty_depth,
walk_ptrs_ty};
@ -13,69 +12,8 @@ use utils::{OPTION_PATH, RESULT_PATH, STRING_PATH};
use self::SelfKind::*;
use self::OutType::*;
use rustc::middle::def_id::DefId;
use rustc::middle::ty::TypeFlags;
#[derive(Clone)]
pub struct MethodsPass { types_implementing_debug: Option<HashSet<DefId>> }
impl MethodsPass {
pub fn new() -> MethodsPass {
MethodsPass { types_implementing_debug: None }
}
fn get_debug_impls(&mut self, cx: &LateContext) -> Option<&HashSet<DefId>> {
if self.types_implementing_debug.is_none() {
let debug = match cx.tcx.lang_items.debug_trait() {
Some(debug) => debug,
None => return None
};
let debug_def = cx.tcx.lookup_trait_def(debug);
let mut impls = HashSet::new();
debug_def.for_each_impl(cx.tcx, |d| {
let o_self_ty = &cx.tcx.impl_trait_ref(d)
.map(|x| x.substs)
.and_then(|x| x.self_ty());
let self_ty = match *o_self_ty {
Some(self_type) => self_type,
None => return
};
let self_ty_def_id = self_ty.ty_to_def_id();
if let Some(self_ty_def_id) = self_ty_def_id {
let has_params = self_ty.flags.get().contains(TypeFlags::HAS_PARAMS);
if !has_params {
impls.insert(self_ty_def_id);
}
}
});
self.types_implementing_debug = Some(impls);
}
self.types_implementing_debug.as_ref()
}
// This checks whether a given type is known to implement Debug. It's
// conservative, i.e. it should not return false positives, but will return
// false negatives.
fn has_debug_impl(&mut self, ty: ty::Ty, cx: &LateContext) -> bool {
let debug_impls = match self.get_debug_impls(cx) {
Some(debug_impls) => debug_impls,
None => return false
};
match walk_ptrs_ty(ty).sty {
ty::TyBool | ty::TyChar | ty::TyInt(..) | ty::TyUint(..)
| ty::TyFloat(..) | ty::TyStr => true,
ty::TyTuple(ref v) if v.is_empty() => true,
ty::TyStruct(..) | ty::TyEnum(..) => {
match ty.ty_to_def_id() {
Some(ref ty_def_id) => debug_impls.contains(ty_def_id),
None => false
}
},
_ => false
}
}
}
pub struct MethodsPass;
declare_lint!(pub OPTION_UNWRAP_USED, Allow,
"using `Option.unwrap()`, which should at least get a better message using `expect()`");
@ -144,7 +82,7 @@ impl LateLintPass for MethodsPass {
&& match_type(cx, cx.tcx.expr_ty(&inner_args[0]), &RESULT_PATH) {
let result_type = cx.tcx.expr_ty(&inner_args[0]);
if let Some(error_type) = get_error_type(cx, result_type) {
if self.has_debug_impl(error_type, cx) {
if has_debug_impl(error_type, cx) {
span_lint(cx, OK_EXPECT, expr.span,
"called `ok().expect()` on a Result \
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>> {
None
}
// This checks whether a given type is known to implement Debug. It's
// conservative, i.e. it should not return false positives, but will return
// false negatives.
fn has_debug_impl<'a, 'b>(ty: ty::Ty<'a>, cx: &LateContext<'b, 'a>) -> bool {
let ty = walk_ptrs_ty(ty);
let debug = match cx.tcx.lang_items.debug_trait() {
Some(debug) => debug,
None => return false
};
let debug_def = cx.tcx.lookup_trait_def(debug);
let mut debug_impl_exists = false;
debug_def.for_each_relevant_impl(cx.tcx, ty, |d| {
let self_ty = &cx.tcx.impl_trait_ref(d).and_then(|im| im.substs.self_ty());
if let Some(self_ty) = *self_ty {
if !self_ty.flags.get().contains(ty::TypeFlags::HAS_PARAMS) {
debug_impl_exists = true;
}
}
});
debug_impl_exists
}
const CONVENTIONS: [(&'static str, &'static [SelfKind]); 5] = [
("into_", &[ValueSelf]),

View file

@ -54,7 +54,7 @@ fn main() {
// the error type implements `Debug`
let res2: Result<i32, MyError> = Ok(0);
res2.ok().expect("oh noes!");
// we're currently don't warn if the error type has a type parameter
// we currently don't warn if the error type has a type parameter
// (but it would be nice if we did)
let res3: Result<u32, MyErrorWithParam<u8>>= Ok(0);
res3.ok().expect("whoof");
@ -62,6 +62,8 @@ fn main() {
res4.ok().expect("argh"); //~ERROR called `ok().expect()`
let res5: io::Result<u32> = Ok(0);
res5.ok().expect("oops"); //~ERROR called `ok().expect()`
let res6: Result<u32, &str> = Ok(0);
res6.ok().expect("meh"); //~ERROR called `ok().expect()`
}
struct MyError(()); // doesn't implement Debug