+mod inefficient_to_string;
+mod manual_saturating_arithmetic;
mod option_map_unwrap_or;
mod unnecessary_filter_map;
use rustc_errors::Applicability;
use syntax::ast;
use syntax::source_map::Span;
-use syntax::symbol::LocalInternedString;
+use syntax::symbol::{sym, LocalInternedString, Symbol};
-use crate::utils::sugg;
use crate::utils::usage::mutated_variables;
use crate::utils::{
get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, in_macro, is_copy,
- is_ctor_function, is_expn_of, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method,
- match_type, match_var, method_calls, method_chain_args, remove_blocks, return_ty, same_tys, single_segment_path,
- snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_sugg,
- span_lint_and_then, span_note_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
+ is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment,
+ match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths,
+ remove_blocks, return_ty, same_tys, single_segment_path, snippet, snippet_with_applicability,
+ snippet_with_macro_callsite, span_help_and_lint, span_lint, span_lint_and_sugg, span_lint_and_then,
+ span_note_and_lint, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq,
};
-use crate::utils::{paths, span_help_and_lint};
declare_clippy_lint! {
/// **What it does:** Checks for `.unwrap()` calls on `Option`s.
/// **Known problems:** None.
///
/// **Example:**
- /// Using unwrap on an `Option`:
+ /// Using unwrap on an `Result`:
///
/// ```rust
/// let res: Result<usize, ()> = Ok(1);
"using `Result.unwrap()`, which might be better handled"
}
+declare_clippy_lint! {
+ /// **What it does:** Checks for `.expect()` calls on `Option`s.
+ ///
+ /// **Why is this bad?** Usually it is better to handle the `None` case. Still,
+ /// for a lot of quick-and-dirty code, `expect` is a good choice, which is why
+ /// this lint is `Allow` by default.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// Using expect on an `Option`:
+ ///
+ /// ```rust
+ /// let opt = Some(1);
+ /// opt.expect("one");
+ /// ```
+ ///
+ /// Better:
+ ///
+ /// ```ignore
+ /// let opt = Some(1);
+ /// opt?;
+ /// # Some::<()>(())
+ /// ```
+ pub OPTION_EXPECT_USED,
+ restriction,
+ "using `Option.expect()`, which might be better handled"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `.expect()` calls on `Result`s.
+ ///
+ /// **Why is this bad?** `result.expect()` will let the thread panic on `Err`
+ /// values. Normally, you want to implement more sophisticated error handling,
+ /// and propagate errors upwards with `try!`.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// Using expect on an `Result`:
+ ///
+ /// ```rust
+ /// let res: Result<usize, ()> = Ok(1);
+ /// res.expect("one");
+ /// ```
+ ///
+ /// Better:
+ ///
+ /// ```
+ /// let res: Result<usize, ()> = Ok(1);
+ /// res?;
+ /// # Ok::<(), ()>(())
+ /// ```
+ pub RESULT_EXPECT_USED,
+ restriction,
+ "using `Result.expect()`, which might be better handled"
+}
+
declare_clippy_lint! {
/// **What it does:** Checks for methods that should live in a trait
/// implementation of a `std` trait (see [llogiq's blog
"using `clone` on `&&T`"
}
+declare_clippy_lint! {
+ /// **What it does:** Checks for usage of `.to_string()` on an `&&T` where
+ /// `T` implements `ToString` directly (like `&&str` or `&&String`).
+ ///
+ /// **Why is this bad?** This bypasses the specialized implementation of
+ /// `ToString` and instead goes through the more expensive string formatting
+ /// facilities.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ /// ```rust
+ /// // Generic implementation for `T: Display` is used (slow)
+ /// ["foo", "bar"].iter().map(|s| s.to_string());
+ ///
+ /// // OK, the specialized impl is used
+ /// ["foo", "bar"].iter().map(|&s| s.to_string());
+ /// ```
+ pub INEFFICIENT_TO_STRING,
+ perf,
+ "using `to_string` on `&&T` where `T: ToString`"
+}
+
declare_clippy_lint! {
/// **What it does:** Checks for `new` not returning `Self`.
///
"suspicious usage of map"
}
+declare_clippy_lint! {
+ /// **What it does:** Checks for `MaybeUninit::uninit().assume_init()`.
+ ///
+ /// **Why is this bad?** For most types, this is undefined behavior.
+ ///
+ /// **Known problems:** For now, we accept empty tuples and tuples / arrays
+ /// of `MaybeUninit`. There may be other types that allow uninitialized
+ /// data, but those are not yet rigorously defined.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// // Beware the UB
+ /// use std::mem::MaybeUninit;
+ ///
+ /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
+ /// ```
+ ///
+ /// Note that the following is OK:
+ ///
+ /// ```rust
+ /// use std::mem::MaybeUninit;
+ ///
+ /// let _: [MaybeUninit<bool>; 5] = unsafe {
+ /// MaybeUninit::uninit().assume_init()
+ /// };
+ /// ```
+ pub UNINIT_ASSUMED_INIT,
+ correctness,
+ "`MaybeUninit::uninit().assume_init()`"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
+ ///
+ /// **Why is this bad?** These can be written simply with `saturating_add/sub` methods.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// # let y: u32 = 0;
+ /// # let x: u32 = 100;
+ /// let add = x.checked_add(y).unwrap_or(u32::max_value());
+ /// let sub = x.checked_sub(y).unwrap_or(u32::min_value());
+ /// ```
+ ///
+ /// can be written using dedicated methods for saturating addition/subtraction as:
+ ///
+ /// ```rust
+ /// # let y: u32 = 0;
+ /// # let x: u32 = 100;
+ /// let add = x.saturating_add(y);
+ /// let sub = x.saturating_sub(y);
+ /// ```
+ pub MANUAL_SATURATING_ARITHMETIC,
+ style,
+ "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`"
+}
+
declare_lint_pass!(Methods => [
OPTION_UNWRAP_USED,
RESULT_UNWRAP_USED,
+ OPTION_EXPECT_USED,
+ RESULT_EXPECT_USED,
SHOULD_IMPLEMENT_TRAIT,
WRONG_SELF_CONVENTION,
WRONG_PUB_SELF_CONVENTION,
CLONE_ON_COPY,
CLONE_ON_REF_PTR,
CLONE_DOUBLE_REF,
+ INEFFICIENT_TO_STRING,
NEW_RET_NO_SELF,
SINGLE_CHAR_PATTERN,
SEARCH_IS_SOME,
INTO_ITER_ON_ARRAY,
INTO_ITER_ON_REF,
SUSPICIOUS_MAP,
+ UNINIT_ASSUMED_INIT,
+ MANUAL_SATURATING_ARITHMETIC,
]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
return;
}
- let (method_names, arg_lists) = method_calls(expr, 2);
+ let (method_names, arg_lists, method_spans) = method_calls(expr, 2);
let method_names: Vec<LocalInternedString> = method_names.iter().map(|s| s.as_str()).collect();
let method_names: Vec<&str> = method_names.iter().map(std::convert::AsRef::as_ref).collect();
["unwrap", "get_mut"] => lint_get_unwrap(cx, expr, arg_lists[1], true),
["unwrap", ..] => lint_unwrap(cx, expr, arg_lists[0]),
["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]),
+ ["expect", ..] => lint_expect(cx, expr, arg_lists[0]),
["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0]),
["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]),
["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]),
["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]),
["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]),
- ["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0]),
+ ["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]),
["flatten", "map"] => lint_map_flatten(cx, expr, arg_lists[1]),
- ["is_some", "find"] => lint_search_is_some(cx, expr, "find", arg_lists[1], arg_lists[0]),
- ["is_some", "position"] => lint_search_is_some(cx, expr, "position", arg_lists[1], arg_lists[0]),
- ["is_some", "rposition"] => lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0]),
+ ["is_some", "find"] => lint_search_is_some(cx, expr, "find", arg_lists[1], arg_lists[0], method_spans[1]),
+ ["is_some", "position"] => {
+ lint_search_is_some(cx, expr, "position", arg_lists[1], arg_lists[0], method_spans[1])
+ },
+ ["is_some", "rposition"] => {
+ lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1])
+ },
["extend", ..] => lint_extend(cx, expr, arg_lists[0]),
["as_ptr", "unwrap"] | ["as_ptr", "expect"] => {
lint_cstring_as_ptr(cx, expr, &arg_lists[1][0], &arg_lists[0][0])
["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]),
["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]),
["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]),
- ["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0]),
+ ["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0], method_spans[0]),
["filter_map", ..] => unnecessary_filter_map::lint(cx, expr, arg_lists[0]),
["count", "map"] => lint_suspicious_map(cx, expr),
+ ["assume_init"] => lint_maybe_uninit(cx, &arg_lists[0][0], expr),
+ ["unwrap_or", arith @ "checked_add"]
+ | ["unwrap_or", arith @ "checked_sub"]
+ | ["unwrap_or", arith @ "checked_mul"] => {
+ manual_saturating_arithmetic::lint(cx, expr, &arg_lists, &arith["checked_".len()..])
+ },
_ => {},
}
- match expr.node {
+ match expr.kind {
hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args) => {
lint_or_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
lint_expect_fun_call(cx, expr, *method_span, &method_call.ident.as_str(), args);
lint_clone_on_copy(cx, expr, &args[0], self_ty);
lint_clone_on_ref_ptr(cx, expr, &args[0]);
}
+ if args.len() == 1 && method_call.ident.name == sym!(to_string) {
+ inefficient_to_string::lint(cx, expr, &args[0], self_ty);
+ }
- match self_ty.sty {
- ty::Ref(_, ty, _) if ty.sty == ty::Str => {
+ match self_ty.kind {
+ ty::Ref(_, ty, _) if ty.kind == ty::Str => {
for &(method, pos) in &PATTERN_METHODS {
if method_call.ident.name.as_str() == method && args.len() > pos {
lint_single_char_pattern(cx, expr, &args[pos]);
let def_id = cx.tcx.hir().local_def_id(item.hir_id);
let ty = cx.tcx.type_of(def_id);
if_chain! {
- if let hir::ImplItemKind::Method(ref sig, id) = impl_item.node;
+ if let hir::ImplItemKind::Method(ref sig, id) = impl_item.kind;
if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next();
- if let hir::ItemKind::Impl(_, _, _, _, None, _, _) = item.node;
+ if let hir::ItemKind::Impl(_, _, _, _, None, _, _) = item.kind;
let method_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
let method_sig = cx.tcx.fn_sig(method_def_id);
}
}
- if let hir::ImplItemKind::Method(_, _) = impl_item.node {
+ if let hir::ImplItemKind::Method(_, _) = impl_item.kind {
let ret_ty = return_ty(cx, impl_item.hir_id);
// walk the return type and check for Self (this does not check associated types)
}
// if return type is impl trait, check the associated types
- if let ty::Opaque(def_id, _) = ret_ty.sty {
+ if let ty::Opaque(def_id, _) = ret_ty.kind {
// one of the associated types must be Self
- for predicate in &cx.tcx.predicates_of(def_id).predicates {
+ for predicate in cx.tcx.predicates_of(def_id).predicates {
match predicate {
(Predicate::Projection(poly_projection_predicate), _) => {
let binder = poly_projection_predicate.ty();
impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
- let call_found = match &expr.node {
+ let call_found = match &expr.kind {
// ignore enum and struct constructors
- hir::ExprKind::Call(..) => !is_ctor_function(self.cx, expr),
+ hir::ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr),
hir::ExprKind::MethodCall(..) => true,
_ => false,
};
if call_found {
- // don't lint for constant values
- let owner_def = self.cx.tcx.hir().get_parent_did(expr.hir_id);
- let promotable = self
- .cx
- .tcx
- .rvalue_promotable_map(owner_def)
- .contains(&expr.hir_id.local_id);
- if !promotable {
- self.found |= true;
- }
+ self.found |= true;
}
if !self.found {
if_chain! {
if !or_has_args;
if name == "unwrap_or";
- if let hir::ExprKind::Path(ref qpath) = fun.node;
+ if let hir::ExprKind::Path(ref qpath) = fun.kind;
let path = &*last_path_segment(qpath).ident.as_str();
if ["default", "new"].contains(&path);
let arg_ty = cx.tables.expr_ty(arg);
let mut finder = FunCallFinder { cx: &cx, found: false };
if { finder.visit_expr(&arg); finder.found };
+ if !contains_return(&arg);
let self_ty = cx.tables.expr_ty(self_expr);
}
if args.len() == 2 {
- match args[1].node {
+ match args[1].kind {
hir::ExprKind::Call(ref fun, ref or_args) => {
let or_has_args = !or_args.is_empty();
if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) {
fn get_arg_root<'a>(cx: &LateContext<'_, '_>, arg: &'a hir::Expr) -> &'a hir::Expr {
let mut arg_root = arg;
loop {
- arg_root = match &arg_root.node {
+ arg_root = match &arg_root.kind {
hir::ExprKind::AddrOf(_, expr) => expr,
hir::ExprKind::MethodCall(method_name, _, call_args) => {
if call_args.len() == 1
&& {
let arg_type = cx.tables.expr_ty(&call_args[0]);
let base_type = walk_ptrs_ty(arg_type);
- base_type.sty == ty::Str || match_type(cx, base_type, &paths::STRING)
+ base_type.kind == ty::Str || match_type(cx, base_type, &paths::STRING)
}
{
&call_args[0]
if match_type(cx, arg_ty, &paths::STRING) {
return false;
}
- if let ty::Ref(ty::ReStatic, ty, ..) = arg_ty.sty {
- if ty.sty == ty::Str {
+ if let ty::Ref(ty::ReStatic, ty, ..) = arg_ty.kind {
+ if ty.kind == ty::Str {
return false;
}
};
applicability: &mut Applicability,
) -> Vec<String> {
if_chain! {
- if let hir::ExprKind::AddrOf(_, ref format_arg) = a.node;
- if let hir::ExprKind::Match(ref format_arg_expr, _, _) = format_arg.node;
- if let hir::ExprKind::Tup(ref format_arg_expr_tup) = format_arg_expr.node;
+ if let hir::ExprKind::AddrOf(_, ref format_arg) = a.kind;
+ if let hir::ExprKind::Match(ref format_arg_expr, _, _) = format_arg.kind;
+ if let hir::ExprKind::Tup(ref format_arg_expr_tup) = format_arg_expr.kind;
then {
format_arg_expr_tup
fn is_call(node: &hir::ExprKind) -> bool {
match node {
hir::ExprKind::AddrOf(_, expr) => {
- is_call(&expr.node)
+ is_call(&expr.kind)
},
hir::ExprKind::Call(..)
| hir::ExprKind::MethodCall(..)
}
}
- if args.len() != 2 || name != "expect" || !is_call(&args[1].node) {
+ if args.len() != 2 || name != "expect" || !is_call(&args[1].kind) {
return;
}
let mut applicability = Applicability::MachineApplicable;
//Special handling for `format!` as arg_root
- if let hir::ExprKind::Call(ref inner_fun, ref inner_args) = arg_root.node {
+ if let hir::ExprKind::Call(ref inner_fun, ref inner_args) = arg_root.kind {
if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1 {
- if let hir::ExprKind::Call(_, format_args) = &inner_args[0].node {
+ if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind {
let fmt_spec = &format_args[0];
let fmt_args = &format_args[1];
/// Checks for the `CLONE_ON_COPY` lint.
fn lint_clone_on_copy(cx: &LateContext<'_, '_>, expr: &hir::Expr, arg: &hir::Expr, arg_ty: Ty<'_>) {
let ty = cx.tables.expr_ty(expr);
- if let ty::Ref(_, inner, _) = arg_ty.sty {
- if let ty::Ref(_, innermost, _) = inner.sty {
+ if let ty::Ref(_, inner, _) = arg_ty.kind {
+ if let ty::Ref(_, innermost, _) = inner.kind {
span_lint_and_then(
cx,
CLONE_DOUBLE_REF,
if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
let mut ty = innermost;
let mut n = 0;
- while let ty::Ref(_, inner, _) = ty.sty {
+ while let ty::Ref(_, inner, _) = ty.kind {
ty = inner;
n += 1;
}
if let Some(snippet) = sugg::Sugg::hir_opt(cx, arg) {
let parent = cx.tcx.hir().get_parent_node(expr.hir_id);
match &cx.tcx.hir().get(parent) {
- hir::Node::Expr(parent) => match parent.node {
+ hir::Node::Expr(parent) => match parent.kind {
// &*x is a nop, &x.clone() is not
hir::ExprKind::AddrOf(..) |
// (*x).func() is useless, x.clone().func() can work in case func borrows mutably
_ => {},
},
hir::Node::Stmt(stmt) => {
- if let hir::StmtKind::Local(ref loc) = stmt.node {
- if let hir::PatKind::Ref(..) = loc.pat.node {
+ if let hir::StmtKind::Local(ref loc) = stmt.kind {
+ if let hir::PatKind::Ref(..) = loc.pat.kind {
// let ref y = *x borrows x, let ref y = x.clone() does not
return;
}
fn lint_clone_on_ref_ptr(cx: &LateContext<'_, '_>, expr: &hir::Expr, arg: &hir::Expr) {
let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(arg));
- if let ty::Adt(_, subst) = obj_ty.sty {
+ if let ty::Adt(_, subst) = obj_ty.kind {
let caller_type = if match_type(cx, obj_ty, &paths::RC) {
"Rc"
} else if match_type(cx, obj_ty, &paths::ARC) {
if let Some(arglists) = method_chain_args(arg, &["chars"]) {
let target = &arglists[0][0];
let self_ty = walk_ptrs_ty(cx.tables.expr_ty(target));
- let ref_str = if self_ty.sty == ty::Str {
+ let ref_str = if self_ty.kind == ty::Str {
""
} else if match_type(cx, self_ty, &paths::STRING) {
"&"
fn lint_cstring_as_ptr(cx: &LateContext<'_, '_>, expr: &hir::Expr, source: &hir::Expr, unwrap: &hir::Expr) {
if_chain! {
let source_type = cx.tables.expr_ty(source);
- if let ty::Adt(def, substs) = source_type.sty;
+ if let ty::Adt(def, substs) = source_type.kind;
if match_def_path(cx, def.did, &paths::RESULT);
if match_type(cx, substs.type_at(0), &paths::CSTRING);
then {
fn lint_iter_cloned_collect<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr, iter_args: &'tcx [hir::Expr]) {
if_chain! {
- if match_type(cx, cx.tables.expr_ty(expr), &paths::VEC);
+ if is_type_diagnostic_item(cx, cx.tables.expr_ty(expr), Symbol::intern("vec_type"));
if let Some(slice) = derefs_to_slice(cx, &iter_args[0], cx.tables.expr_ty(&iter_args[0]));
if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite());
}
}
-fn lint_unnecessary_fold(cx: &LateContext<'_, '_>, expr: &hir::Expr, fold_args: &[hir::Expr]) {
+fn lint_unnecessary_fold(cx: &LateContext<'_, '_>, expr: &hir::Expr, fold_args: &[hir::Expr], fold_span: Span) {
fn check_fold_with_op(
cx: &LateContext<'_, '_>,
expr: &hir::Expr,
fold_args: &[hir::Expr],
+ fold_span: Span,
op: hir::BinOpKind,
replacement_method_name: &str,
replacement_has_args: bool,
) {
if_chain! {
// Extract the body of the closure passed to fold
- if let hir::ExprKind::Closure(_, _, body_id, _, _) = fold_args[2].node;
+ if let hir::ExprKind::Closure(_, _, body_id, _, _) = fold_args[2].kind;
let closure_body = cx.tcx.hir().body(body_id);
let closure_expr = remove_blocks(&closure_body.value);
// Check if the closure body is of the form `acc <op> some_expr(x)`
- if let hir::ExprKind::Binary(ref bin_op, ref left_expr, ref right_expr) = closure_expr.node;
+ if let hir::ExprKind::Binary(ref bin_op, ref left_expr, ref right_expr) = closure_expr.kind;
if bin_op.node == op;
// Extract the names of the two arguments to the closure
- if let Some(first_arg_ident) = get_arg_name(&closure_body.arguments[0].pat);
- if let Some(second_arg_ident) = get_arg_name(&closure_body.arguments[1].pat);
+ if let Some(first_arg_ident) = get_arg_name(&closure_body.params[0].pat);
+ if let Some(second_arg_ident) = get_arg_name(&closure_body.params[1].pat);
if match_var(&*left_expr, first_arg_ident);
if replacement_has_args || match_var(&*right_expr, second_arg_ident);
- if let hir::ExprKind::MethodCall(_, span, _) = &expr.node;
-
then {
let mut applicability = Applicability::MachineApplicable;
let sugg = if replacement_has_args {
span_lint_and_sugg(
cx,
UNNECESSARY_FOLD,
- span.with_hi(expr.span.hi()),
+ fold_span.with_hi(expr.span.hi()),
// TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f)
"this `.fold` can be written more succinctly using another method",
"try",
);
// Check if the first argument to .fold is a suitable literal
- if let hir::ExprKind::Lit(ref lit) = fold_args[1].node {
+ if let hir::ExprKind::Lit(ref lit) = fold_args[1].kind {
match lit.node {
- ast::LitKind::Bool(false) => check_fold_with_op(cx, expr, fold_args, hir::BinOpKind::Or, "any", true),
- ast::LitKind::Bool(true) => check_fold_with_op(cx, expr, fold_args, hir::BinOpKind::And, "all", true),
- ast::LitKind::Int(0, _) => check_fold_with_op(cx, expr, fold_args, hir::BinOpKind::Add, "sum", false),
- ast::LitKind::Int(1, _) => check_fold_with_op(cx, expr, fold_args, hir::BinOpKind::Mul, "product", false),
+ ast::LitKind::Bool(false) => {
+ check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Or, "any", true)
+ },
+ ast::LitKind::Bool(true) => {
+ check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::And, "all", true)
+ },
+ ast::LitKind::Int(0, _) => {
+ check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Add, "sum", false)
+ },
+ ast::LitKind::Int(1, _) => {
+ check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Mul, "product", false)
+ },
_ => (),
}
}
let mut_str = if is_mut { "_mut" } else { "" };
let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.tables.expr_ty(&iter_args[0])).is_some() {
"slice"
- } else if match_type(cx, cx.tables.expr_ty(&iter_args[0]), &paths::VEC) {
+ } else if is_type_diagnostic_item(cx, cx.tables.expr_ty(&iter_args[0]), Symbol::intern("vec_type")) {
"Vec"
} else if match_type(cx, cx.tables.expr_ty(&iter_args[0]), &paths::VEC_DEQUE) {
"VecDeque"
}
fn lint_get_unwrap<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr, get_args: &'tcx [hir::Expr], is_mut: bool) {
- // Note: we don't want to lint `get_mut().unwrap` for HashMap or BTreeMap,
+ // Note: we don't want to lint `get_mut().unwrap` for `HashMap` or `BTreeMap`,
// because they do not implement `IndexMut`
let mut applicability = Applicability::MachineApplicable;
let expr_ty = cx.tables.expr_ty(&get_args[0]);
let caller_type = if derefs_to_slice(cx, &get_args[0], expr_ty).is_some() {
needs_ref = get_args_str.parse::<usize>().is_ok();
"slice"
- } else if match_type(cx, expr_ty, &paths::VEC) {
+ } else if is_type_diagnostic_item(cx, expr_ty, Symbol::intern("vec_type")) {
needs_ref = get_args_str.parse::<usize>().is_ok();
"Vec"
} else if match_type(cx, expr_ty, &paths::VEC_DEQUE) {
if_chain! {
if needs_ref;
if let Some(parent) = get_parent_expr(cx, expr);
- if let hir::ExprKind::Unary(hir::UnOp::UnDeref, _) = parent.node;
+ if let hir::ExprKind::Unary(hir::UnOp::UnDeref, _) = parent.kind;
then {
needs_ref = false;
span = parent.span;
ty: Ty<'tcx>,
) -> Option<&'tcx hir::Expr> {
fn may_slice<'a>(cx: &LateContext<'_, 'a>, ty: Ty<'a>) -> bool {
- match ty.sty {
+ match ty.kind {
ty::Slice(_) => true,
ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
- ty::Adt(..) => match_type(cx, ty, &paths::VEC),
+ ty::Adt(..) => is_type_diagnostic_item(cx, ty, Symbol::intern("vec_type")),
ty::Array(_, size) => size.eval_usize(cx.tcx, cx.param_env) < 32,
ty::Ref(_, inner, _) => may_slice(cx, inner),
_ => false,
}
}
- if let hir::ExprKind::MethodCall(ref path, _, ref args) = expr.node {
+ if let hir::ExprKind::MethodCall(ref path, _, ref args) = expr.kind {
if path.ident.name == sym!(iter) && may_slice(cx, cx.tables.expr_ty(&args[0])) {
Some(&args[0])
} else {
None
}
} else {
- match ty.sty {
+ match ty.kind {
ty::Slice(_) => Some(expr),
ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
ty::Ref(_, inner, _) => {
}
}
+/// lint use of `expect()` for `Option`s and `Result`s
+fn lint_expect(cx: &LateContext<'_, '_>, expr: &hir::Expr, expect_args: &[hir::Expr]) {
+ let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&expect_args[0]));
+
+ let mess = if match_type(cx, obj_ty, &paths::OPTION) {
+ Some((OPTION_EXPECT_USED, "an Option", "None"))
+ } else if match_type(cx, obj_ty, &paths::RESULT) {
+ Some((RESULT_EXPECT_USED, "a Result", "Err"))
+ } else {
+ None
+ };
+
+ if let Some((lint, kind, none_value)) = mess {
+ span_lint(
+ cx,
+ lint,
+ expr.span,
+ &format!(
+ "used expect() on {} value. If this value is an {} it will panic",
+ kind, none_value
+ ),
+ );
+ }
+}
+
/// lint use of `ok().expect()` for `Result`s
fn lint_ok_expect(cx: &LateContext<'_, '_>, expr: &hir::Expr, ok_args: &[hir::Expr]) {
if_chain! {
fn lint_map_or_none<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr, map_or_args: &'tcx [hir::Expr]) {
if match_type(cx, cx.tables.expr_ty(&map_or_args[0]), &paths::OPTION) {
// check if the first non-self argument to map_or() is None
- let map_or_arg_is_none = if let hir::ExprKind::Path(ref qpath) = map_or_args[1].node {
+ let map_or_arg_is_none = if let hir::ExprKind::Path(ref qpath) = map_or_args[1].kind {
match_qpath(qpath, &paths::OPTION_NONE)
} else {
false
const LINT_MSG: &str = "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`";
const NO_OP_MSG: &str = "using `Option.and_then(Some)`, which is a no-op";
- // Searches an return expressions in `y` in `_.and_then(|x| Some(y))`, which we don't lint
- struct RetCallFinder {
- found: bool,
- }
-
- impl<'tcx> intravisit::Visitor<'tcx> for RetCallFinder {
- fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
- if self.found {
- return;
- }
- if let hir::ExprKind::Ret(..) = &expr.node {
- self.found = true;
- } else {
- intravisit::walk_expr(self, expr);
- }
- }
-
- fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
- intravisit::NestedVisitorMap::None
- }
- }
-
let ty = cx.tables.expr_ty(&args[0]);
if !match_type(cx, ty, &paths::OPTION) {
return;
}
- match args[1].node {
+ match args[1].kind {
hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => {
let closure_body = cx.tcx.hir().body(body_id);
let closure_expr = remove_blocks(&closure_body.value);
if_chain! {
- if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.node;
- if let hir::ExprKind::Path(ref qpath) = some_expr.node;
+ if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.kind;
+ if let hir::ExprKind::Path(ref qpath) = some_expr.kind;
if match_qpath(qpath, &paths::OPTION_SOME);
if some_args.len() == 1;
then {
let inner_expr = &some_args[0];
- let mut finder = RetCallFinder { found: false };
- finder.visit_expr(inner_expr);
- if finder.found {
+ if contains_return(inner_expr) {
return;
}
cx: &LateContext<'a, 'tcx>,
expr: &'tcx hir::Expr,
flat_map_args: &'tcx [hir::Expr],
+ flat_map_span: Span,
) {
if match_trait_method(cx, expr, &paths::ITERATOR) {
- let arg_node = &flat_map_args[1].node;
+ let arg_node = &flat_map_args[1].kind;
let apply_lint = |message: &str| {
- if let hir::ExprKind::MethodCall(_, span, _) = &expr.node {
- span_lint_and_sugg(
- cx,
- FLAT_MAP_IDENTITY,
- span.with_hi(expr.span.hi()),
- message,
- "try",
- "flatten()".to_string(),
- Applicability::MachineApplicable,
- );
- }
+ span_lint_and_sugg(
+ cx,
+ FLAT_MAP_IDENTITY,
+ flat_map_span.with_hi(expr.span.hi()),
+ message,
+ "try",
+ "flatten()".to_string(),
+ Applicability::MachineApplicable,
+ );
};
if_chain! {
if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node;
let body = cx.tcx.hir().body(*body_id);
- if let hir::PatKind::Binding(_, _, binding_ident, _) = body.arguments[0].pat.node;
- if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.node;
+ if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind;
+ if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind;
if path.segments.len() == 1;
if path.segments[0].ident.as_str() == binding_ident.as_str();
search_method: &str,
search_args: &'tcx [hir::Expr],
is_some_args: &'tcx [hir::Expr],
+ method_span: Span,
) {
// lint if caller of search is an Iterator
if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) {
let search_snippet = snippet(cx, search_args[1].span, "..");
if search_snippet.lines().count() <= 1 {
// suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()`
+ // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()`
let any_search_snippet = if_chain! {
if search_method == "find";
- if let hir::ExprKind::Closure(_, _, body_id, ..) = search_args[1].node;
+ if let hir::ExprKind::Closure(_, _, body_id, ..) = search_args[1].kind;
let closure_body = cx.tcx.hir().body(body_id);
- if let Some(closure_arg) = closure_body.arguments.get(0);
- if let hir::PatKind::Ref(..) = closure_arg.pat.node;
+ if let Some(closure_arg) = closure_body.params.get(0);
then {
- Some(search_snippet.replacen('&', "", 1))
+ if let hir::PatKind::Ref(..) = closure_arg.pat.kind {
+ Some(search_snippet.replacen('&', "", 1))
+ } else if let Some(name) = get_arg_name(&closure_arg.pat) {
+ Some(search_snippet.replace(&format!("*{}", name), &name.as_str()))
+ } else {
+ None
+ }
} else {
None
}
};
// add note if not multi-line
- span_note_and_lint(
+ span_lint_and_sugg(
cx,
SEARCH_IS_SOME,
- expr.span,
+ method_span.with_hi(expr.span.hi()),
&msg,
- expr.span,
- &format!(
- "replace `{0}({1}).is_some()` with `any({2})`",
- search_method,
- search_snippet,
+ "try this",
+ format!(
+ "any({})",
any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
),
+ Applicability::MachineApplicable,
);
} else {
span_lint(cx, SEARCH_IS_SOME, expr.span, &msg);
) -> bool {
if_chain! {
if let Some(args) = method_chain_args(info.chain, chain_methods);
- if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.node;
+ if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind;
if arg_char.len() == 1;
- if let hir::ExprKind::Path(ref qpath) = fun.node;
+ if let hir::ExprKind::Path(ref qpath) = fun.kind;
if let Some(segment) = single_segment_path(qpath);
if segment.ident.name == sym!(Some);
then {
let mut applicability = Applicability::MachineApplicable;
let self_ty = walk_ptrs_ty(cx.tables.expr_ty_adjusted(&args[0][0]));
- if self_ty.sty != ty::Str {
+ if self_ty.kind != ty::Str {
return false;
}
) -> bool {
if_chain! {
if let Some(args) = method_chain_args(info.chain, chain_methods);
- if let hir::ExprKind::Lit(ref lit) = info.other.node;
+ if let hir::ExprKind::Lit(ref lit) = info.other.kind;
if let ast::LitKind::Char(c) = lit.node;
then {
let mut applicability = Applicability::MachineApplicable;
/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
fn lint_single_char_pattern<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, _expr: &'tcx hir::Expr, arg: &'tcx hir::Expr) {
if_chain! {
- if let hir::ExprKind::Lit(lit) = &arg.node;
+ if let hir::ExprKind::Lit(lit) = &arg.kind;
if let ast::LitKind::Str(r, style) = lit.node;
if r.as_str().len() == 1;
then {
// allow the `as_ref` or `as_mut` if it is followed by another method call
if_chain! {
if let Some(parent) = get_parent_expr(cx, expr);
- if let hir::ExprKind::MethodCall(_, ref span, _) = parent.node;
+ if let hir::ExprKind::MethodCall(_, ref span, _) = parent.kind;
if span != &expr.span;
then {
return;
} else {
INTO_ITER_ON_REF
};
- let mutbl = match self_ref_ty.sty {
+ let mutbl = match self_ref_ty.kind {
ty::Ref(_, _, mutbl) => mutbl,
_ => unreachable!(),
};
}
}
+/// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter)
+fn lint_maybe_uninit(cx: &LateContext<'_, '_>, expr: &hir::Expr, outer: &hir::Expr) {
+ if_chain! {
+ if let hir::ExprKind::Call(ref callee, ref args) = expr.kind;
+ if args.is_empty();
+ if let hir::ExprKind::Path(ref path) = callee.kind;
+ if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT);
+ if !is_maybe_uninit_ty_valid(cx, cx.tables.expr_ty_adjusted(outer));
+ then {
+ span_lint(
+ cx,
+ UNINIT_ASSUMED_INIT,
+ outer.span,
+ "this call for this type may be undefined behavior"
+ );
+ }
+ }
+}
+
+fn is_maybe_uninit_ty_valid(cx: &LateContext<'_, '_>, ty: Ty<'_>) -> bool {
+ match ty.kind {
+ ty::Array(ref component, _) => is_maybe_uninit_ty_valid(cx, component),
+ ty::Tuple(ref types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)),
+ ty::Adt(ref adt, _) => {
+ // needs to be a MaybeUninit
+ match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT)
+ },
+ _ => false,
+ }
+}
+
fn lint_suspicious_map(cx: &LateContext<'_, '_>, expr: &hir::Expr) {
span_help_and_lint(
cx,
/// Given a `Result<T, E>` type, return its error type (`E`).
fn get_error_type<'a>(cx: &LateContext<'_, '_>, ty: Ty<'a>) -> Option<Ty<'a>> {
- match ty.sty {
+ match ty.kind {
ty::Adt(_, substs) if match_type(cx, ty, &paths::RESULT) => substs.types().nth(1),
_ => None,
}
/// This checks whether a given type is known to implement Debug.
fn has_debug_impl<'a, 'b>(ty: Ty<'a>, cx: &LateContext<'b, 'a>) -> bool {
- match cx.tcx.lang_items().debug_trait() {
- Some(debug) => implements_trait(cx, ty, debug, &[]),
- None => false,
- }
+ cx.tcx
+ .get_diagnostic_item(sym::debug_trait)
+ .map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
}
enum Convention {
} else if ty.is_box() {
ty.boxed_ty() == parent_ty
} else if ty.is_rc() || ty.is_arc() {
- if let ty::Adt(_, substs) = ty.sty {
+ if let ty::Adt(_, substs) = ty.kind {
substs.types().next().map_or(false, |t| t == parent_ty)
} else {
false
parent_ty: Ty<'a>,
ty: Ty<'a>,
) -> bool {
- if let ty::Ref(_, t, m) = ty.sty {
+ if let ty::Ref(_, t, m) = ty.kind {
return m == mutability && t == parent_ty;
}
}
}
+ #[must_use]
fn description(self) -> &'static str {
match self {
Self::Value => "self by value",
}
impl Convention {
+ #[must_use]
fn check(&self, other: &str) -> bool {
match *self {
Self::Eq(this) => this == other,
impl OutType {
fn matches(self, cx: &LateContext<'_, '_>, ty: &hir::FunctionRetTy) -> bool {
- let is_unit = |ty: &hir::Ty| SpanlessEq::new(cx).eq_ty_kind(&ty.node, &hir::TyKind::Tup(vec![].into()));
+ let is_unit = |ty: &hir::Ty| SpanlessEq::new(cx).eq_ty_kind(&ty.kind, &hir::TyKind::Tup(vec![].into()));
match (self, ty) {
(Self::Unit, &hir::DefaultReturn(_)) => true,
(Self::Unit, &hir::Return(ref ty)) if is_unit(ty) => true,
(Self::Bool, &hir::Return(ref ty)) if is_bool(ty) => true,
(Self::Any, &hir::Return(ref ty)) if !is_unit(ty) => true,
- (Self::Ref, &hir::Return(ref ty)) => matches!(ty.node, hir::TyKind::Rptr(_, _)),
+ (Self::Ref, &hir::Return(ref ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)),
_ => false,
}
}
}
fn is_bool(ty: &hir::Ty) -> bool {
- if let hir::TyKind::Path(ref p) = ty.node {
+ if let hir::TyKind::Path(ref p) = ty.kind {
match_qpath(p, &["bool"])
} else {
false
}
}
+
+// Returns `true` if `expr` contains a return expression
+fn contains_return(expr: &hir::Expr) -> bool {
+ struct RetCallFinder {
+ found: bool,
+ }
+
+ impl<'tcx> intravisit::Visitor<'tcx> for RetCallFinder {
+ fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
+ if self.found {
+ return;
+ }
+ if let hir::ExprKind::Ret(..) = &expr.kind {
+ self.found = true;
+ } else {
+ intravisit::walk_expr(self, expr);
+ }
+ }
+
+ fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
+ intravisit::NestedVisitorMap::None
+ }
+ }
+
+ let mut visitor = RetCallFinder { found: false };
+ visitor.visit_expr(expr);
+ visitor.found
+}