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

This commit is contained in:
flip1995 2021-06-17 10:21:47 +02:00
commit e3eede7b90
No known key found for this signature in database
GPG Key ID: 1CA0DF2AF59D68A5
65 changed files with 1182 additions and 627 deletions

View File

@ -90,11 +90,6 @@ jobs:
- name: Checkout
uses: actions/checkout@v2.3.3
# FIXME: should not be necessary once 1.24.2 is the default version on the windows runner
- name: Update rustup
run: rustup self update
if: runner.os == 'Windows'
- name: Install toolchain
run: rustup show active-toolchain

View File

@ -2295,6 +2295,7 @@ Released 2018-09-13
<!-- begin autogenerated links to lint list -->
[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
[`append_instead_of_extend`]: https://rust-lang.github.io/rust-clippy/master/index.html#append_instead_of_extend
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
@ -2358,6 +2359,7 @@ Released 2018-09-13
[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons

View File

@ -1,15 +1,15 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::match_type;
use clippy_utils::{contains_name, get_pat_name, paths, single_segment_path};
use clippy_utils::visitors::LocalUsedVisitor;
use clippy_utils::{path_to_local_id, paths, peel_ref_operators, remove_blocks, strip_pat_refs};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp};
use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, UintTy};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
use rustc_span::Symbol;
declare_clippy_lint! {
/// **What it does:** Checks for naive byte counts
@ -38,42 +38,43 @@ declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
impl<'tcx> LateLintPass<'tcx> for ByteCount {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
if let ExprKind::MethodCall(count, _, count_args, _) = expr.kind;
if let ExprKind::MethodCall(count, _, [count_recv], _) = expr.kind;
if count.ident.name == sym!(count);
if count_args.len() == 1;
if let ExprKind::MethodCall(filter, _, filter_args, _) = count_args[0].kind;
if let ExprKind::MethodCall(filter, _, [filter_recv, filter_arg], _) = count_recv.kind;
if filter.ident.name == sym!(filter);
if filter_args.len() == 2;
if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind;
if let ExprKind::Closure(_, _, body_id, _, _) = filter_arg.kind;
let body = cx.tcx.hir().body(body_id);
if body.params.len() == 1;
if let Some(argname) = get_pat_name(body.params[0].pat);
if let [param] = body.params;
if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
if let ExprKind::Binary(ref op, l, r) = body.value.kind;
if op.node == BinOpKind::Eq;
if match_type(cx,
cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(),
cx.typeck_results().expr_ty(filter_recv).peel_refs(),
&paths::SLICE_ITER);
let operand_is_arg = |expr| {
let expr = peel_ref_operators(cx, remove_blocks(expr));
path_to_local_id(expr, arg_id)
};
let needle = if operand_is_arg(l) {
r
} else if operand_is_arg(r) {
l
} else {
return;
};
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
if !LocalUsedVisitor::new(cx, arg_id).check_expr(needle);
then {
let needle = match get_path_name(l) {
Some(name) if check_arg(name, argname, r) => r,
_ => match get_path_name(r) {
Some(name) if check_arg(name, argname, l) => l,
_ => { return; }
}
};
if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() {
return;
}
let haystack = if let ExprKind::MethodCall(path, _, args, _) =
filter_args[0].kind {
filter_recv.kind {
let p = path.ident.name;
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
&args[0]
} else {
&filter_args[0]
&filter_recv
}
} else {
&filter_args[0]
&filter_recv
};
let mut applicability = Applicability::MaybeIncorrect;
span_lint_and_sugg(
@ -91,24 +92,3 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount {
};
}
}
fn check_arg(name: Symbol, arg: Symbol, needle: &Expr<'_>) -> bool {
name == arg && !contains_name(name, needle)
}
fn get_path_name(expr: &Expr<'_>) -> Option<Symbol> {
match expr.kind {
ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) | ExprKind::Unary(UnOp::Deref, e) => {
get_path_name(e)
},
ExprKind::Block(b, _) => {
if b.stmts.is_empty() {
b.expr.as_ref().and_then(|p| get_path_name(p))
} else {
None
}
},
ExprKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
_ => None,
}
}

View File

@ -1,11 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::visitors::LocalUsedVisitor;
use clippy_utils::{is_lang_ctor, path_to_local, SpanlessEq};
use clippy_utils::{is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq};
use if_chain::if_chain;
use rustc_hir::LangItem::OptionNone;
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind, UnOp};
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TypeckResults;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{MultiSpan, Span};
@ -73,7 +72,7 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext
if arms_inner.iter().all(|arm| arm.guard.is_none());
// match expression must be a local binding
// match <local> { .. }
if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results()));
if let Some(binding_id) = path_to_local(peel_ref_operators(cx, expr_in));
// one of the branches must be "wild-like"
if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner));
let (wild_inner_arm, non_wild_inner_arm) =
@ -163,16 +162,3 @@ fn pat_contains_or(pat: &Pat<'_>) -> bool {
});
result
}
/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
fn strip_ref_operators<'hir>(mut expr: &'hir Expr<'hir>, typeck_results: &TypeckResults<'_>) -> &'hir Expr<'hir> {
loop {
match expr.kind {
ExprKind::AddrOf(_, _, e) => expr = e,
ExprKind::Unary(UnOp::Deref, e) if typeck_results.expr_ty(e).is_ref() => expr = e,
_ => break,
}
}
expr
}

View File

@ -7,7 +7,6 @@ use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{Ident, Symbol};
@ -122,7 +121,7 @@ impl LateLintPass<'_> for Default {
if let StmtKind::Local(local) = stmt.kind;
if let Some(expr) = local.init;
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
if !in_external_macro(cx.tcx.sess, expr.span);
if !in_macro(expr.span);
// only take bindings to identifiers
if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
// only when assigning `... = Default::default()`

View File

@ -7,9 +7,10 @@ use rustc_hir::{
intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor},
Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::{
hir::map::Map,
lint::in_external_macro,
ty::{self, FloatTy, IntTy, PolyFnSig, Ty},
};
use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -73,6 +74,7 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
/// Check whether a passed literal has potential to cause fallback or not.
fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) {
if_chain! {
if !in_external_macro(self.cx.sess(), lit.span);
if let Some(ty_bound) = self.ty_bounds.last();
if matches!(lit.node,
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed));

View File

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint;
use clippy_utils::fn_def_id;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::Expr;
use rustc_hir::{def::Res, def_id::DefId, Crate, Expr};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Symbol;
@ -13,21 +13,14 @@ declare_clippy_lint! {
/// **Why is this bad?** Some methods are undesirable in certain contexts,
/// and it's beneficial to lint for them as needed.
///
/// **Known problems:** Currently, you must write each function as a
/// fully-qualified path. This lint doesn't support aliases or reexported
/// names; be aware that many types in `std` are actually reexports.
///
/// For example, if you want to disallow `Duration::as_secs`, your clippy.toml
/// configuration would look like
/// `disallowed-methods = ["core::time::Duration::as_secs"]` and not
/// `disallowed-methods = ["std::time::Duration::as_secs"]` as you might expect.
/// **Known problems:** None.
///
/// **Example:**
///
/// An example clippy.toml configuration:
/// ```toml
/// # clippy.toml
/// disallowed-methods = ["alloc::vec::Vec::leak", "std::time::Instant::now"]
/// disallowed-methods = ["std::vec::Vec::leak", "std::time::Instant::now"]
/// ```
///
/// ```rust,ignore
@ -52,6 +45,7 @@ declare_clippy_lint! {
#[derive(Clone, Debug)]
pub struct DisallowedMethod {
disallowed: FxHashSet<Vec<Symbol>>,
def_ids: FxHashSet<(DefId, Vec<Symbol>)>,
}
impl DisallowedMethod {
@ -61,6 +55,7 @@ impl DisallowedMethod {
.iter()
.map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
.collect(),
def_ids: FxHashSet::default(),
}
}
}
@ -68,10 +63,20 @@ impl DisallowedMethod {
impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]);
impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
for path in &self.disallowed {
let segs = path.iter().map(ToString::to_string).collect::<Vec<_>>();
if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::<Vec<_>>())
{
self.def_ids.insert((id, path.clone()));
}
}
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let Some(def_id) = fn_def_id(cx, expr) {
let func_path = cx.get_def_path(def_id);
if self.disallowed.contains(&func_path) {
if self.def_ids.iter().any(|(id, _)| def_id == *id) {
let func_path = cx.get_def_path(def_id);
let func_path_string = func_path
.into_iter()
.map(Symbol::to_ident_string)

View File

@ -0,0 +1,126 @@
use clippy_utils::diagnostics::span_lint;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::{
def::Res, def_id::DefId, Crate, Item, ItemKind, PolyTraitRef, TraitBoundModifier, Ty, TyKind, UseKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{Span, Symbol};
declare_clippy_lint! {
/// **What it does:** Denies the configured types in clippy.toml.
///
/// **Why is this bad?** Some types are undesirable in certain contexts.
///
/// **Known problems:** None.
///
/// N.B. There is no way to ban primitive types.
///
/// **Example:**
///
/// An example clippy.toml configuration:
/// ```toml
/// # clippy.toml
/// disallowed-methods = ["std::collections::BTreeMap"]
/// ```
///
/// ```rust,ignore
/// use std::collections::BTreeMap;
/// // or its use
/// let x = std::collections::BTreeMap::new();
/// ```
/// Use instead:
/// ```rust,ignore
/// // A similar type that is allowed by the config
/// use std::collections::HashMap;
/// ```
pub DISALLOWED_TYPE,
nursery,
"use of a disallowed type"
}
#[derive(Clone, Debug)]
pub struct DisallowedType {
disallowed: FxHashSet<Vec<Symbol>>,
def_ids: FxHashSet<(DefId, Vec<Symbol>)>,
}
impl DisallowedType {
pub fn new(disallowed: &FxHashSet<String>) -> Self {
Self {
disallowed: disallowed
.iter()
.map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>())
.collect(),
def_ids: FxHashSet::default(),
}
}
}
impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]);
impl<'tcx> LateLintPass<'tcx> for DisallowedType {
fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
for path in &self.disallowed {
let segs = path.iter().map(ToString::to_string).collect::<Vec<_>>();
if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::<Vec<_>>())
{
self.def_ids.insert((id, path.clone()));
}
}
}
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if_chain! {
if let ItemKind::Use(path, UseKind::Single) = &item.kind;
if let Res::Def(_, did) = path.res;
if let Some((_, name)) = self.def_ids.iter().find(|(id, _)| *id == did);
then {
emit(cx, name, item.span,);
}
}
}
fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
if_chain! {
if let TyKind::Path(path) = &ty.kind;
if let Some(did) = cx.qpath_res(path, ty.hir_id).opt_def_id();
if let Some((_, name)) = self.def_ids.iter().find(|(id, _)| *id == did);
then {
emit(cx, name, path.span());
}
}
}
fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) {
if_chain! {
if let Res::Def(_, did) = poly.trait_ref.path.res;
if let Some((_, name)) = self.def_ids.iter().find(|(id, _)| *id == did);
then {
emit(cx, name, poly.trait_ref.path.span);
}
}
}
// TODO: if non primitive const generics are a thing
// fn check_generic_arg(&mut self, cx: &LateContext<'tcx>, arg: &'tcx GenericArg<'tcx>) {
// match arg {
// GenericArg::Const(c) => {},
// }
// }
// fn check_generic_param(&mut self, cx: &LateContext<'tcx>, param: &'tcx GenericParam<'tcx>) {
// match param.kind {
// GenericParamKind::Const { .. } => {},
// }
// }
}
fn emit(cx: &LateContext<'_>, name: &[Symbol], span: Span) {
let name = name.iter().map(|s| s.to_ident_string()).collect::<Vec<_>>().join("::");
span_lint(
cx,
DISALLOWED_TYPE,
span,
&format!("`{}` is not allowed according to config", name),
);
}

