utils: add match_type() helper function

which saves one level of matching when checking for type paths
This commit is contained in:
Georg Brandl 2015-08-21 19:00:33 +02:00
parent a437936d49
commit 8a10440641
6 changed files with 53 additions and 63 deletions

View File

@ -2,7 +2,7 @@ use syntax::ast::*;
use rustc::lint::*;
use rustc::middle::ty;
use utils::{span_lint, match_def_path, walk_ptrs_ty};
use utils::{span_lint, match_type, walk_ptrs_ty};
use utils::{OPTION_PATH, RESULT_PATH, STRING_PATH};
#[derive(Copy,Clone)]
@ -24,31 +24,24 @@ impl LintPass for MethodsPass {
fn check_expr(&mut self, cx: &Context, expr: &Expr) {
if let ExprMethodCall(ref ident, _, ref args) = expr.node {
let ref obj_ty = walk_ptrs_ty(cx.tcx.expr_ty(&*args[0])).sty;
let obj_ty = walk_ptrs_ty(cx.tcx.expr_ty(&*args[0]));
if ident.node.name == "unwrap" {
if let ty::TyEnum(did, _) = *obj_ty {
if match_def_path(cx, did.did, &OPTION_PATH) {
span_lint(cx, OPTION_UNWRAP_USED, expr.span,
"used unwrap() on an Option value. If you don't want \
to handle the None case gracefully, consider using
expect() to provide a better panic message");
}
else if match_def_path(cx, did.did, &RESULT_PATH) {
span_lint(cx, RESULT_UNWRAP_USED, expr.span,
"used unwrap() on a Result value. Graceful handling \
of Err values is preferred");
}
if match_type(cx, obj_ty, &OPTION_PATH) {
span_lint(cx, OPTION_UNWRAP_USED, expr.span,
"used unwrap() on an Option value. If you don't want \
to handle the None case gracefully, consider using \
expect() to provide a better panic message");
} else if match_type(cx, obj_ty, &RESULT_PATH) {
span_lint(cx, RESULT_UNWRAP_USED, expr.span,
"used unwrap() on a Result value. Graceful handling \
of Err values is preferred");
}
}
else if ident.node.name == "to_string" {
if let ty::TyStr = *obj_ty {
if obj_ty.sty == ty::TyStr {
span_lint(cx, STR_TO_STRING, expr.span, "`str.to_owned()` is faster");
}
else if let ty::TyStruct(did, _) = *obj_ty {
if match_def_path(cx, did.did, &STRING_PATH) {
span_lint(cx, STRING_TO_STRING, expr.span,
"`String.to_string()` is a no-op")
}
} else if match_type(cx, obj_ty, &STRING_PATH) {
span_lint(cx, STRING_TO_STRING, expr.span, "`String.to_string()` is a no-op");
}
}
}

View File

@ -6,7 +6,7 @@ use rustc::lint::*;
use syntax::ast::*;
use rustc::middle::ty;
use utils::{span_lint, match_def_path};
use utils::{span_lint, match_type};
use utils::{STRING_PATH, VEC_PATH};
declare_lint! {
@ -50,18 +50,15 @@ fn check_fn(cx: &Context, decl: &FnDecl) {
}
let ref sty = cx.tcx.pat_ty(&*arg.pat).sty;
if let &ty::TyRef(_, ty::TypeAndMut { ty, mutbl: MutImmutable }) = sty {
if let ty::TyStruct(did, _) = ty.sty {
if match_def_path(cx, did.did, &VEC_PATH) {
span_lint(cx, PTR_ARG, arg.ty.span,
"writing `&Vec<_>` instead of `&[_]` involves one more reference \
and cannot be used with non-Vec-based slices. Consider changing \
the type to `&[...]`");
}
else if match_def_path(cx, did.did, &STRING_PATH) {
span_lint(cx, PTR_ARG, arg.ty.span,
"writing `&String` instead of `&str` involves a new object \
where a slice will do. Consider changing the type to `&str`");
}
if match_type(cx, ty, &VEC_PATH) {
span_lint(cx, PTR_ARG, arg.ty.span,
"writing `&Vec<_>` instead of `&[_]` involves one more reference \
and cannot be used with non-Vec-based slices. Consider changing \
the type to `&[...]`");
} else if match_type(cx, ty, &STRING_PATH) {
span_lint(cx, PTR_ARG, arg.ty.span,
"writing `&String` instead of `&str` involves a new object \
where a slice will do. Consider changing the type to `&str`");
}
}
}

View File

@ -1,8 +1,7 @@
use rustc::lint::{Context, LintArray, LintPass};
use rustc::middle::ty::TypeVariants::TyStruct;
use syntax::ast::*;
use syntax::codemap::Spanned;
use utils::{match_def_path};
use utils::match_type;
declare_lint! {
pub RANGE_STEP_BY_ZERO, Warn,
@ -34,11 +33,9 @@ impl LintPass for StepByZero {
fn is_range(cx: &Context, expr: &Expr) -> bool {
// No need for walk_ptrs_ty here because step_by moves self, so it
// can't be called on a borrowed range.
if let TyStruct(did, _) = cx.tcx.expr_ty(expr).sty {
// Note: RangeTo and RangeFull don't have step_by
match_def_path(cx, did.did, &["core", "ops", "Range"]) ||
match_def_path(cx, did.did, &["core", "ops", "RangeFrom"])
} else { false }
let ty = cx.tcx.expr_ty(expr);
// Note: RangeTo and RangeFull don't have step_by
match_type(cx, ty, &["core", "ops", "Range"]) || match_type(cx, ty, &["core", "ops", "RangeFrom"])
}
fn is_lit_zero(expr: &Expr) -> bool {

View File

@ -4,12 +4,11 @@
//! disable the subsumed lint unless it has a higher level
use rustc::lint::*;
use rustc::middle::ty::TypeVariants::TyStruct;
use syntax::ast::*;
use syntax::codemap::Spanned;
use eq_op::is_exp_equal;
use utils::{match_def_path, span_lint, walk_ptrs_ty, get_parent_expr};
use utils::{match_type, span_lint, walk_ptrs_ty, get_parent_expr};
use utils::STRING_PATH;
declare_lint! {
@ -62,10 +61,7 @@ impl LintPass for StringAdd {
}
fn is_string(cx: &Context, e: &Expr) -> bool {
let ty = walk_ptrs_ty(cx.tcx.expr_ty(e));
if let TyStruct(did, _) = ty.sty {
match_def_path(cx, did.did, &STRING_PATH)
} else { false }
match_type(cx, walk_ptrs_ty(cx.tcx.expr_ty(e)), &STRING_PATH)
}
fn is_add(cx: &Context, src: &Expr, target: &Expr) -> bool {

View File

@ -5,7 +5,7 @@ use syntax::ast_util::{is_comparison_binop, binop_to_string};
use rustc::middle::ty;
use syntax::codemap::ExpnInfo;
use utils::{in_macro, match_def_path, snippet, span_lint, span_help_and_lint, in_external_macro};
use utils::{in_macro, match_type, snippet, span_lint, span_help_and_lint, in_external_macro};
use utils::{LL_PATH, VEC_PATH};
/// Handles all the linting of funky types
@ -26,23 +26,18 @@ impl LintPass for TypePass {
fn check_ty(&mut self, cx: &Context, ast_ty: &ast::Ty) {
if let Some(ty) = cx.tcx.ast_ty_to_ty_cache.borrow().get(&ast_ty.id) {
if let ty::TyBox(ref inner) = ty.sty {
if let ty::TyStruct(did, _) = inner.sty {
if match_def_path(cx, did.did, &VEC_PATH) {
span_help_and_lint(
cx, BOX_VEC, ast_ty.span,
"you seem to be trying to use `Box<Vec<T>>`. Did you mean to use `Vec<T>`?",
"`Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation");
}
if match_type(cx, inner, &VEC_PATH) {
span_help_and_lint(
cx, BOX_VEC, ast_ty.span,
"you seem to be trying to use `Box<Vec<T>>`. Did you mean to use `Vec<T>`?",
"`Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation");
}
}
if let ty::TyStruct(did, _) = ty.sty {
if match_def_path(cx, did.did, &LL_PATH) {
span_help_and_lint(
cx, LINKEDLIST, ast_ty.span,
"I see you're using a LinkedList! Perhaps you meant some other data structure?",
"a RingBuf might work");
return;
}
else if match_type(cx, ty, &LL_PATH) {
span_help_and_lint(
cx, LINKEDLIST, ast_ty.span,
"I see you're using a LinkedList! Perhaps you meant some other data structure?",
"a RingBuf might work");
}
}
}

View File

@ -44,6 +44,18 @@ pub fn match_def_path(cx: &Context, def_id: DefId, path: &[&str]) -> bool {
.zip(path.iter()).all(|(nm, p)| nm == p))
}
/// check if type is struct or enum type with given def path
pub fn match_type(cx: &Context, ty: ty::Ty, path: &[&str]) -> bool {
match ty.sty {
ty::TyEnum(ref adt, _) | ty::TyStruct(ref adt, _) => {
match_def_path(cx, adt.did, path)
}
_ => {
false
}
}
}
/// match a Path against a slice of segment string literals, e.g.
/// `match_path(path, &["std", "rt", "begin_unwind"])`
pub fn match_path(path: &Path, segments: &[&str]) -> bool {