Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
e3eede7b90
5
.github/workflows/clippy_bors.yml
vendored
5
.github/workflows/clippy_bors.yml
vendored
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()`
|
||||
|
@ -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));
|
||||
|
@ -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)
|
||||
|
126
clippy_lints/src/disallowed_type.rs
Normal file
126
clippy_lints/src/disallowed_type.rs
Normal 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),
|
||||
);
|
||||
}
|
@ -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));
|
||||
|
||||
}
|
||||
|
||||
|
@ -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> {
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
},
|
||||
|
@ -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
|
||||
}
|
||||
}
|
41
clippy_lints/src/methods/append_instead_of_extend.rs
Normal file
41
clippy_lints/src/methods/append_instead_of_extend.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
38
clippy_lints/src/methods/map_identity.rs
Normal file
38
clippy_lints/src/methods/map_identity.rs
Normal 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -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", []) => {
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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};
|
||||
|
@ -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);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
),
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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))
|
||||
|
@ -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, _) => {
|
||||
|
@ -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) => {{
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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`
|
||||
|
||||
|
@ -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"
|
||||
]
|
||||
|
9
tests/ui-toml/toml_disallowed_type/clippy.toml
Normal file
9
tests/ui-toml/toml_disallowed_type/clippy.toml
Normal 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",
|
||||
]
|
35
tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs
Normal file
35
tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs
Normal 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;
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
55
tests/ui/append_instead_of_extend.fixed
Normal file
55
tests/ui/append_instead_of_extend.fixed
Normal 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
|
||||
}
|
55
tests/ui/append_instead_of_extend.rs
Normal file
55
tests/ui/append_instead_of_extend.rs
Normal 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
|
||||
}
|
22
tests/ui/append_instead_of_extend.stderr
Normal file
22
tests/ui/append_instead_of_extend.stderr
Normal 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
|
||||
|
@ -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")));
|
||||
}
|
||||
|
@ -106,3 +106,10 @@ macro_rules! field_reassign_with_default {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! default_numeric_fallback {
|
||||
() => {
|
||||
let x = 22;
|
||||
};
|
||||
}
|
||||
|
@ -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() {}
|
||||
|
@ -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
|
||||
|
||||
|
@ -64,7 +64,7 @@ fn test_units() {
|
||||
/// WebGL
|
||||
/// TensorFlow
|
||||
/// TrueType
|
||||
/// iOS macOS
|
||||
/// iOS macOS FreeBSD
|
||||
/// TeX LaTeX BibTeX BibLaTeX
|
||||
/// MinGW
|
||||
/// CamelCase (see also #2395)
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()) };
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
#![warn(clippy::temporary_assignment)]
|
||||
#![allow(const_item_mutation)]
|
||||
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
|
@ -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;
|
||||
| ^^^^^^^^^^^^
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user