View File

@ -187,6 +187,7 @@ mod default_numeric_fallback;
mod dereference;
mod derive;
mod disallowed_method;
mod disallowed_type;
mod doc;
mod double_comparison;
mod double_parens;
@ -254,7 +255,6 @@ mod manual_strip;
mod manual_unwrap_or;
mod map_clone;
mod map_err_ignore;
mod map_identity;
mod map_unit_fn;
mod match_on_vec_items;
mod matches;
@ -583,6 +583,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
derive::EXPL_IMPL_CLONE_ON_COPY,
derive::UNSAFE_DERIVE_DESERIALIZE,
disallowed_method::DISALLOWED_METHOD,
disallowed_type::DISALLOWED_TYPE,
doc::DOC_MARKDOWN,
doc::MISSING_ERRORS_DOC,
doc::MISSING_PANICS_DOC,
@ -705,7 +706,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
manual_unwrap_or::MANUAL_UNWRAP_OR,
map_clone::MAP_CLONE,
map_err_ignore::MAP_ERR_IGNORE,
map_identity::MAP_IDENTITY,
map_unit_fn::OPTION_MAP_UNIT_FN,
map_unit_fn::RESULT_MAP_UNIT_FN,
match_on_vec_items::MATCH_ON_VEC_ITEMS,
@ -730,6 +730,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
mem_replace::MEM_REPLACE_WITH_DEFAULT,
mem_replace::MEM_REPLACE_WITH_UNINIT,
methods::APPEND_INSTEAD_OF_EXTEND,
methods::BIND_INSTEAD_OF_MAP,
methods::BYTES_NTH,
methods::CHARS_LAST_CMP,
@ -765,6 +766,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
methods::MANUAL_STR_REPEAT,
methods::MAP_COLLECT_RESULT_UNIT,
methods::MAP_FLATTEN,
methods::MAP_IDENTITY,
methods::MAP_UNWRAP_OR,
methods::NEW_RET_NO_SELF,
methods::OK_EXPECT,
@ -1260,7 +1262,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(manual_strip::MANUAL_STRIP),
LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
LintId::of(map_clone::MAP_CLONE),
LintId::of(map_identity::MAP_IDENTITY),
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
@ -1276,6 +1277,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
LintId::of(methods::APPEND_INSTEAD_OF_EXTEND),
LintId::of(methods::BIND_INSTEAD_OF_MAP),
LintId::of(methods::BYTES_NTH),
LintId::of(methods::CHARS_LAST_CMP),
@ -1301,6 +1303,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
LintId::of(methods::MANUAL_STR_REPEAT),
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(methods::MAP_IDENTITY),
LintId::of(methods::NEW_RET_NO_SELF),
LintId::of(methods::OK_EXPECT),
LintId::of(methods::OPTION_AS_REF_DEREF),
@ -1586,7 +1589,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(loops::WHILE_LET_LOOP),
LintId::of(manual_strip::MANUAL_STRIP),
LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
LintId::of(map_identity::MAP_IDENTITY),
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
LintId::of(matches::MATCH_AS_REF),
@ -1601,6 +1603,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(methods::ITER_COUNT),
LintId::of(methods::MANUAL_FILTER_MAP),
LintId::of(methods::MANUAL_FIND_MAP),
LintId::of(methods::MAP_IDENTITY),
LintId::of(methods::OPTION_AS_REF_DEREF),
LintId::of(methods::OPTION_FILTER_MAP),
LintId::of(methods::SEARCH_IS_SOME),
@ -1735,6 +1738,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
LintId::of(loops::MANUAL_MEMCPY),
LintId::of(loops::NEEDLESS_COLLECT),
LintId::of(methods::APPEND_INSTEAD_OF_EXTEND),
LintId::of(methods::EXPECT_FUN_CALL),
LintId::of(methods::ITER_NTH),
LintId::of(methods::MANUAL_STR_REPEAT),
@ -1761,6 +1765,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
LintId::of(disallowed_method::DISALLOWED_METHOD),
LintId::of(disallowed_type::DISALLOWED_TYPE),
LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),
@ -2039,7 +2044,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
single_char_binding_names_threshold,
});
store.register_late_pass(|| box macro_use::MacroUseImports::default());
store.register_late_pass(|| box map_identity::MapIdentity);
store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive);
store.register_late_pass(|| box repeat_once::RepeatOnce);
@ -2066,6 +2070,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv));
store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison);
store.register_late_pass(|| box unused_async::UnusedAsync);
let disallowed_types = conf.disallowed_types.iter().cloned().collect::<FxHashSet<_>>();
store.register_late_pass(move || box disallowed_type::DisallowedType::new(&disallowed_types));
}

View File

