Merge remote-tracking branch 'upstream/master' into rustup

This commit is contained in:
Eduardo Broto 2020-10-31 09:33:40 +01:00
commit 9d73371663
67 changed files with 1210 additions and 274 deletions

View file

@ -88,9 +88,28 @@ impl<'tcx> LateLintPass<'tcx> for Arithmetic {
let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r));
if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() {
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
self.expr_span = Some(expr.span);
} else if l_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
match op.node {
hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind {
hir::ExprKind::Lit(_lit) => (),
hir::ExprKind::Unary(hir::UnOp::UnNeg, expr) => {
if let hir::ExprKind::Lit(lit) = &expr.kind {
if let rustc_ast::ast::LitKind::Int(1, _) = lit.node {
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
self.expr_span = Some(expr.span);
}
}
},
_ => {
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
self.expr_span = Some(expr.span);
},
},
_ => {
span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
self.expr_span = Some(expr.span);
},
}
} else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
self.expr_span = Some(expr.span);
}

View file

@ -92,13 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
|db| {
cx.tcx.infer_ctxt().enter(|infcx| {
for FulfillmentError { obligation, .. } in send_errors {
infcx.maybe_note_obligation_cause_for_async_await(
db,
&obligation,
);
if let Trait(trait_pred, _) =
obligation.predicate.skip_binders()
{
infcx.maybe_note_obligation_cause_for_async_await(db, &obligation);
if let Trait(trait_pred, _) = obligation.predicate.skip_binders() {
db.note(&format!(
"`{}` doesn't implement `{}`",
trait_pred.self_ty(),

View file

@ -2,7 +2,8 @@
use crate::utils::span_lint;
use rustc_ast::ast::{Block, ItemKind, StmtKind};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
@ -53,7 +54,7 @@ declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]);
impl EarlyLintPass for ItemsAfterStatements {
fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) {
if item.span.from_expansion() {
if in_external_macro(cx.sess(), item.span) {
return;
}
@ -67,7 +68,7 @@ impl EarlyLintPass for ItemsAfterStatements {
// lint on all further items
for stmt in stmts {
if let StmtKind::Item(ref it) = *stmt {
if it.span.from_expansion() {
if in_external_macro(cx.sess(), it.span) {
return;
}
if let ItemKind::MacroDef(..) = it.kind {

View file

@ -68,7 +68,44 @@ declare_clippy_lint! {
"traits or impls with a public `len` method but no corresponding `is_empty` method"
}
declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY]);
declare_clippy_lint! {
/// **What it does:** Checks for comparing to an empty slice such as "" or [],`
/// and suggests using `.is_empty()` where applicable.
///
/// **Why is this bad?** Some structures can answer `.is_empty()` much faster
/// than checking for equality. So it is good to get into the habit of using
/// `.is_empty()`, and having it is cheap.
/// Besides, it makes the intent clearer than a manual comparison in some contexts.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```ignore
/// if s == "" {
/// ..
/// }
///
/// if arr == [] {
/// ..
/// }
/// ```
/// Use instead:
/// ```ignore
/// if s.is_empty() {
/// ..
/// }
///
/// if arr.is_empty() {
/// ..
/// }
/// ```
pub COMPARISON_TO_EMPTY,
style,
"checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead"
}
declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]);
impl<'tcx> LateLintPass<'tcx> for LenZero {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@ -221,6 +258,8 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>
}
check_len(cx, span, method_path.ident.name, args, &lit.node, op, compare_to)
} else {
check_empty_expr(cx, span, method, lit, op)
}
}
@ -258,6 +297,42 @@ fn check_len(
}
}
fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) {
if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
COMPARISON_TO_EMPTY,
span,
"comparison to empty slice",
&format!("using `{}is_empty` is clearer and more explicit", op),
format!(
"{}{}.is_empty()",
op,
snippet_with_applicability(cx, lit1.span, "_", &mut applicability)
),
applicability,
);
}
}
fn is_empty_string(expr: &Expr<'_>) -> bool {
if let ExprKind::Lit(ref lit) = expr.kind {
if let LitKind::Str(lit, _) = lit.node {
let lit = lit.as_str();
return lit == "";
}
}
false
}
fn is_empty_array(expr: &Expr<'_>) -> bool {
if let ExprKind::Array(ref arr) = expr.kind {
return arr.is_empty();
}
false
}
/// Checks if this type has an `is_empty` method.
fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
/// Gets an `AssocItem` and return true if it matches `is_empty(self)`.

View file

@ -615,6 +615,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&large_const_arrays::LARGE_CONST_ARRAYS,
&large_enum_variant::LARGE_ENUM_VARIANT,
&large_stack_arrays::LARGE_STACK_ARRAYS,
&len_zero::COMPARISON_TO_EMPTY,
&len_zero::LEN_WITHOUT_IS_EMPTY,
&len_zero::LEN_ZERO,
&let_if_seq::USELESS_LET_IF_SEQ,
@ -702,6 +703,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&methods::ITER_NTH_ZERO,
&methods::ITER_SKIP_NEXT,
&methods::MANUAL_SATURATING_ARITHMETIC,
&methods::MAP_COLLECT_RESULT_UNIT,
&methods::MAP_FLATTEN,
&methods::MAP_UNWRAP_OR,
&methods::NEW_RET_NO_SELF,
@ -1366,6 +1368,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&int_plus_one::INT_PLUS_ONE),
LintId::of(&large_const_arrays::LARGE_CONST_ARRAYS),
LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT),
LintId::of(&len_zero::COMPARISON_TO_EMPTY),
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
LintId::of(&len_zero::LEN_ZERO),
LintId::of(&let_underscore::LET_UNDERSCORE_LOCK),
@ -1427,6 +1430,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&methods::ITER_NTH_ZERO),
LintId::of(&methods::ITER_SKIP_NEXT),
LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
LintId::of(&methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(&methods::NEW_RET_NO_SELF),
LintId::of(&methods::OK_EXPECT),
LintId::of(&methods::OPTION_AS_REF_DEREF),
@ -1592,6 +1596,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&functions::RESULT_UNIT_ERR),
LintId::of(&if_let_some_result::IF_LET_SOME_RESULT),
LintId::of(&inherent_to_string::INHERENT_TO_STRING),
LintId::of(&len_zero::COMPARISON_TO_EMPTY),
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
LintId::of(&len_zero::LEN_ZERO),
LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
@ -1621,6 +1626,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&methods::ITER_NTH_ZERO),
LintId::of(&methods::ITER_SKIP_NEXT),
LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC),
LintId::of(&methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(&methods::NEW_RET_NO_SELF),
LintId::of(&methods::OK_EXPECT),
LintId::of(&methods::OPTION_MAP_OR_NONE),

View file

@ -32,8 +32,7 @@ use crate::utils::{
is_copy, 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,
single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint,
span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth,
SpanlessEq,
span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq,
};
declare_clippy_lint! {
@ -1349,6 +1348,27 @@ declare_clippy_lint! {
"using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
}
declare_clippy_lint! {
/// **What it does:** Checks for usage of `_.map(_).collect::<Result<(),_>()`.
///
/// **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic.
///
/// **Known problems:** None
///
/// **Example:**
///
/// ```rust
/// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
/// ```
/// Use instead:
/// ```rust
/// (0..3).try_for_each(|t| Err(t));
/// ```
pub MAP_COLLECT_RESULT_UNIT,
style,
"using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
}
declare_lint_pass!(Methods => [
UNWRAP_USED,
EXPECT_USED,
@ -1398,6 +1418,7 @@ declare_lint_pass!(Methods => [
FILETYPE_IS_FILE,
OPTION_AS_REF_DEREF,
UNNECESSARY_LAZY_EVALUATIONS,
MAP_COLLECT_RESULT_UNIT,
]);
impl<'tcx> LateLintPass<'tcx> for Methods {
@ -1479,6 +1500,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"),
["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"),
["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"),
["collect", "map"] => lint_map_collect(cx, expr, arg_lists[1], arg_lists[0]),
_ => {},
}
@ -1712,7 +1734,7 @@ fn lint_or_fun_call<'tcx>(
"try this",
format!(
"{}.unwrap_or_default()",
snippet_with_applicability(cx, self_expr.span, "_", &mut applicability)
snippet_with_applicability(cx, self_expr.span, "..", &mut applicability)
),
applicability,
);
@ -2119,7 +2141,7 @@ fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::
return;
};
let snippet = snippet_with_macro_callsite(cx, arg.span, "_");
let snippet = snippet_with_macro_callsite(cx, arg.span, "..");
span_lint_and_sugg(
cx,
@ -2155,9 +2177,9 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E
"try this",
format!(
"{}.push_str({}{})",
snippet_with_applicability(cx, args[0].span, "_", &mut applicability),
snippet_with_applicability(cx, args[0].span, "..", &mut applicability),
ref_str,
snippet_with_applicability(cx, target.span, "_", &mut applicability)
snippet_with_applicability(cx, target.span, "..", &mut applicability)
),
applicability,
);
@ -2404,7 +2426,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args:
let mut applicability = Applicability::MachineApplicable;
let expr_ty = cx.typeck_results().expr_ty(&get_args[0]);
let get_args_str = if get_args.len() > 1 {
snippet_with_applicability(cx, get_args[1].span, "_", &mut applicability)
snippet_with_applicability(cx, get_args[1].span, "..", &mut applicability)
} else {
return; // not linting on a .get().unwrap() chain or variant
};
@ -2464,7 +2486,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args:
format!(
"{}{}[{}]",
borrow_str,
snippet_with_applicability(cx, get_args[0].span, "_", &mut applicability),
snippet_with_applicability(cx, get_args[0].span, "..", &mut applicability),
get_args_str
),
applicability,
@ -2480,7 +2502,7 @@ fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[
cx,
ITER_SKIP_NEXT,
expr.span.trim_start(caller.span).unwrap(),
"called `skip(x).next()` on an iterator",
"called `skip(..).next()` on an iterator",
"use `nth` instead",
hint,
Applicability::MachineApplicable,
@ -2683,11 +2705,11 @@ fn lint_map_unwrap_or_else<'tcx>(
// lint message
let msg = if is_option {
"called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling \
`map_or_else(g, f)` instead"
"called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling \
`map_or_else(<g>, <f>)` instead"
} else {
"called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling \
`.map_or_else(g, f)` instead"
"called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling \
`.map_or_else(<g>, <f>)` instead"
};
// get snippets for args to map() and unwrap_or_else()
let map_snippet = snippet(cx, map_args[1].span, "..");
@ -2697,16 +2719,15 @@ fn lint_map_unwrap_or_else<'tcx>(
let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt();
if same_span && !multiline {
span_lint_and_note(
let var_snippet = snippet(cx, map_args[0].span, "..");
span_lint_and_sugg(
cx,
MAP_UNWRAP_OR,
expr.span,
msg,
None,
&format!(
"replace `map({0}).unwrap_or_else({1})` with `map_or_else({1}, {0})`",
map_snippet, unwrap_snippet,
),
"try this",
format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet),
Applicability::MachineApplicable,
);
return true;
} else if same_span && multiline {
@ -2753,8 +2774,8 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
if is_option {
let self_snippet = snippet(cx, map_or_args[0].span, "..");
let func_snippet = snippet(cx, map_or_args[2].span, "..");
let msg = "called `map_or(None, f)` on an `Option` value. This can be done more directly by calling \
`and_then(f)` instead";
let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
`and_then(..)` instead";
(
OPTION_MAP_OR_NONE,
msg,
@ -2792,18 +2813,20 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
fn lint_filter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) {
// lint if caller of `.filter().next()` is an Iterator
if match_trait_method(cx, expr, &paths::ITERATOR) {
let msg = "called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling \
`.find(p)` instead.";
let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
`.find(..)` instead.";
let filter_snippet = snippet(cx, filter_args[1].span, "..");
if filter_snippet.lines().count() <= 1 {
let iter_snippet = snippet(cx, filter_args[0].span, "..");
// add note if not multi-line
span_lint_and_note(
span_lint_and_sugg(
cx,
FILTER_NEXT,
expr.span,
msg,
None,
&format!("replace `filter({0}).next()` with `find({0})`", filter_snippet),
"try this",
format!("{}.find({})", iter_snippet, filter_snippet),
Applicability::MachineApplicable,
);
} else {
span_lint(cx, FILTER_NEXT, expr.span, msg);
@ -2823,9 +2846,9 @@ fn lint_skip_while_next<'tcx>(
cx,
SKIP_WHILE_NEXT,
expr.span,
"called `skip_while(p).next()` on an `Iterator`",
"called `skip_while(<p>).next()` on an `Iterator`",
None,
"this is more succinctly expressed by calling `.find(!p)` instead",
"this is more succinctly expressed by calling `.find(!<p>)` instead",
);
}
}
@ -2839,7 +2862,7 @@ fn lint_filter_map<'tcx>(
) {
// lint if caller of `.filter().map()` is an Iterator
if match_trait_method(cx, expr, &paths::ITERATOR) {
let msg = "called `filter(p).map(q)` on an `Iterator`";
let msg = "called `filter(..).map(..)` on an `Iterator`";
let hint = "this is more succinctly expressed by calling `.filter_map(..)` instead";
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
}
@ -2848,17 +2871,19 @@ fn lint_filter_map<'tcx>(
/// lint use of `filter_map().next()` for `Iterators`
fn lint_filter_map_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) {
if match_trait_method(cx, expr, &paths::ITERATOR) {
let msg = "called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling \
`.find_map(p)` instead.";
let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
`.find_map(..)` instead.";
let filter_snippet = snippet(cx, filter_args[1].span, "..");
if filter_snippet.lines().count() <= 1 {
span_lint_and_note(
let iter_snippet = snippet(cx, filter_args[0].span, "..");
span_lint_and_sugg(
cx,
FILTER_MAP_NEXT,
expr.span,
msg,
None,
&format!("replace `filter_map({0}).next()` with `find_map({0})`", filter_snippet),
"try this",
format!("{}.find_map({})", iter_snippet, filter_snippet),
Applicability::MachineApplicable,
);
} else {
span_lint(cx, FILTER_MAP_NEXT, expr.span, msg);
@ -2875,7 +2900,7 @@ fn lint_find_map<'tcx>(
) {
// lint if caller of `.filter().map()` is an Iterator
if match_trait_method(cx, &map_args[0], &paths::ITERATOR) {
let msg = "called `find(p).map(q)` on an `Iterator`";
let msg = "called `find(..).map(..)` on an `Iterator`";
let hint = "this is more succinctly expressed by calling `.find_map(..)` instead";
span_lint_and_help(cx, FIND_MAP, expr.span, msg, None, hint);
}
@ -2890,7 +2915,7 @@ fn lint_filter_map_map<'tcx>(
) {
// lint if caller of `.filter().map()` is an Iterator
if match_trait_method(cx, expr, &paths::ITERATOR) {
let msg = "called `filter_map(p).map(q)` on an `Iterator`";
let msg = "called `filter_map(..).map(..)` on an `Iterator`";
let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead";
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
}
@ -2905,7 +2930,7 @@ fn lint_filter_flat_map<'tcx>(
) {
// lint if caller of `.filter().flat_map()` is an Iterator
if match_trait_method(cx, expr, &paths::ITERATOR) {
let msg = "called `filter(p).flat_map(q)` on an `Iterator`";
let msg = "called `filter(..).flat_map(..)` on an `Iterator`";
let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
and filtering by returning `iter::empty()`";
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
@ -2921,7 +2946,7 @@ fn lint_filter_map_flat_map<'tcx>(
) {
// lint if caller of `.filter_map().flat_map()` is an Iterator
if match_trait_method(cx, expr, &paths::ITERATOR) {
let msg = "called `filter_map(p).flat_map(q)` on an `Iterator`";
let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`";
let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
and filtering by returning `iter::empty()`";
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
@ -3092,9 +3117,9 @@ fn lint_chars_cmp(
"like this",
format!("{}{}.{}({})",
if info.eq { "" } else { "!" },
snippet_with_applicability(cx, args[0][0].span, "_", &mut applicability),
snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
suggest,
snippet_with_applicability(cx, arg_char[0].span, "_", &mut applicability)),
snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)),
applicability,
);
@ -3141,7 +3166,7 @@ fn lint_chars_cmp_with_unwrap<'tcx>(
"like this",
format!("{}{}.{}('{}')",
if info.eq { "" } else { "!" },
snippet_with_applicability(cx, args[0][0].span, "_", &mut applicability),
snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
suggest,
c),
applicability,
@ -3216,7 +3241,7 @@ fn lint_single_char_pattern(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &h
fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let mut applicability = Applicability::MachineApplicable;
if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) {
let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
let base_string_snippet = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
let sugg = format!("{}.push({})", base_string_snippet, extension_string);
span_lint_and_sugg(
cx,
@ -3259,7 +3284,7 @@ fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_re
expr.span,
&format!("this call to `{}` does nothing", call_name),
"try this",
snippet_with_applicability(cx, recvr.span, "_", &mut applicability).to_string(),
snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(),
applicability,
);
}
@ -3445,6 +3470,42 @@ fn lint_option_as_ref_deref<'tcx>(
}
}
fn lint_map_collect(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
map_args: &[hir::Expr<'_>],
collect_args: &[hir::Expr<'_>],
) {
if_chain! {
// called on Iterator
if let [map_expr] = collect_args;
if match_trait_method(cx, map_expr, &paths::ITERATOR);
// return of collect `Result<(),_>`
let collect_ret_ty = cx.typeck_results().expr_ty(expr);
if is_type_diagnostic_item(cx, collect_ret_ty, sym!(result_type));
if let ty::Adt(_, substs) = collect_ret_ty.kind();
if let Some(result_t) = substs.types().next();
if result_t.is_unit();
// get parts for snippet
if let [iter, map_fn] = map_args;
then {
span_lint_and_sugg(
cx,
MAP_COLLECT_RESULT_UNIT,
expr.span,
"`.map().collect()` can be replaced with `.try_for_each()`",
"try this",
format!(
"{}.try_for_each({})",
snippet(cx, iter.span, ".."),
snippet(cx, map_fn.span, "..")
),
Applicability::MachineApplicable,
);
}
}
}
/// 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.kind() {

View file

@ -53,15 +53,15 @@ pub(super) fn lint<'tcx>(
// lint message
// comparing the snippet from source to raw text ("None") below is safe
// because we already have checked the type.
let arg = if unwrap_snippet == "None" { "None" } else { "a" };
let arg = if unwrap_snippet == "None" { "None" } else { "<a>" };
let unwrap_snippet_none = unwrap_snippet == "None";
let suggest = if unwrap_snippet_none {
"and_then(f)"
"and_then(<f>)"
} else {
"map_or(a, f)"
"map_or(<a>, <f>)"
};
let msg = &format!(
"called `map(f).unwrap_or({})` on an `Option` value. \
"called `map(<f>).unwrap_or({})` on an `Option` value. \
This can be done more directly by calling `{}` instead",
arg, suggest
);

View file

@ -7,6 +7,7 @@ use rustc_hir::{
StmtKind, TyKind, UnOp,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::hygiene::DesugaringKind;
@ -271,13 +272,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
k: FnKind<'tcx>,
decl: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>,
_: Span,
span: Span,
_: HirId,
) {
if let FnKind::Closure(_) = k {
// Does not apply to closures
return;
}
if in_external_macro(cx.tcx.sess, span) {
return;
}
for arg in iter_input_pats(decl, body) {
if let PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) = arg.pat.kind {
span_lint(
@ -293,13 +297,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if_chain! {
if !in_external_macro(cx.tcx.sess, stmt.span);
if let StmtKind::Local(ref local) = stmt.kind;
if let PatKind::Binding(an, .., name, None) = local.pat.kind;
if let Some(ref init) = local.init;
if !higher::is_from_for_desugar(local);
then {
if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut {
let sugg_init = if init.span.from_expansion() {
// use the macro callsite when the init span (but not the whole local span)
// comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];`
let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() {
Sugg::hir_with_macro_callsite(cx, init, "..")
} else {
Sugg::hir(cx, init, "..")
@ -310,7 +317,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
("", sugg_init.addr())
};
let tyopt = if let Some(ref ty) = local.ty {
format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "_"))
format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, ".."))
} else {
String::new()
};
@ -326,7 +333,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
"try",
format!(
"let {name}{tyopt} = {initref};",
name=snippet(cx, name.span, "_"),
name=snippet(cx, name.span, ".."),
tyopt=tyopt,
initref=initref,
),

View file

@ -10,9 +10,9 @@ use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor};
use rustc_hir::{
BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem,
ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind,
TraitFn, TraitItem, TraitItemKind, TyKind, UnOp,
BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId,
ImplItem, ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt,
StmtKind, SyntheticTyParamKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map;
@ -678,17 +678,30 @@ impl Types {
// details.
return;
}
// When trait objects or opaque types have lifetime or auto-trait bounds,
// we need to add parentheses to avoid a syntax error due to its ambiguity.
// Originally reported as the issue #3128.
let inner_snippet = snippet(cx, inner.span, "..");
let suggestion = match &inner.kind {
TyKind::TraitObject(bounds, lt_bound) if bounds.len() > 1 || !lt_bound.is_elided() => {
format!("&{}({})", ltopt, &inner_snippet)
},
TyKind::Path(qpath)
if get_bounds_if_impl_trait(cx, qpath, inner.hir_id)
.map_or(false, |bounds| bounds.len() > 1) =>
{
format!("&{}({})", ltopt, &inner_snippet)
},
_ => format!("&{}{}", ltopt, &inner_snippet),
};
span_lint_and_sugg(
cx,
BORROWED_BOX,
hir_ty.span,
"you seem to be trying to use `&Box<T>`. Consider using just `&T`",
"try",
format!(
"&{}{}",
ltopt,
&snippet(cx, inner.span, "..")
),
suggestion,
// To make this `MachineApplicable`, at least one needs to check if it isn't a trait item
// because the trait impls of it will break otherwise;
// and there may be other cases that result in invalid code.
@ -721,6 +734,21 @@ fn is_any_trait(t: &hir::Ty<'_>) -> bool {
false
}
fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option<GenericBounds<'tcx>> {
if_chain! {
if let Some(did) = qpath_res(cx, qpath, id).opt_def_id();
if let Some(node) = cx.tcx.hir().get_if_local(did);
if let Node::GenericParam(generic_param) = node;
if let GenericParamKind::Type { synthetic, .. } = generic_param.kind;
if synthetic == Some(SyntheticTyParamKind::ImplTrait);
then {
Some(generic_param.bounds)
} else {
None
}
}
}
declare_clippy_lint! {
/// **What it does:** Checks for binding a unit value.
///