@ -204,11 +204,8 @@ struct MinifyingSugg<'a>(Sugg<'a>);
impl<'a> MinifyingSugg<'a> {
fn as_str(&self) -> &str {
// HACK: Don't sync to Clippy! Required because something with the `or_patterns` feature
// changed and this would now require parentheses.
match &self.0 {
Sugg::NonParen(s) | Sugg::MaybeParen(s) | Sugg::BinOp(_, s) => s.as_ref(),
}
let (Sugg::NonParen(s) | Sugg::MaybeParen(s) | Sugg::BinOp(_, s)) = &self.0;
s.as_ref()
}
fn into_sugg(self) -> Sugg<'a> {

View File

@ -7,10 +7,10 @@ use clippy_utils::{is_trait_method, path_to_local_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{Block, Expr, ExprKind, GenericArg, GenericArgs, HirId, Local, Pat, PatKind, QPath, StmtKind, Ty};
use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, StmtKind};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
use rustc_span::symbol::{sym, Ident};
use rustc_span::sym;
use rustc_span::{MultiSpan, Span};
const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
@ -24,10 +24,8 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
if let ExprKind::MethodCall(method, _, args, _) = expr.kind;
if let ExprKind::MethodCall(chain_method, method0_span, _, _) = args[0].kind;
if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
if let Some(generic_args) = chain_method.args;
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
if let Some(ty) = cx.typeck_results().node_type_opt(ty.hir_id);
then {
let ty = cx.typeck_results().expr_ty(&args[0]);
let mut applicability = Applicability::MachineApplicable;
let is_empty_sugg = "next().is_none()".to_string();
let method_name = &*method.ident.name.as_str();
@ -72,40 +70,25 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
}
fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
fn get_hir_id<'tcx>(ty: Option<&Ty<'tcx>>, method_args: Option<&GenericArgs<'tcx>>) -> Option<HirId> {
if let Some(ty) = ty {
return Some(ty.hir_id);
}
if let Some(generic_args) = method_args {
if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0) {
return Some(ty.hir_id);
}
}
None
}
if let ExprKind::Block(block, _) = expr.kind {
for stmt in block.stmts {
if_chain! {
if let StmtKind::Local(
Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. },
init: Some(init_expr), ty, .. }
) = stmt.kind;
if let StmtKind::Local(local) = stmt.kind;
if let PatKind::Binding(_, id, ..) = local.pat.kind;
if let Some(init_expr) = local.init;
if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind;
if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator);
if let Some(hir_id) = get_hir_id(*ty, method_name.args);
if let Some(ty) = cx.typeck_results().node_type_opt(hir_id);
let ty = cx.typeck_results().expr_ty(init_expr);
if is_type_diagnostic_item(cx, ty, sym::vec_type) ||
is_type_diagnostic_item(cx, ty, sym::vecdeque_type) ||
is_type_diagnostic_item(cx, ty, sym::BinaryHeap) ||
is_type_diagnostic_item(cx, ty, sym::LinkedList);
if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident);
if let Some(iter_calls) = detect_iter_and_into_iters(block, id);
if let [iter_call] = &*iter_calls;
then {
let mut used_count_visitor = UsedCountVisitor {
cx,
id: *pat_id,
id,
count: 0,
};
walk_block(&mut used_count_visitor, block);
@ -187,48 +170,40 @@ enum IterFunctionKind {
struct IterFunctionVisitor {
uses: Vec<IterFunction>,
seen_other: bool,
target: Ident,
target: HirId,
}
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
// Check function calls on our collection
if_chain! {
if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind;
if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. }) = args.get(0);
if let &[name] = &path.segments;
if name.ident == self.target;
then {
let len = sym!(len);
let is_empty = sym!(is_empty);
let contains = sym!(contains);
match method_name.ident.name {
sym::into_iter => self.uses.push(
IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }
),
name if name == len => self.uses.push(
IterFunction { func: IterFunctionKind::Len, span: expr.span }
),
name if name == is_empty => self.uses.push(
IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }
),
name if name == contains => self.uses.push(
IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }
),
if let ExprKind::MethodCall(method_name, _, [recv, args @ ..], _) = &expr.kind {
if path_to_local_id(recv, self.target) {
match &*method_name.ident.name.as_str() {
"into_iter" => self.uses.push(IterFunction {
func: IterFunctionKind::IntoIter,
span: expr.span,
}),
"len" => self.uses.push(IterFunction {
func: IterFunctionKind::Len,
span: expr.span,
}),
"is_empty" => self.uses.push(IterFunction {
func: IterFunctionKind::IsEmpty,
span: expr.span,
}),
"contains" => self.uses.push(IterFunction {
func: IterFunctionKind::Contains(args[0].span),
span: expr.span,
}),
_ => self.seen_other = true,
}
return
return;
}
}
// Check if the collection is used for anything else
if_chain! {
if let Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. } = expr;
if let &[name] = &path.segments;
if name.ident == self.target;
then {
self.seen_other = true;
} else {
walk_expr(self, expr);
}
if path_to_local_id(expr, self.target) {
self.seen_other = true;
} else {
walk_expr(self, expr);
}
}
@ -262,10 +237,10 @@ impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> {
/// Detect the occurrences of calls to `iter` or `into_iter` for the
/// given identifier
fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option<Vec<IterFunction>> {
fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, id: HirId) -> Option<Vec<IterFunction>> {
let mut visitor = IterFunctionVisitor {
uses: Vec::new(),
target: identifier,
target: id,
seen_other: false,
};
visitor.visit_block(block);

View File

@ -1,7 +1,9 @@
use super::WHILE_LET_ON_ITERATOR;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used};
use clippy_utils::{
get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
@ -315,9 +317,10 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
}
}
if let Some(e) = get_enclosing_loop(cx.tcx, loop_expr) {
// The iterator expression will be used on the next iteration unless it is declared within the outer
// loop.
if let Some(e) = get_enclosing_loop_or_closure(cx.tcx, loop_expr) {
// The iterator expression will be used on the next iteration (for loops), or on the next call (for
// closures) unless it is declared within the enclosing expression. TODO: Check for closures
// used where an `FnOnce` type is expected.
let local_id = match iter_expr.path {
Res::Local(id) => id,
_ => return true,

View File

@ -3,19 +3,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
use clippy_utils::{
can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs,
can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, path_to_local_id,
peel_hir_expr_refs,
};
use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind};
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{
symbol::{sym, Ident},
SyntaxContext,
};
use rustc_span::{sym, SyntaxContext};
declare_clippy_lint! {
/// **What it does:** Checks for usages of `match` which could be implemented using `map`
@ -141,13 +139,13 @@ impl LateLintPass<'_> for ManualMap {
scrutinee_str.into()
};
let body_str = if let PatKind::Binding(annotation, _, some_binding, None) = some_pat.kind {
match can_pass_as_func(cx, some_binding, some_expr) {
let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
match can_pass_as_func(cx, id, some_expr) {
Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
},
_ => {
if match_var(some_expr, some_binding.name)
if path_to_local_id(some_expr, id)
&& !is_allowed(cx, MATCH_AS_REF, expr.hir_id)
&& binding_ref.is_some()
{
@ -199,10 +197,10 @@ impl LateLintPass<'_> for ManualMap {
// Checks whether the expression could be passed as a function, or whether a closure is needed.
// Returns the function to be passed to `map` if it exists.
fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
match expr.kind {
ExprKind::Call(func, [arg])
if match_var(arg, binding.name) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
{
Some(func)
},

View File

@ -1,126 +0,0 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_adjusted, is_qpath_def_path, is_trait_method, match_var, paths, remove_blocks};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! {
/// **What it does:** Checks for instances of `map(f)` where `f` is the identity function.
///
/// **Why is this bad?** It can be written more concisely without the call to `map`.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// let x = [1, 2, 3];
/// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
/// ```
/// Use instead:
/// ```rust
/// let x = [1, 2, 3];
/// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
/// ```
pub MAP_IDENTITY,
complexity,
"using iterator.map(|x| x)"
}
declare_lint_pass!(MapIdentity => [MAP_IDENTITY]);
impl<'tcx> LateLintPass<'tcx> for MapIdentity {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if expr.span.from_expansion() {
return;
}
if_chain! {
if let Some([caller, func]) = get_map_argument(cx, expr);
if is_expr_identity_function(cx, func);
then {
span_lint_and_sugg(
cx,
MAP_IDENTITY,
expr.span.trim_start(caller.span).unwrap(),
"unnecessary map of the identity function",
"remove the call to `map`",
String::new(),
Applicability::MachineApplicable
)
}
}
}
}
/// Returns the arguments passed into map() if the expression is a method call to
/// map(). Otherwise, returns None.
fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> {
if_chain! {
if let ExprKind::MethodCall(method, _, args, _) = expr.kind;
if args.len() == 2 && method.ident.name == sym::map;
let caller_ty = cx.typeck_results().expr_ty(&args[0]);
if is_trait_method(cx, expr, sym::Iterator)
|| is_type_diagnostic_item(cx, caller_ty, sym::result_type)
|| is_type_diagnostic_item(cx, caller_ty, sym::option_type);
then {
Some(args)
} else {
None
}
}
}
/// Checks if an expression represents the identity function
/// Only examines closures and `std::convert::identity`
fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match expr.kind {
ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
_ => false,
}
}
/// Checks if a function's body represents the identity function
/// Looks for bodies of the form `|x| x`, `|x| return x`, `|x| { return x }` or `|x| {
/// return x; }`
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
let params = func.params;
let body = remove_blocks(&func.value);
// if there's less/more than one parameter, then it is not the identity function
if params.len() != 1 {
return false;
}
match body.kind {
ExprKind::Path(QPath::Resolved(None, _)) => match_expr_param(cx, body, params[0].pat),
ExprKind::Ret(Some(ret_val)) => match_expr_param(cx, ret_val, params[0].pat),
ExprKind::Block(block, _) => {
if_chain! {
if block.stmts.len() == 1;
if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block.stmts[0].kind;
if let ExprKind::Ret(Some(ret_val)) = expr.kind;
then {
match_expr_param(cx, ret_val, params[0].pat)
} else {
false
}
}
},
_ => false,
}
}
/// Returns true iff an expression returns the same thing as a parameter's pattern
fn match_expr_param(cx: &LateContext<'_>, expr: &Expr<'_>, pat: &Pat<'_>) -> bool {
if let PatKind::Binding(_, _, ident, _) = pat.kind {
match_var(expr, ident.name) && !(cx.typeck_results().hir_owner == expr.hir_id.owner && is_adjusted(cx, expr))
} else {
false
}
}

View File

@ -0,0 +1,41 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
use super::APPEND_INSTEAD_OF_EXTEND;
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) {
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
if_chain! {
if is_type_diagnostic_item(cx, ty, sym::vec_type);
//check source object
if let ExprKind::MethodCall(src_method, _, [drain_vec, drain_arg], _) = &arg.kind;
if src_method.ident.as_str() == "drain";
if let src_ty = cx.typeck_results().expr_ty(drain_vec).peel_refs();
if is_type_diagnostic_item(cx, src_ty, sym::vec_type);
//check drain range
if let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs();
if is_type_lang_item(cx, src_ty_range, LangItem::RangeFull);
then {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
APPEND_INSTEAD_OF_EXTEND,
expr.span,
"use of `extend` instead of `append` for adding the full range of a second vector",
"try this",
format!(
"{}.append(&mut {})",
snippet_with_applicability(cx, recv.span, "..", &mut applicability),
snippet_with_applicability(cx, drain_vec.span, "..", &mut applicability)
),
applicability,
);
}
}
}

View File

@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::{is_expr_path_def_path, is_trait_method, path_to_local_id, paths};
use if_chain::if_chain;
use clippy_utils::{is_expr_identity_function, is_trait_method};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
@ -9,32 +8,15 @@ use rustc_span::{source_map::Span, sym};
use super::FILTER_MAP_IDENTITY;
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) {
if is_trait_method(cx, expr, sym::Iterator) {
let apply_lint = |message: &str| {
span_lint_and_sugg(
cx,
FILTER_MAP_IDENTITY,
filter_map_span.with_hi(expr.span.hi()),
message,
"try",
"flatten()".to_string(),
Applicability::MachineApplicable,
);
};
if_chain! {
if let hir::ExprKind::Closure(_, _, body_id, _, _) = filter_map_arg.kind;
let body = cx.tcx.hir().body(body_id);
if let hir::PatKind::Binding(_, binding_id, ..) = body.params[0].pat.kind;
if path_to_local_id(&body.value, binding_id);
then {
apply_lint("called `filter_map(|x| x)` on an `Iterator`");
}
}
if is_expr_path_def_path(cx, filter_map_arg, &paths::CONVERT_IDENTITY) {
apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`");
}
if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, filter_map_arg) {
span_lint_and_sugg(
cx,
FILTER_MAP_IDENTITY,
filter_map_span.with_hi(expr.span.hi()),
"use of `filter_map` with an identity function",
"try",
"flatten()".to_string(),
Applicability::MachineApplicable,
);
}
}

View File

@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::{is_expr_path_def_path, is_trait_method, paths};
use if_chain::if_chain;
use clippy_utils::{is_expr_identity_function, is_trait_method};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
@ -15,36 +14,15 @@ pub(super) fn check<'tcx>(
flat_map_arg: &'tcx hir::Expr<'_>,
flat_map_span: Span,
) {
if is_trait_method(cx, expr, sym::Iterator) {
let apply_lint = |message: &str| {
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, _, _) = flat_map_arg.kind;
let body = cx.tcx.hir().body(body_id);
if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind;
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind;
if path.segments.len() == 1;
if path.segments[0].ident.name == binding_ident.name;
then {
apply_lint("called `flat_map(|x| x)` on an `Iterator`");
}
}
if is_expr_path_def_path(cx, flat_map_arg, &paths::CONVERT_IDENTITY) {
apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
}
if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, flat_map_arg) {
span_lint_and_sugg(
cx,
FLAT_MAP_IDENTITY,
flat_map_span.with_hi(expr.span.hi()),
"use of `flat_map` with an identity function",
"try",
"flatten()".to_string(),
Applicability::MachineApplicable,
);
}
}

View File

@ -0,0 +1,38 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_expr_identity_function, is_trait_method};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::{source_map::Span, sym};
use super::MAP_IDENTITY;
pub(super) fn check(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
caller: &hir::Expr<'_>,
map_arg: &hir::Expr<'_>,
_map_span: Span,
) {
let caller_ty = cx.typeck_results().expr_ty(caller);
if_chain! {
if is_trait_method(cx, expr, sym::Iterator)
|| is_type_diagnostic_item(cx, caller_ty, sym::result_type)
|| is_type_diagnostic_item(cx, caller_ty, sym::option_type);
if is_expr_identity_function(cx, map_arg);
if let Some(sugg_span) = expr.span.trim_start(caller.span);
then {
span_lint_and_sugg(
cx,
MAP_IDENTITY,
sugg_span,
"unnecessary map of the identity function",
"remove the call to `map`",
String::new(),
Applicability::MachineApplicable,
)
}
}
}

View File

@ -1,3 +1,4 @@
mod append_instead_of_extend;
mod bind_instead_of_map;
mod bytes_nth;
mod chars_cmp;
@ -35,6 +36,7 @@ mod manual_saturating_arithmetic;
mod manual_str_repeat;
mod map_collect_result_unit;
mod map_flatten;
mod map_identity;
mod map_unwrap_or;
mod ok_expect;
mod option_as_ref_deref;
@ -1031,6 +1033,30 @@ declare_clippy_lint! {
"using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
}
declare_clippy_lint! {
/// **What it does:** Checks for occurrences where one vector gets extended instead of append
///
/// **Why is this bad?** Using `append` instead of `extend` is more concise and faster
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// let mut a = vec![1, 2, 3];
/// let mut b = vec![4, 5, 6];
///
/// // Bad
/// a.extend(b.drain(..));
///
/// // Good
/// a.append(&mut b);
/// ```
pub APPEND_INSTEAD_OF_EXTEND,
perf,
"using vec.append(&mut vec) to move the full range of a vecor to another"
}
declare_clippy_lint! {
/// **What it does:** Checks for the use of `.extend(s.chars())` where s is a
/// `&str` or `String`.
@ -1561,6 +1587,29 @@ declare_clippy_lint! {
"call to `filter_map` where `flatten` is sufficient"
}
declare_clippy_lint! {
/// **What it does:** Checks for instances of `map(f)` where `f` is the identity function.
///
/// **Why is this bad?** It can be written more concisely without the call to `map`.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// let x = [1, 2, 3];
/// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
/// ```
/// Use instead:
/// ```rust
/// let x = [1, 2, 3];
/// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
/// ```
pub MAP_IDENTITY,
complexity,
"using iterator.map(|x| x)"
}
declare_clippy_lint! {
/// **What it does:** Checks for the use of `.bytes().nth()`.
///
@ -1728,6 +1777,7 @@ impl_lint_pass!(Methods => [
FILTER_NEXT,
SKIP_WHILE_NEXT,
FILTER_MAP_IDENTITY,
MAP_IDENTITY,
MANUAL_FILTER_MAP,
MANUAL_FIND_MAP,
OPTION_FILTER_MAP,
@ -1760,7 +1810,8 @@ impl_lint_pass!(Methods => [
INSPECT_FOR_EACH,
IMPLICIT_CLONE,
SUSPICIOUS_SPLITN,
MANUAL_STR_REPEAT
MANUAL_STR_REPEAT,
APPEND_INSTEAD_OF_EXTEND
]);
/// Extracts a method call name, args, and `Span` of the method name.
@ -2022,7 +2073,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
_ => expect_used::check(cx, expr, recv),
},
("extend", [arg]) => string_extend_chars::check(cx, expr, recv, arg),
("extend", [arg]) => {
string_extend_chars::check(cx, expr, recv, arg);
append_instead_of_extend::check(cx, expr, recv, arg);
},
("filter_map", [arg]) => {
unnecessary_filter_map::check(cx, expr, arg);
filter_map_identity::check(cx, expr, arg, span);
@ -2058,6 +2112,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
_ => {},
}
}
map_identity::check(cx, expr, recv, m_arg, span);
},
("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
("next", []) => {

View File

@ -7,7 +7,8 @@
use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint;
use rustc_ast::ast;
use if_chain::if_chain;
use rustc_ast::ast::{self, MetaItem, MetaItemKind};
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty;
@ -55,6 +56,20 @@ impl MissingDoc {
*self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
}
fn has_include(meta: Option<MetaItem>) -> bool {
if_chain! {
if let Some(meta) = meta;
if let MetaItemKind::List(list) = meta.kind;
if let Some(meta) = list.get(0);
if let Some(name) = meta.ident();
then {
name.name == sym::include
} else {
false
}
}
}
fn check_missing_docs_attrs(
&self,
cx: &LateContext<'_>,
@ -80,7 +95,7 @@ impl MissingDoc {
let has_doc = attrs
.iter()
.any(|a| a.doc_str().is_some());
.any(|a| a.doc_str().is_some() || Self::has_include(a.meta()));
if !has_doc {
span_lint(
cx,

View File

@ -87,10 +87,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
self.found = true;
return;
},
ExprKind::If(..) => {
self.found = true;
return;
},
ExprKind::Path(_) => {
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
if adj

View File

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use rustc_ast::ast::{
Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat,
PatKind,
Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat, PatKind,
};
use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
use rustc_lint::{EarlyContext, EarlyLintPass};

View File

@ -132,7 +132,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
}
}
// `{ cloned = &arg; clone(move cloned); }` or `{ cloned = &arg; to_path_buf(cloned); }`
// `{ arg = &cloned; clone(move arg); }` or `{ arg = &cloned; to_path_buf(arg); }`
let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb));
let loc = mir::Location {
@ -628,7 +628,7 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => {
visit_op(lhs);
visit_op(rhs);
}
},
_ => (),
}
}

View File

@ -1,3 +1,4 @@
use crate::rustc_lint::LintContext;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_macro_callsite;
use clippy_utils::{in_macro, sugg};
@ -45,6 +46,7 @@ impl LateLintPass<'_> for SemicolonIfNothingReturned {
if t_expr.is_unit();
if let snippet = snippet_with_macro_callsite(cx, expr.span, "}");
if !snippet.ends_with('}');
if cx.sess().source_map().is_multiline(block.span);
then {
// filter out the desugared `for` loop
if let ExprKind::DropTemps(..) = &expr.kind {

View File

@ -1,6 +1,6 @@
#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path, eq_maybe_qself};
use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{meets_msrv, msrvs, over};
use rustc_ast::mut_visit::*;
@ -277,7 +277,8 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize)
ps1, start, alternatives,
|k, ps1, idx| matches!(
k,
TupleStruct(qself2, path2, ps2) if eq_maybe_qself(qself1, qself2) && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx)
TupleStruct(qself2, path2, ps2)
if eq_maybe_qself(qself1, qself2) && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx)
),
|k| always_pat!(k, TupleStruct(_, _, ps) => ps),
),

View File

@ -1,23 +1,21 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::same_type_and_consts;
use clippy_utils::{in_macro, meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{
self as hir,
def::{self, DefKind},
def::{CtorOf, DefKind, Res},
def_id::LocalDefId,
intravisit::{walk_ty, NestedVisitorMap, Visitor},
Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, PathSegment,
QPath, TyKind,
Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, QPath, TyKind,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map;
use rustc_middle::ty::{AssocKind, Ty};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{BytePos, Span};
use rustc_span::Span;
use rustc_typeck::hir_ty_to_ty;
declare_clippy_lint! {
@ -234,111 +232,58 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
}
fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
if in_macro(hir_ty.span)
|| in_impl(cx, hir_ty)
|| !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS)
{
return;
}
let lint_dependend_on_expr_kind = if let Some(StackItem::Check {
hir_id,
types_to_lint,
types_to_skip,
..
}) = self.stack.last()
{
if types_to_skip.contains(&hir_ty.hir_id) {
false
} else if types_to_lint.contains(&hir_ty.hir_id) {
true
} else {
let self_ty = ty_from_hir_id(cx, *hir_id);
should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty)
}
} else {
false
};
if lint_dependend_on_expr_kind {
// FIXME: this span manipulation should not be necessary
// @flip1995 found an ast lowering issue in
// https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#l142-l162
if_chain! {
if !in_macro(hir_ty.span) && !in_impl(cx, hir_ty);
if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
if let Some(StackItem::Check {
hir_id,
types_to_lint,
types_to_skip,
..
}) = self.stack.last();
if !types_to_skip.contains(&hir_ty.hir_id);
if types_to_lint.contains(&hir_ty.hir_id)
|| {
let self_ty = ty_from_hir_id(cx, *hir_id);
should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty)
};
let hir = cx.tcx.hir();
let id = hir.get_parent_node(hir_ty.hir_id);
if !hir.opt_span(id).map_or(false, in_macro) {
match hir.find(id) {
Some(Node::Expr(Expr {
kind: ExprKind::Path(QPath::TypeRelative(_, segment)),
..
})) => span_lint_until_last_segment(cx, hir_ty.span, segment),
_ => span_lint(cx, hir_ty.span),
}
if !hir.opt_span(id).map_or(false, in_macro);
then {
span_lint(cx, hir_ty.span);
}
}
}
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
fn expr_ty_matches(cx: &LateContext<'_>, expr: &Expr<'_>, self_ty: Ty<'_>) -> bool {
let def_id = expr.hir_id.owner;
if cx.tcx.has_typeck_results(def_id) {
cx.tcx.typeck(def_id).expr_ty_opt(expr) == Some(self_ty)
} else {
false
}
if_chain! {
if !in_macro(expr.span);
if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
if let Some(StackItem::Check { hir_id, .. }) = self.stack.last();
if cx.typeck_results().expr_ty(expr) == ty_from_hir_id(cx, *hir_id);
then {} else { return; }
}
if in_macro(expr.span) || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS) {
return;
}
if let Some(StackItem::Check { hir_id, .. }) = self.stack.last() {
let self_ty = ty_from_hir_id(cx, *hir_id);
match &expr.kind {
ExprKind::Struct(QPath::Resolved(_, path), ..) => {
if expr_ty_matches(cx, expr, self_ty) {
match path.res {
def::Res::SelfTy(..) => (),
def::Res::Def(DefKind::Variant, _) => span_lint_on_path_until_last_segment(cx, path),
_ => {
span_lint(cx, path.span);
},
match expr.kind {
ExprKind::Struct(QPath::Resolved(_, path), ..) => match path.res {
Res::SelfTy(..) => (),
Res::Def(DefKind::Variant, _) => lint_path_to_variant(cx, path),
_ => span_lint(cx, path.span),
},
// tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
ExprKind::Call(fun, _) => {
if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind {
if let Res::Def(DefKind::Ctor(ctor_of, _), ..) = path.res {
match ctor_of {
CtorOf::Variant => lint_path_to_variant(cx, path),
CtorOf::Struct => span_lint(cx, path.span),
}
}
},
// tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
ExprKind::Call(fun, _) => {
if let Expr {
kind: ExprKind::Path(ref qpath),
..
} = fun
{
if expr_ty_matches(cx, expr, self_ty) {
let res = cx.qpath_res(qpath, fun.hir_id);
if let def::Res::Def(DefKind::Ctor(ctor_of, _), ..) = res {
match ctor_of {
def::CtorOf::Variant => {
span_lint_on_qpath_resolved(cx, qpath, true);
},
def::CtorOf::Struct => {
span_lint_on_qpath_resolved(cx, qpath, false);
},
}
}
}
}
},
// unit enum variants (`Enum::A`)
ExprKind::Path(qpath) => {
if expr_ty_matches(cx, expr, self_ty) {
span_lint_on_qpath_resolved(cx, qpath, true);
}
},
_ => (),
}
}
},
// unit enum variants (`Enum::A`)
ExprKind::Path(QPath::Resolved(_, path)) => lint_path_to_variant(cx, path),
_ => (),
}
}
@ -405,33 +350,12 @@ fn span_lint(cx: &LateContext<'_>, span: Span) {
);
}
#[allow(clippy::cast_possible_truncation)]
fn span_lint_until_last_segment(cx: &LateContext<'_>, span: Span, segment: &PathSegment<'_>) {
let sp = span.with_hi(segment.ident.span.lo());
// remove the trailing ::
let span_without_last_segment = match snippet_opt(cx, sp) {
Some(snippet) => match snippet.rfind("::") {
Some(bidx) => sp.with_hi(sp.lo() + BytePos(bidx as u32)),
None => sp,
},
None => sp,
};
span_lint(cx, span_without_last_segment);
}
fn span_lint_on_path_until_last_segment(cx: &LateContext<'_>, path: &Path<'_>) {
if path.segments.len() > 1 {
span_lint_until_last_segment(cx, path.span, path.segments.last().unwrap());
}
}
fn span_lint_on_qpath_resolved(cx: &LateContext<'_>, qpath: &QPath<'_>, until_last_segment: bool) {
if let QPath::Resolved(_, path) = qpath {
if until_last_segment {
span_lint_on_path_until_last_segment(cx, path);
} else {
span_lint(cx, path.span);
}
fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) {
if let [.., self_seg, _variant] = path.segments {
let span = path
.span
.with_hi(self_seg.args().span_ext().unwrap_or(self_seg.ident.span).hi());
span_lint(cx, span);
}
}
@ -462,7 +386,7 @@ fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool {
if same_type_and_consts(ty, self_ty);
if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
then {
!matches!(path.res, def::Res::SelfTy(..))
!matches!(path.res, Res::SelfTy(..) | Res::Def(DefKind::TyParam, _))
} else {
false
}

View File

@ -149,7 +149,7 @@ define_Conf! {
"WebGL",
"TensorFlow",
"TrueType",
"iOS", "macOS",
"iOS", "macOS", "FreeBSD",
"TeX", "LaTeX", "BibTeX", "BibLaTeX",
"MinGW",
"CamelCase",
@ -190,6 +190,8 @@ define_Conf! {
(warn_on_all_wildcard_imports: bool = false),
/// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths.
(disallowed_methods: Vec<String> = Vec::new()),
/// Lint: DISALLOWED_TYPE. The list of disallowed types, written as fully qualified paths.
(disallowed_types: Vec<String> = Vec::new()),
/// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
(unreadable_literal_lint_fractions: bool = true),
/// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other

View File

@ -9,6 +9,7 @@
//! a simple mistake)
use if_chain::if_chain;
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{
self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath,
@ -485,16 +486,32 @@ fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<St
///
/// Would result in `Hello world!\n=^.^=\n`
fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
cx.tcx
.hir()
.attrs(item.hir_id())
.iter()
.filter_map(|x| x.doc_str().map(|sym| sym.as_str().to_string()))
.reduce(|mut acc, sym| {
acc.push_str(&sym);
acc.push('\n');
acc
})
let attrs = cx.tcx.hir().attrs(item.hir_id());
let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str);
let mut docs = String::from(&*lines.next()?.as_str());
let mut in_code_block = false;
for line in lines {
docs.push('\n');
let line = line.as_str();
let line = &*line;
if let Some(info) = line.trim_start().strip_prefix("```") {
in_code_block = !in_code_block;
if in_code_block {
let lang = info
.trim()
.split(',')
// remove rustdoc directives
.find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic"))
// if no language is present, fill in "rust"
.unwrap_or("rust");
docs.push_str("```");
docs.push_str(lang);
continue;
}
}
docs.push_str(line);
}
Some(docs)
}
fn get_lint_group_and_level_or_lint(

View File

@ -47,9 +47,14 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
| (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
(Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)),
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
(TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)),
(TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => {
eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r))
},
(Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => {
lr == rr && eq_maybe_qself(lqself, rqself) &&eq_path(lp, rp) && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf))
lr == rr
&& eq_maybe_qself(lqself, rqself)
&& eq_path(lp, rp)
&& unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf))
},
(Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)),
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
@ -82,7 +87,7 @@ pub fn eq_maybe_qself(l: &Option<QSelf>, r: &Option<QSelf>) -> bool {
match (l, r) {
(Some(l), Some(r)) => eq_qself(l, r),
(None, None) => true,
_ => false
_ => false,
}
}
@ -178,7 +183,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
(Struct(lse), Struct(rse)) => {
eq_maybe_qself(&lse.qself, &rse.qself)
eq_maybe_qself(&lse.qself, &rse.qself)
&& eq_path(&lse.path, &rse.path)
&& eq_struct_rest(&lse.rest, &rse.rest)
&& unordered_over(&lse.fields, &rse.fields, |l, r| eq_field(l, r))

View File

@ -1,6 +1,6 @@
#![allow(clippy::float_cmp)]
use crate::{clip, sext, unsext};
use crate::{clip, is_direct_expn_of, sext, unsext};
use if_chain::if_chain;
use rustc_ast::ast::{self, LitFloatType, LitKind};
use rustc_data_structures::sync::Lrc;
@ -230,7 +230,13 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
match e.kind {
ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
ExprKind::Block(block, _) => self.block(block),
ExprKind::Lit(ref lit) => Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e))),
ExprKind::Lit(ref lit) => {
if is_direct_expn_of(e.span, "cfg").is_some() {
None
} else {
Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e)))
}
},
ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec),
ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
ExprKind::Repeat(value, _) => {

View File

@ -72,7 +72,7 @@ use rustc_hir::LangItem::{ResultErr, ResultOk};
use rustc_hir::{
def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path,
PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
};
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::exports::Export;
@ -326,16 +326,6 @@ pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol)
.map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
}
/// Checks if an expression references a variable of the given name.
pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool {
if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
if let [p] = path.segments {
return p.ident.name == var;
}
}
false
}
pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
match *path {
QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
@ -707,16 +697,6 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
}
}
/// Gets the name of a `Pat`, if any.
pub fn get_pat_name(pat: &Pat<'_>) -> Option<Symbol> {
match pat.kind {
PatKind::Binding(.., ref spname, _) => Some(spname.name),
PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.ident.name),
PatKind::Box(p) | PatKind::Ref(p, _) => get_pat_name(&*p),
_ => None,
}
}
pub struct ContainsName {
pub name: Symbol,
pub result: bool,
@ -861,14 +841,16 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
})
}
/// Gets the loop enclosing the given expression, if any.
pub fn get_enclosing_loop(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
/// Gets the loop or closure enclosing the given expression, if any.
pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
let map = tcx.hir();
for (_, node) in map.parent_iter(expr.hir_id) {
match node {
Node::Expr(
e @ Expr {
kind: ExprKind::Loop(..),
e
@
Expr {
kind: ExprKind::Loop(..) | ExprKind::Closure(..),
..
},
) => return Some(e),
@ -1399,6 +1381,55 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some())
}
/// Checks if an expression represents the identity function
/// Only examines closures and `std::convert::identity`
pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
/// Checks if a function's body represents the identity function. Looks for bodies of the form:
/// * `|x| x`
/// * `|x| return x`
/// * `|x| { return x }`
/// * `|x| { return x; }`
fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
let id = if_chain! {
if let [param] = func.params;
if let PatKind::Binding(_, id, _, _) = param.pat.kind;
then {
id
} else {
return false;
}
};
let mut expr = &func.value;
loop {
match expr.kind {
#[rustfmt::skip]
ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
| ExprKind::Ret(Some(e)) => expr = e,
#[rustfmt::skip]
ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
if_chain! {
if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
if let ExprKind::Ret(Some(ret_val)) = e.kind;
then {
expr = ret_val;
} else {
return false;
}
}
},
_ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
}
}
}
match expr.kind {
ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
_ => false,
}
}
/// Gets the node where an expression is either used, or it's type is unified with another branch.
pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
let map = tcx.hir();
@ -1654,6 +1685,19 @@ pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
(e, count)
}
/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
loop {
match expr.kind {
ExprKind::AddrOf(_, _, e) => expr = e,
ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
_ => break,
}
}
expr
}
#[macro_export]
macro_rules! unwrap_cargo_metadata {
($cx: ident, $lint: ident, $deps: expr) => {{

View File

@ -1,10 +1,10 @@
use crate::source::snippet;
use crate::{get_pat_name, match_var};
use crate::{path_to_local_id, strip_pat_refs};
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{Body, BodyId, Expr, ExprKind, Param};
use rustc_hir::{Body, BodyId, Expr, ExprKind, HirId, PatKind};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
use rustc_span::{Span, Symbol};
use rustc_span::Span;
use std::borrow::Cow;
pub fn get_spans(
@ -14,10 +14,11 @@ pub fn get_spans(
replacements: &[(&'static str, &'static str)],
) -> Option<Vec<(Span, Cow<'static, str>)>> {
if let Some(body) = opt_body_id.map(|id| cx.tcx.hir().body(id)) {
get_binding_name(&body.params[idx]).map_or_else(
|| Some(vec![]),
|name| extract_clone_suggestions(cx, name, replacements, body),
)
if let PatKind::Binding(_, binding_id, _, _) = strip_pat_refs(body.params[idx].pat).kind {
extract_clone_suggestions(cx, binding_id, replacements, body)
} else {
Some(vec![])
}
} else {
Some(vec![])
}
@ -25,13 +26,13 @@ pub fn get_spans(
fn extract_clone_suggestions<'tcx>(
cx: &LateContext<'tcx>,
name: Symbol,
id: HirId,
replace: &[(&'static str, &'static str)],
body: &'tcx Body<'_>,
) -> Option<Vec<(Span, Cow<'static, str>)>> {
let mut visitor = PtrCloneVisitor {
cx,
name,
id,
replace,
spans: vec![],
abort: false,
@ -42,7 +43,7 @@ fn extract_clone_suggestions<'tcx>(
struct PtrCloneVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
name: Symbol,
id: HirId,
replace: &'a [(&'static str, &'static str)],
spans: Vec<(Span, Cow<'static, str>)>,
abort: bool,
@ -55,16 +56,15 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
if self.abort {
return;
}
if let ExprKind::MethodCall(seg, _, args, _) = expr.kind {
if args.len() == 1 && match_var(&args[0], self.name) {
if let ExprKind::MethodCall(seg, _, [recv], _) = expr.kind {
if path_to_local_id(recv, self.id) {
if seg.ident.name.as_str() == "capacity" {
self.abort = true;
return;
}
for &(fn_name, suffix) in self.replace {
if seg.ident.name.as_str() == fn_name {
self.spans
.push((expr.span, snippet(self.cx, args[0].span, "_") + suffix));
self.spans.push((expr.span, snippet(self.cx, recv.span, "_") + suffix));
return;
}
}
@ -77,7 +77,3 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
NestedVisitorMap::None
}
}
fn get_binding_name(arg: &Param<'_>) -> Option<Symbol> {
get_pat_name(arg.pat)
}

View File

@ -6,7 +6,7 @@ You may need following tooltips to catch up with common operations.
- [Retrieving the type of an expression](#retrieving-the-type-of-an-expression)
- [Checking if an expression is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method)
- [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait)
- [Checking if a type defines a method](#checking-if-a-type-defines-a-method)
- [Checking if a type defines a specific method](#checking-if-a-type-defines-a-specific-method)
- [Dealing with macros](#dealing-with-macros)
Useful Rustc dev guide links:

View File

@ -101,6 +101,21 @@ After this, the release should be available on the Clippy [release page].
[release page]: https://github.com/rust-lang/rust-clippy/releases
## Update the `stable` branch
At this step you should have already checked out the commit of the `rust-1.XX.0`
tag. Updating the stable branch from here is as easy as:
```bash
# Assuming the current directory corresponds to the Clippy repository and the
# commit of the just created rust-1.XX.0 tag is checked out.
$ git push upstream rust-1.XX.0:stable # `upstream` is the `rust-lang/rust-clippy` remote
```
_NOTE: Usually there are no stable backports for Clippy, so this update should
be possible without force pushing or anything like this. If there should have
happened a stable backport, make sure to re-merge those changes just as with the
`beta` branch._
## Update `CHANGELOG.md`

View File

@ -1 +1,5 @@
disallowed-methods = ["core::iter::traits::iterator::Iterator::sum", "regex::re_unicode::Regex::is_match", "regex::re_unicode::Regex::new"]
disallowed-methods = [
"std::iter::Iterator::sum",
"regex::Regex::is_match",
"regex::Regex::new"
]

View File

@ -0,0 +1,9 @@
disallowed-types = [
"std::collections::HashMap",
"std::sync::atomic::AtomicU32",
"syn::TypePath",
"proc_macro2::Ident",
"std::thread::Thread",
"std::time::Instant",
"std::io::Read",
]

View File

@ -0,0 +1,35 @@
#![warn(clippy::disallowed_type)]
extern crate quote;
extern crate syn;
use std::sync as foo;
use std::sync::atomic::AtomicU32;
use std::time::Instant as Sneaky;
struct HashMap;
fn bad_return_type() -> fn() -> Sneaky {
todo!()
}
fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {
todo!()
}
fn trait_obj(_: &dyn std::io::Read) {
todo!()
}
static BAD: foo::atomic::AtomicPtr<()> = foo::atomic::AtomicPtr::new(std::ptr::null_mut());
#[allow(clippy::diverging_sub_expression)]
fn main() {
let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
let _ = Sneaky::now();
let _ = foo::atomic::AtomicU32::new(0);
static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
let _ = syn::Ident::new("", todo!());
let _ = HashMap;
}

View File

@ -0,0 +1,88 @@
error: `std::sync::atomic::AtomicU32` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:7:1
|
LL | use std::sync::atomic::AtomicU32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::disallowed-type` implied by `-D warnings`
error: `std::time::Instant` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:8:1
|
LL | use std::time::Instant as Sneaky;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `std::time::Instant` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:12:33
|
LL | fn bad_return_type() -> fn() -> Sneaky {
| ^^^^^^
error: `std::time::Instant` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:16:28
|
LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {
| ^^^^^^
error: `std::sync::atomic::AtomicU32` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:16:39
|
LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {
| ^^^^^^^^^^^^^^^^^^^^^^
error: `std::io::Read` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:20:22
|
LL | fn trait_obj(_: &dyn std::io::Read) {
| ^^^^^^^^^^^^^
error: `std::collections::HashMap` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:28:48
|
LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: `std::collections::HashMap` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:28:12
|
LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `std::time::Instant` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:29:13
|
LL | let _ = Sneaky::now();
| ^^^^^^
error: `std::sync::atomic::AtomicU32` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:30:13
|
LL | let _ = foo::atomic::AtomicU32::new(0);
| ^^^^^^^^^^^^^^^^^^^^^^
error: `std::sync::atomic::AtomicU32` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:31:17
|
LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `std::sync::atomic::AtomicU32` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:31:48
|
LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
| ^^^^^^^^^^^^^^^^^^^^^^
error: `syn::TypePath` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:32:43
|
LL | let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
| ^^^^^^^^^^^^^
error: `proc_macro2::Ident` is not allowed according to config
--> $DIR/conf_disallowed_type.rs:33:13
|
LL | let _ = syn::Ident::new("", todo!());
| ^^^^^^^^^^
error: aborting due to 14 previous errors

View File

@ -1,4 +1,4 @@
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `third-party` at line 5 column 1
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `third-party` at line 5 column 1
error: aborting due to previous error

View File

@ -0,0 +1,55 @@
// run-rustfix
#![warn(clippy::append_instead_of_extend)]
use std::collections::BinaryHeap;
fn main() {
//gets linted
let mut vec1 = vec![0u8; 1024];
let mut vec2: std::vec::Vec<u8> = Vec::new();
vec2.append(&mut vec1);
let mut vec3 = vec![0u8; 1024];
let mut vec4: std::vec::Vec<u8> = Vec::new();
vec4.append(&mut vec3);
let mut vec11: std::vec::Vec<u8> = Vec::new();
vec11.append(&mut return_vector());
//won't get linted it dosen't move the entire content of a vec into another
let mut test1 = vec![0u8, 10];
let mut test2: std::vec::Vec<u8> = Vec::new();
test2.extend(test1.drain(4..10));
let mut vec3 = vec![0u8; 104];
let mut vec7: std::vec::Vec<u8> = Vec::new();
vec3.append(&mut vec7);
let mut vec5 = vec![0u8; 1024];
let mut vec6: std::vec::Vec<u8> = Vec::new();
vec5.extend(vec6.drain(..4));
let mut vec9: std::vec::Vec<u8> = Vec::new();
return_vector().append(&mut vec9);
//won't get linted because it is not a vec
let mut heap = BinaryHeap::from(vec![1, 3]);
let mut heap2 = BinaryHeap::from(vec![]);
heap2.extend(heap.drain())
}
fn return_vector() -> Vec<u8> {
let mut new_vector = vec![];
for i in 1..10 {
new_vector.push(i)
}
new_vector
}

View File

@ -0,0 +1,55 @@
// run-rustfix
#![warn(clippy::append_instead_of_extend)]
use std::collections::BinaryHeap;
fn main() {
//gets linted
let mut vec1 = vec![0u8; 1024];
let mut vec2: std::vec::Vec<u8> = Vec::new();
vec2.extend(vec1.drain(..));
let mut vec3 = vec![0u8; 1024];
let mut vec4: std::vec::Vec<u8> = Vec::new();
vec4.extend(vec3.drain(..));
let mut vec11: std::vec::Vec<u8> = Vec::new();
vec11.extend(return_vector().drain(..));
//won't get linted it dosen't move the entire content of a vec into another
let mut test1 = vec![0u8, 10];
let mut test2: std::vec::Vec<u8> = Vec::new();
test2.extend(test1.drain(4..10));
let mut vec3 = vec![0u8; 104];
let mut vec7: std::vec::Vec<u8> = Vec::new();
vec3.append(&mut vec7);
let mut vec5 = vec![0u8; 1024];
let mut vec6: std::vec::Vec<u8> = Vec::new();
vec5.extend(vec6.drain(..4));
let mut vec9: std::vec::Vec<u8> = Vec::new();
return_vector().append(&mut vec9);
//won't get linted because it is not a vec
let mut heap = BinaryHeap::from(vec![1, 3]);
let mut heap2 = BinaryHeap::from(vec![]);
heap2.extend(heap.drain())
}
fn return_vector() -> Vec<u8> {
let mut new_vector = vec![];
for i in 1..10 {
new_vector.push(i)
}
new_vector
}

View File

@ -0,0 +1,22 @@
error: use of `extend` instead of `append` for adding the full range of a second vector
--> $DIR/append_instead_of_extend.rs:9:5
|
LL | vec2.extend(vec1.drain(..));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec2.append(&mut vec1)`
|
= note: `-D clippy::append-instead-of-extend` implied by `-D warnings`
error: use of `extend` instead of `append` for adding the full range of a second vector
--> $DIR/append_instead_of_extend.rs:14:5
|
LL | vec4.extend(vec3.drain(..));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec4.append(&mut vec3)`
error: use of `extend` instead of `append` for adding the full range of a second vector
--> $DIR/append_instead_of_extend.rs:18:5
|
LL | vec11.extend(return_vector().drain(..));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec11.append(&mut return_vector())`
error: aborting due to 3 previous errors

View File

@ -28,4 +28,7 @@ fn main() {
debug_assert!(false); // #3948
assert_const!(3);
assert_const!(-1);
// Don't lint on this:
assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf")));
}

View File

@ -106,3 +106,10 @@ macro_rules! field_reassign_with_default {
}
};
}
#[macro_export]
macro_rules! default_numeric_fallback {
() => {
let x = 22;
};
}

View File

@ -1,3 +1,5 @@
// aux-build:macro_rules.rs
#![warn(clippy::default_numeric_fallback)]
#![allow(unused)]
#![allow(clippy::never_loop)]
@ -5,6 +7,9 @@
#![allow(clippy::unnecessary_operation)]
#![allow(clippy::branches_sharing_code)]
#[macro_use]
extern crate macro_rules;
mod basic_expr {
fn test() {
// Should lint unsuffixed literals typed `i32`.
@ -133,4 +138,22 @@ mod method_calls {
}
}
mod in_macro {
macro_rules! internal_macro {
() => {
let x = 22;
};
}
// Should lint in internal macro.
fn internal() {
internal_macro!();
}
// Should NOT lint in external macro.
fn external() {
default_numeric_fallback!();
}
}
fn main() {}

View File

@ -1,5 +1,5 @@
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:11:17
--> $DIR/default_numeric_fallback.rs:16:17
|
LL | let x = 22;
| ^^ help: consider adding suffix: `22_i32`
@ -7,142 +7,153 @@ LL | let x = 22;
= note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:12:18
--> $DIR/default_numeric_fallback.rs:17:18
|
LL | let x = [1, 2, 3];
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:12:21
--> $DIR/default_numeric_fallback.rs:17:21
|
LL | let x = [1, 2, 3];
| ^ help: consider adding suffix: `2_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:12:24
--> $DIR/default_numeric_fallback.rs:17:24
|
LL | let x = [1, 2, 3];
| ^ help: consider adding suffix: `3_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:13:28
--> $DIR/default_numeric_fallback.rs:18:28
|
LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:13:31
--> $DIR/default_numeric_fallback.rs:18:31
|
LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `2_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:13:44
--> $DIR/default_numeric_fallback.rs:18:44
|
LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `3_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:13:47
--> $DIR/default_numeric_fallback.rs:18:47
|
LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `4_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:14:23
--> $DIR/default_numeric_fallback.rs:19:23
|
LL | let x = match 1 {
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:15:13
--> $DIR/default_numeric_fallback.rs:20:13
|
LL | 1 => 1,
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:15:18
--> $DIR/default_numeric_fallback.rs:20:18
|
LL | 1 => 1,
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:16:18
--> $DIR/default_numeric_fallback.rs:21:18
|
LL | _ => 2,
| ^ help: consider adding suffix: `2_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:20:17
--> $DIR/default_numeric_fallback.rs:25:17
|
LL | let x = 0.12;
| ^^^^ help: consider adding suffix: `0.12_f64`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:38:21
--> $DIR/default_numeric_fallback.rs:43:21
|
LL | let y = 1;
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:46:21
--> $DIR/default_numeric_fallback.rs:51:21
|
LL | let y = 1;
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:52:21
--> $DIR/default_numeric_fallback.rs:57:21
|
LL | let y = 1;
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:64:9
--> $DIR/default_numeric_fallback.rs:69:9
|
LL | 1
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:70:27
--> $DIR/default_numeric_fallback.rs:75:27
|
LL | let f = || -> _ { 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:74:29
--> $DIR/default_numeric_fallback.rs:79:29
|
LL | let f = || -> i32 { 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:88:21
--> $DIR/default_numeric_fallback.rs:93:21
|
LL | generic_arg(1);
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:91:32
--> $DIR/default_numeric_fallback.rs:96:32
|
LL | let x: _ = generic_arg(1);
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:109:28
--> $DIR/default_numeric_fallback.rs:114:28
|
LL | GenericStruct { x: 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:112:36
--> $DIR/default_numeric_fallback.rs:117:36
|
LL | let _ = GenericStruct { x: 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:132:23
--> $DIR/default_numeric_fallback.rs:137:23
|
LL | s.generic_arg(1);
| ^ help: consider adding suffix: `1_i32`
error: aborting due to 24 previous errors
error: default numeric fallback might occur
--> $DIR/default_numeric_fallback.rs:144:21
|
LL | let x = 22;
| ^^ help: consider adding suffix: `22_i32`
...
LL | internal_macro!();
| ------------------ in this macro invocation
|
= note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 25 previous errors

View File

@ -64,7 +64,7 @@ fn test_units() {
/// WebGL
/// TensorFlow
/// TrueType
/// iOS macOS
/// iOS macOS FreeBSD
/// TeX LaTeX BibTeX BibLaTeX
/// MinGW
/// CamelCase (see also #2395)

View File

@ -29,6 +29,21 @@ struct C {
i: Vec<i32>,
j: i64,
}
#[derive(Default)]
struct D {
a: Option<i32>,
b: Option<i32>,
}
macro_rules! m {
($key:ident: $value:tt) => {{
let mut data = $crate::D::default();
data.$key = Some($value);
data
}};
}
/// Implements .next() that returns a different number each time.
struct SideEffect(i32);
@ -143,6 +158,11 @@ fn main() {
let mut a: WrapperMulti<i32, i64> = Default::default();
a.i = 42;
// Don't lint in macros
m! {
a: 42
};
}
mod m {

View File

@ -1,108 +1,108 @@
error: field assignment outside of initializer for an instance created with Default::default()
--> $DIR/field_reassign_with_default.rs:48:5
--> $DIR/field_reassign_with_default.rs:63:5
|
LL | a.i = 42;
| ^^^^^^^^^
|
= note: `-D clippy::field-reassign-with-default` implied by `-D warnings`
note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments
--> $DIR/field_reassign_with_default.rs:47:5
--> $DIR/field_reassign_with_default.rs:62:5
|
LL | let mut a: A = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> $DIR/field_reassign_with_default.rs:88:5
--> $DIR/field_reassign_with_default.rs:103:5
|
LL | a.j = 43;
| ^^^^^^^^^
|
note: consider initializing the variable with `main::A { j: 43, i: 42 }` and removing relevant reassignments
--> $DIR/field_reassign_with_default.rs:87:5
--> $DIR/field_reassign_with_default.rs:102:5
|
LL | let mut a: A = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> $DIR/field_reassign_with_default.rs:93:5
--> $DIR/field_reassign_with_default.rs:108:5
|
LL | a.i = 42;
| ^^^^^^^^^
|
note: consider initializing the variable with `main::A { i: 42, j: 44 }` and removing relevant reassignments
--> $DIR/field_reassign_with_default.rs:92:5
--> $DIR/field_reassign_with_default.rs:107:5
|
LL | let mut a: A = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> $DIR/field_reassign_with_default.rs:99:5
--> $DIR/field_reassign_with_default.rs:114:5
|
LL | a.i = 42;
| ^^^^^^^^^
|
note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments
--> $DIR/field_reassign_with_default.rs:98:5
--> $DIR/field_reassign_with_default.rs:113:5
|
LL | let mut a = A::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> $DIR/field_reassign_with_default.rs:109:5
--> $DIR/field_reassign_with_default.rs:124:5
|
LL | a.i = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: consider initializing the variable with `main::A { i: Default::default(), ..Default::default() }` and removing relevant reassignments
--> $DIR/field_reassign_with_default.rs:108:5
--> $DIR/field_reassign_with_default.rs:123:5
|
LL | let mut a: A = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> $DIR/field_reassign_with_default.rs:113:5
--> $DIR/field_reassign_with_default.rs:128:5
|
LL | a.i = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: consider initializing the variable with `main::A { i: Default::default(), j: 45 }` and removing relevant reassignments
--> $DIR/field_reassign_with_default.rs:112:5
--> $DIR/field_reassign_with_default.rs:127:5
|
LL | let mut a: A = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> $DIR/field_reassign_with_default.rs:135:5
--> $DIR/field_reassign_with_default.rs:150:5
|
LL | a.i = vec![1];
| ^^^^^^^^^^^^^^
|
note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments
--> $DIR/field_reassign_with_default.rs:134:5
--> $DIR/field_reassign_with_default.rs:149:5
|
LL | let mut a: C = C::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> $DIR/field_reassign_with_default.rs:142:5
--> $DIR/field_reassign_with_default.rs:157:5
|
LL | a.i = true;
| ^^^^^^^^^^^
|
note: consider initializing the variable with `Wrapper::<bool> { i: true }` and removing relevant reassignments
--> $DIR/field_reassign_with_default.rs:141:5
--> $DIR/field_reassign_with_default.rs:156:5
|
LL | let mut a: Wrapper<bool> = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: field assignment outside of initializer for an instance created with Default::default()
--> $DIR/field_reassign_with_default.rs:145:5
--> $DIR/field_reassign_with_default.rs:160:5
|
LL | a.i = 42;
| ^^^^^^^^^
|
note: consider initializing the variable with `WrapperMulti::<i32, i64> { i: 42, ..Default::default() }` and removing relevant reassignments
--> $DIR/field_reassign_with_default.rs:144:5
--> $DIR/field_reassign_with_default.rs:159:5
|
LL | let mut a: WrapperMulti<i32, i64> = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -1,6 +1,6 @@
// run-rustfix
#![allow(unused_imports)]
#![allow(unused_imports, clippy::needless_return)]
#![warn(clippy::filter_map_identity)]
fn main() {
@ -13,4 +13,7 @@ fn main() {
use std::convert::identity;
let iterator = vec![Some(1), None, Some(2)].into_iter();
let _ = iterator.flatten();
let iterator = vec![Some(1), None, Some(2)].into_iter();
let _ = iterator.flatten();
}

View File

@ -1,6 +1,6 @@
// run-rustfix
#![allow(unused_imports)]
#![allow(unused_imports, clippy::needless_return)]
#![warn(clippy::filter_map_identity)]
fn main() {
@ -13,4 +13,7 @@ fn main() {
use std::convert::identity;
let iterator = vec![Some(1), None, Some(2)].into_iter();
let _ = iterator.filter_map(identity);
let iterator = vec![Some(1), None, Some(2)].into_iter();
let _ = iterator.filter_map(|x| return x);
}

View File

@ -1,4 +1,4 @@
error: called `filter_map(|x| x)` on an `Iterator`
error: use of `filter_map` with an identity function
--> $DIR/filter_map_identity.rs:8:22
|
LL | let _ = iterator.filter_map(|x| x);
@ -6,17 +6,23 @@ LL | let _ = iterator.filter_map(|x| x);
|
= note: `-D clippy::filter-map-identity` implied by `-D warnings`
error: called `filter_map(std::convert::identity)` on an `Iterator`
error: use of `filter_map` with an identity function
--> $DIR/filter_map_identity.rs:11:22
|
LL | let _ = iterator.filter_map(std::convert::identity);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
error: called `filter_map(std::convert::identity)` on an `Iterator`
error: use of `filter_map` with an identity function
--> $DIR/filter_map_identity.rs:15:22
|
LL | let _ = iterator.filter_map(identity);
| ^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
error: aborting due to 3 previous errors
error: use of `filter_map` with an identity function
--> $DIR/filter_map_identity.rs:18:22
|
LL | let _ = iterator.filter_map(|x| return x);
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
error: aborting due to 4 previous errors

View File

@ -1,6 +1,6 @@
// run-rustfix
#![allow(unused_imports)]
#![allow(unused_imports, clippy::needless_return)]
#![warn(clippy::flat_map_identity)]
use std::convert;
@ -11,4 +11,7 @@ fn main() {
let iterator = [[0, 1], [2, 3], [4, 5]].iter();
let _ = iterator.flatten();
let iterator = [[0, 1], [2, 3], [4, 5]].iter();
let _ = iterator.flatten();
}

View File

@ -1,6 +1,6 @@
// run-rustfix
#![allow(unused_imports)]
#![allow(unused_imports, clippy::needless_return)]
#![warn(clippy::flat_map_identity)]
use std::convert;
@ -11,4 +11,7 @@ fn main() {
let iterator = [[0, 1], [2, 3], [4, 5]].iter();
let _ = iterator.flat_map(convert::identity);
let iterator = [[0, 1], [2, 3], [4, 5]].iter();
let _ = iterator.flat_map(|x| return x);
}

View File

@ -1,4 +1,4 @@
error: called `flat_map(|x| x)` on an `Iterator`
error: use of `flat_map` with an identity function
--> $DIR/flat_map_identity.rs:10:22
|
LL | let _ = iterator.flat_map(|x| x);
@ -6,11 +6,17 @@ LL | let _ = iterator.flat_map(|x| x);
|
= note: `-D clippy::flat-map-identity` implied by `-D warnings`
error: called `flat_map(std::convert::identity)` on an `Iterator`
error: use of `flat_map` with an identity function
--> $DIR/flat_map_identity.rs:13:22
|
LL | let _ = iterator.flat_map(convert::identity);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
error: aborting due to 2 previous errors
error: use of `flat_map` with an identity function
--> $DIR/flat_map_identity.rs:16:22
|
LL | let _ = iterator.flat_map(|x| return x);
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()`
error: aborting due to 3 previous errors

View File

@ -17,6 +17,24 @@ fn basic101(x: i32) {
y = x + 1
}
#[rustfmt::skip]
fn closure_error() {
let _d = || {
hello()
};
}
#[rustfmt::skip]
fn unsafe_checks_error() {
use std::mem::MaybeUninit;
use std::ptr;
let mut s = MaybeUninit::<String>::uninit();
let _d = || unsafe {
ptr::drop_in_place(s.as_mut_ptr())
};
}
// this is fine
fn print_sum(a: i32, b: i32) {
println!("{}", a + b);
@ -53,3 +71,29 @@ fn loop_test(x: i32) {
println!("{}", ext);
}
}
fn closure() {
let _d = || hello();
}
#[rustfmt::skip]
fn closure_block() {
let _d = || { hello() };
}
unsafe fn some_unsafe_op() {}
unsafe fn some_other_unsafe_fn() {}
fn do_something() {
unsafe { some_unsafe_op() };
unsafe { some_other_unsafe_fn() };
}
fn unsafe_checks() {
use std::mem::MaybeUninit;
use std::ptr;
let mut s = MaybeUninit::<String>::uninit();
let _d = || unsafe { ptr::drop_in_place(s.as_mut_ptr()) };
}

View File

@ -18,5 +18,17 @@ error: consider adding a `;` to the last statement for consistent formatting
LL | y = x + 1
| ^^^^^^^^^ help: add a `;` here: `y = x + 1;`
error: aborting due to 3 previous errors
error: consider adding a `;` to the last statement for consistent formatting
--> $DIR/semicolon_if_nothing_returned.rs:23:9
|
LL | hello()
| ^^^^^^^ help: add a `;` here: `hello();`
error: consider adding a `;` to the last statement for consistent formatting
--> $DIR/semicolon_if_nothing_returned.rs:34:9
|
LL | ptr::drop_in_place(s.as_mut_ptr())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());`
error: aborting due to 5 previous errors

View File

@ -1,5 +1,4 @@
#![warn(clippy::temporary_assignment)]
#![allow(const_item_mutation)]
use std::ops::{Deref, DerefMut};

View File

@ -1,5 +1,5 @@
error: assignment to temporary
--> $DIR/temporary_assignment.rs:48:5
--> $DIR/temporary_assignment.rs:47:5
|
LL | Struct { field: 0 }.field = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -7,7 +7,7 @@ LL | Struct { field: 0 }.field = 1;
= note: `-D clippy::temporary-assignment` implied by `-D warnings`
error: assignment to temporary
--> $DIR/temporary_assignment.rs:49:5
--> $DIR/temporary_assignment.rs:48:5
|
LL | / MultiStruct {
LL | | structure: Struct { field: 0 },
@ -17,13 +17,13 @@ LL | | .field = 1;
| |______________^
error: assignment to temporary
--> $DIR/temporary_assignment.rs:54:5
--> $DIR/temporary_assignment.rs:53:5
|
LL | ArrayStruct { array: [0] }.array[0] = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: assignment to temporary
--> $DIR/temporary_assignment.rs:55:5
--> $DIR/temporary_assignment.rs:54:5
|
LL | (0, 0).0 = 1;
| ^^^^^^^^^^^^

View File

@ -492,3 +492,26 @@ mod issue7206 {
}
}
}
mod self_is_ty_param {
trait Trait {
type Type;
type Hi;
fn test();
}
impl<I> Trait for I
where
I: Iterator,
I::Item: Trait, // changing this to Self would require <Self as Iterator>
{
type Type = I;
type Hi = I::Item;
fn test() {
let _: I::Item;
let _: I; // this could lint, but is questionable
}
}
}

View File

@ -279,7 +279,7 @@ mod generics {
impl<T> Foo<T> {
// `Self` is applicable here
fn foo(value: T) -> Foo<T> {
Foo { value }
Foo::<T> { value }
}
// `Cannot` use `Self` as a return type as the generic types are different
@ -492,3 +492,26 @@ mod issue7206 {
}
}
}
mod self_is_ty_param {
trait Trait {
type Type;
type Hi;
fn test();
}
impl<I> Trait for I
where
I: Iterator,
I::Item: Trait, // changing this to Self would require <Self as Iterator>
{
type Type = I;
type Hi = I::Item;
fn test() {
let _: I::Item;
let _: I; // this could lint, but is questionable
}
}
}

View File

@ -153,8 +153,8 @@ LL | fn foo(value: T) -> Foo<T> {
error: unnecessary structure name repetition
--> $DIR/use_self.rs:282:13
|
LL | Foo { value }
| ^^^ help: use the applicable keyword: `Self`
LL | Foo::<T> { value }
| ^^^^^^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
--> $DIR/use_self.rs:454:13

View File

@ -320,6 +320,20 @@ fn issue1924() {
println!("iterator field {}", it.1);
}
fn issue7249() {
let mut it = 0..10;
let mut x = || {
// Needs &mut, the closure can be called multiple times
for x in &mut it {
if x % 2 == 0 {
break;
}
}
};
x();
x();
}
fn main() {
let mut it = 0..20;
for _ in it {

View File

@ -320,6 +320,20 @@ fn issue1924() {
println!("iterator field {}", it.1);
}
fn issue7249() {
let mut it = 0..10;
let mut x = || {
// Needs &mut, the closure can be called multiple times
while let Some(x) = it.next() {
if x % 2 == 0 {
break;
}
}
};
x();
x();
}
fn main() {
let mut it = 0..20;
while let Some(..) = it.next() {

View File

@ -105,10 +105,16 @@ LL | while let Some(n) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it`
error: this loop could be written as a `for` loop
--> $DIR/while_let_on_iterator.rs:325:5
--> $DIR/while_let_on_iterator.rs:327:9
|
LL | while let Some(x) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut it`
error: this loop could be written as a `for` loop
--> $DIR/while_let_on_iterator.rs:339:5
|
LL | while let Some(..) = it.next() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
error: aborting due to 18 previous errors
error: aborting due to 19 previous errors