未验证 提交 d5ab347d 编写于 作者: F flip1995

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

...@@ -74,10 +74,3 @@ jobs: ...@@ -74,10 +74,3 @@ jobs:
run: bash .github/driver.sh run: bash .github/driver.sh
env: env:
OS: ${{ runner.os }} OS: ${{ runner.os }}
- name: Test cargo dev new lint
run: |
cargo dev new_lint --name new_early_pass --pass early
cargo dev new_lint --name new_late_pass --pass late
cargo check
git reset --hard HEAD
...@@ -143,13 +143,6 @@ jobs: ...@@ -143,13 +143,6 @@ jobs:
env: env:
OS: ${{ runner.os }} OS: ${{ runner.os }}
- name: Test cargo dev new lint
run: |
cargo dev new_lint --name new_early_pass --pass early
cargo dev new_lint --name new_late_pass --pass late
cargo check
git reset --hard HEAD
integration_build: integration_build:
needs: changelog needs: changelog
runs-on: ubuntu-latest runs-on: ubuntu-latest
......
...@@ -36,6 +36,13 @@ jobs: ...@@ -36,6 +36,13 @@ jobs:
- name: Test fmt - name: Test fmt
run: cargo dev fmt --check run: cargo dev fmt --check
- name: Test cargo dev new lint
run: |
cargo dev new_lint --name new_early_pass --pass early
cargo dev new_lint --name new_late_pass --pass late
cargo check
git reset --hard HEAD
# These jobs doesn't actually test anything, but they're only used to tell # These jobs doesn't actually test anything, but they're only used to tell
# bors the build completed, as there is no practical way to detect when a # bors the build completed, as there is no practical way to detect when a
# workflow is successful listening to webhooks only. # workflow is successful listening to webhooks only.
......
...@@ -3042,6 +3042,7 @@ Released 2018-09-13 ...@@ -3042,6 +3042,7 @@ Released 2018-09-13
<!-- lint disable no-unused-definitions --> <!-- lint disable no-unused-definitions -->
<!-- begin autogenerated links to lint list --> <!-- begin autogenerated links to lint list -->
[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons [`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped [`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`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 [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
...@@ -3076,6 +3077,7 @@ Released 2018-09-13 ...@@ -3076,6 +3077,7 @@ Released 2018-09-13
[`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment [`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment
[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut [`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss [`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
[`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes
[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8 [`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp [`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp [`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
...@@ -3225,6 +3227,7 @@ Released 2018-09-13 ...@@ -3225,6 +3227,7 @@ Released 2018-09-13
[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero [`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned [`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero [`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits [`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays [`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
...@@ -3297,6 +3300,7 @@ Released 2018-09-13 ...@@ -3297,6 +3300,7 @@ Released 2018-09-13
[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items [`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc [`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
[`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop
[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes [`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals [`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files [`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files
...@@ -3327,6 +3331,7 @@ Released 2018-09-13 ...@@ -3327,6 +3331,7 @@ Released 2018-09-13
[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each [`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init [`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
...@@ -3351,6 +3356,7 @@ Released 2018-09-13 ...@@ -3351,6 +3356,7 @@ Released 2018-09-13
[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref [`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
[`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes [`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes
[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect [`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
[`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion
[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap [`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
...@@ -3498,6 +3504,7 @@ Released 2018-09-13 ...@@ -3498,6 +3504,7 @@ Released 2018-09-13
[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord [`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
[`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
......
...@@ -50,7 +50,7 @@ syn = { version = "1.0", features = ["full"] } ...@@ -50,7 +50,7 @@ syn = { version = "1.0", features = ["full"] }
futures = "0.3" futures = "0.3"
parking_lot = "0.11.2" parking_lot = "0.11.2"
tokio = { version = "1", features = ["io-util"] } tokio = { version = "1", features = ["io-util"] }
num_cpus = "1.13" rustc-semver = "1.1"
[build-dependencies] [build-dependencies]
rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
......
...@@ -255,7 +255,38 @@ ...@@ -255,7 +255,38 @@
"usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`" "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
} }
declare_clippy_lint! {
/// ### What it does
/// Checks for attributes that allow lints without a reason.
///
/// (This requires the `lint_reasons` feature)
///
/// ### Why is this bad?
/// Allowing a lint should always have a reason. This reason should be documented to
/// ensure that others understand the reasoning
///
/// ### Example
/// Bad:
/// ```rust
/// #![feature(lint_reasons)]
///
/// #![allow(clippy::some_lint)]
/// ```
///
/// Good:
/// ```rust
/// #![feature(lint_reasons)]
///
/// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
/// ```
#[clippy::version = "1.61.0"]
pub ALLOW_ATTRIBUTES_WITHOUT_REASON,
restriction,
"ensures that all `allow` and `expect` attributes have a reason"
}
declare_lint_pass!(Attributes => [ declare_lint_pass!(Attributes => [
ALLOW_ATTRIBUTES_WITHOUT_REASON,
INLINE_ALWAYS, INLINE_ALWAYS,
DEPRECATED_SEMVER, DEPRECATED_SEMVER,
USELESS_ATTRIBUTE, USELESS_ATTRIBUTE,
...@@ -269,6 +300,9 @@ fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) { ...@@ -269,6 +300,9 @@ fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) {
if is_lint_level(ident.name) { if is_lint_level(ident.name) {
check_clippy_lint_names(cx, ident.name, items); check_clippy_lint_names(cx, ident.name, items);
} }
if matches!(ident.name, sym::allow | sym::expect) {
check_lint_reason(cx, ident.name, items, attr);
}
if items.is_empty() || !attr.has_name(sym::deprecated) { if items.is_empty() || !attr.has_name(sym::deprecated) {
return; return;
} }
...@@ -404,6 +438,30 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe ...@@ -404,6 +438,30 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe
} }
} }
fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem], attr: &'_ Attribute) {
// Check for the feature
if !cx.tcx.sess.features_untracked().lint_reasons {
return;
}
// Check if the reason is present
if let Some(item) = items.last().and_then(NestedMetaItem::meta_item)
&& let MetaItemKind::NameValue(_) = &item.kind
&& item.path == sym::reason
{
return;
}
span_lint_and_help(
cx,
ALLOW_ATTRIBUTES_WITHOUT_REASON,
attr.span,
&format!("`{}` attribute without specifying a reason", name.as_str()),
None,
"try adding a reason at the end with `, reason = \"..\"`",
);
}
fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool { fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
if let ItemKind::Fn(_, _, eid) = item.kind { if let ItemKind::Fn(_, _, eid) = item.kind {
is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value) is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value)
...@@ -659,5 +717,5 @@ fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> { ...@@ -659,5 +717,5 @@ fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> {
} }
fn is_lint_level(symbol: Symbol) -> bool { fn is_lint_level(symbol: Symbol) -> bool {
matches!(symbol, sym::allow | sym::warn | sym::deny | sym::forbid) matches!(symbol, sym::allow | sym::expect | sym::warn | sym::deny | sym::forbid)
} }
use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source::snippet_opt};
use if_chain::if_chain;
use rustc_ast::Mutability;
use rustc_hir::{Expr, ExprKind, Node};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, layout::LayoutOf, Ty, TypeAndMut};
use rustc_semver::RustcVersion;
use super::CAST_SLICE_DIFFERENT_SIZES;
fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let map = cx.tcx.hir();
if_chain! {
if let Some(parent_id) = map.find_parent_node(expr.hir_id);
if let Some(parent) = map.find(parent_id);
then {
let expr = match parent {
Node::Block(block) => {
if let Some(parent_expr) = block.expr {
parent_expr
} else {
return false;
}
},
Node::Expr(expr) => expr,
_ => return false,
};
matches!(expr.kind, ExprKind::Cast(..))
} else {
false
}
}
}
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVersion>) {
// suggestion is invalid if `ptr::slice_from_raw_parts` does not exist
if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) {
return;
}
// if this cast is the child of another cast expression then don't emit something for it, the full
// chain will be analyzed
if is_child_of_cast(cx, expr) {
return;
}
if let Some((from_slice_ty, to_slice_ty)) = expr_cast_chain_tys(cx, expr) {
if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(from_slice_ty.ty), cx.layout_of(to_slice_ty.ty)) {
let from_size = from_layout.size.bytes();
let to_size = to_layout.size.bytes();
if from_size != to_size && from_size != 0 && to_size != 0 {
span_lint_and_then(
cx,
CAST_SLICE_DIFFERENT_SIZES,
expr.span,
&format!(
"casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count",
from_slice_ty, from_size, to_slice_ty, to_size,
),
|diag| {
let cast_expr = match expr.kind {
ExprKind::Cast(cast_expr, ..) => cast_expr,
_ => unreachable!("expr should be a cast as checked by expr_cast_chain_tys"),
};
let ptr_snippet = snippet_opt(cx, cast_expr.span).unwrap();
let (mutbl_fn_str, mutbl_ptr_str) = match to_slice_ty.mutbl {
Mutability::Mut => ("_mut", "mut"),
Mutability::Not => ("", "const"),
};
let sugg = format!(
"core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {to_slice_ty}, ..)"
);
diag.span_suggestion(
expr.span,
&format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
sugg,
rustc_errors::Applicability::HasPlaceholders,
);
},
);
}
}
}
}
/// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if
/// the type is one of those slices
fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option<TypeAndMut<'_>> {
match ty.kind() {
ty::RawPtr(TypeAndMut { ty: slice_ty, mutbl }) => match slice_ty.kind() {
ty::Slice(ty) => Some(TypeAndMut { ty: *ty, mutbl: *mutbl }),
_ => None,
},
_ => None,
}
}
/// Returns the pair (original ptr T, final ptr U) if the expression is composed of casts
/// Returns None if the expr is not a Cast
fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<(TypeAndMut<'tcx>, TypeAndMut<'tcx>)> {
if let ExprKind::Cast(cast_expr, _cast_to_hir_ty) = expr.peel_blocks().kind {
let cast_to = cx.typeck_results().expr_ty(expr);
let to_slice_ty = get_raw_slice_ty_mut(cast_to)?;
if let Some((inner_from_ty, _inner_to_ty)) = expr_cast_chain_tys(cx, cast_expr) {
Some((inner_from_ty, to_slice_ty))
} else {
let cast_from = cx.typeck_results().expr_ty(cast_expr);
let from_slice_ty = get_raw_slice_ty_mut(cast_from)?;
Some((from_slice_ty, to_slice_ty))
}
} else {
None
}
}
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
mod cast_ptr_alignment; mod cast_ptr_alignment;
mod cast_ref_to_mut; mod cast_ref_to_mut;
mod cast_sign_loss; mod cast_sign_loss;
mod cast_slice_different_sizes;
mod char_lit_as_u8; mod char_lit_as_u8;
mod fn_to_numeric_cast; mod fn_to_numeric_cast;
mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_any;
...@@ -409,6 +410,50 @@ ...@@ -409,6 +410,50 @@
"casts from an enum type to an integral type which will truncate the value" "casts from an enum type to an integral type which will truncate the value"
} }
declare_clippy_lint! {
/// Checks for `as` casts between raw pointers to slices with differently sized elements.
///
/// ### Why is this bad?
/// The produced raw pointer to a slice does not update its length metadata. The produced
/// pointer will point to a different number of bytes than the original pointer because the
/// length metadata of a raw slice pointer is in elements rather than bytes.
/// Producing a slice reference from the raw pointer will either create a slice with
/// less data (which can be surprising) or create a slice with more data and cause Undefined Behavior.
///
/// ### Example
/// // Missing data
/// ```rust
/// let a = [1_i32, 2, 3, 4];
/// let p = &a as *const [i32] as *const [u8];
/// unsafe {
/// println!("{:?}", &*p);
/// }
/// ```
/// // Undefined Behavior (note: also potential alignment issues)
/// ```rust
/// let a = [1_u8, 2, 3, 4];
/// let p = &a as *const [u8] as *const [u32];
/// unsafe {
/// println!("{:?}", &*p);
/// }
/// ```
/// Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length
/// ```rust
/// let a = [1_i32, 2, 3, 4];
/// let old_ptr = &a as *const [i32];
/// // The data pointer is cast to a pointer to the target `u8` not `[u8]`
/// // The length comes from the known length of 4 i32s times the 4 bytes per i32
/// let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16);
/// unsafe {
/// println!("{:?}", &*new_ptr);
/// }
/// ```
#[clippy::version = "1.60.0"]
pub CAST_SLICE_DIFFERENT_SIZES,
correctness,
"casting using `as` between raw pointers to slices of types with different sizes"
}
pub struct Casts { pub struct Casts {
msrv: Option<RustcVersion>, msrv: Option<RustcVersion>,
} }
...@@ -428,6 +473,7 @@ pub fn new(msrv: Option<RustcVersion>) -> Self { ...@@ -428,6 +473,7 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
CAST_LOSSLESS, CAST_LOSSLESS,
CAST_REF_TO_MUT, CAST_REF_TO_MUT,
CAST_PTR_ALIGNMENT, CAST_PTR_ALIGNMENT,
CAST_SLICE_DIFFERENT_SIZES,
UNNECESSARY_CAST, UNNECESSARY_CAST,
FN_TO_NUMERIC_CAST_ANY, FN_TO_NUMERIC_CAST_ANY,
FN_TO_NUMERIC_CAST, FN_TO_NUMERIC_CAST,
...@@ -478,6 +524,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { ...@@ -478,6 +524,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
cast_ref_to_mut::check(cx, expr); cast_ref_to_mut::check(cx, expr);
cast_ptr_alignment::check(cx, expr); cast_ptr_alignment::check(cx, expr);
char_lit_as_u8::check(cx, expr); char_lit_as_u8::check(cx, expr);
ptr_as_ptr::check(cx, expr, &self.msrv);
cast_slice_different_sizes::check(cx, expr, &self.msrv);
} }
extract_msrv_attr!(LateContext); extract_msrv_attr!(LateContext);
......
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_in_test_function;
use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability; use rustc_errors::Applicability;
...@@ -35,6 +36,10 @@ impl LateLintPass<'_> for DbgMacro { ...@@ -35,6 +36,10 @@ impl LateLintPass<'_> for DbgMacro {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
if cx.tcx.is_diagnostic_item(sym::dbg_macro, macro_call.def_id) { if cx.tcx.is_diagnostic_item(sym::dbg_macro, macro_call.def_id) {
// we make an exception for test code
if is_in_test_function(cx.tcx, expr.hir_id) {
return;
}
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let suggestion = match expr.peel_drop_temps().kind { let suggestion = match expr.peel_drop_temps().kind {
// dbg!() // dbg!()
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(booleans::NONMINIMAL_BOOL),
LintId::of(casts::CAST_ENUM_TRUNCATION), LintId::of(casts::CAST_ENUM_TRUNCATION),
LintId::of(casts::CAST_REF_TO_MUT), LintId::of(casts::CAST_REF_TO_MUT),
LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
LintId::of(casts::CHAR_LIT_AS_U8), LintId::of(casts::CHAR_LIT_AS_U8),
LintId::of(casts::FN_TO_NUMERIC_CAST), LintId::of(casts::FN_TO_NUMERIC_CAST),
LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
...@@ -109,6 +110,7 @@ ...@@ -109,6 +110,7 @@
LintId::of(loops::ITER_NEXT_LOOP), LintId::of(loops::ITER_NEXT_LOOP),
LintId::of(loops::MANUAL_FLATTEN), LintId::of(loops::MANUAL_FLATTEN),
LintId::of(loops::MANUAL_MEMCPY), LintId::of(loops::MANUAL_MEMCPY),
LintId::of(loops::MISSING_SPIN_LOOP),
LintId::of(loops::MUT_RANGE_BOUND), LintId::of(loops::MUT_RANGE_BOUND),
LintId::of(loops::NEEDLESS_COLLECT), LintId::of(loops::NEEDLESS_COLLECT),
LintId::of(loops::NEEDLESS_RANGE_LOOP), LintId::of(loops::NEEDLESS_RANGE_LOOP),
...@@ -136,6 +138,7 @@ ...@@ -136,6 +138,7 @@
LintId::of(matches::MATCH_OVERLAPPING_ARM), LintId::of(matches::MATCH_OVERLAPPING_ARM),
LintId::of(matches::MATCH_REF_PATS), LintId::of(matches::MATCH_REF_PATS),
LintId::of(matches::MATCH_SINGLE_BINDING), LintId::of(matches::MATCH_SINGLE_BINDING),
LintId::of(matches::NEEDLESS_MATCH),
LintId::of(matches::REDUNDANT_PATTERN_MATCHING), LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
LintId::of(matches::SINGLE_MATCH), LintId::of(matches::SINGLE_MATCH),
LintId::of(matches::WILDCARD_IN_OR_PATTERNS), LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
...@@ -163,6 +166,7 @@ ...@@ -163,6 +166,7 @@
LintId::of(methods::ITER_NTH_ZERO), LintId::of(methods::ITER_NTH_ZERO),
LintId::of(methods::ITER_OVEREAGER_CLONED), LintId::of(methods::ITER_OVEREAGER_CLONED),
LintId::of(methods::ITER_SKIP_NEXT), LintId::of(methods::ITER_SKIP_NEXT),
LintId::of(methods::ITER_WITH_DRAIN),
LintId::of(methods::MANUAL_FILTER_MAP), LintId::of(methods::MANUAL_FILTER_MAP),
LintId::of(methods::MANUAL_FIND_MAP), LintId::of(methods::MANUAL_FIND_MAP),
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
...@@ -189,6 +193,7 @@ ...@@ -189,6 +193,7 @@
LintId::of(methods::SUSPICIOUS_SPLITN), LintId::of(methods::SUSPICIOUS_SPLITN),
LintId::of(methods::UNINIT_ASSUMED_INIT), LintId::of(methods::UNINIT_ASSUMED_INIT),
LintId::of(methods::UNNECESSARY_FILTER_MAP), LintId::of(methods::UNNECESSARY_FILTER_MAP),
LintId::of(methods::UNNECESSARY_FIND_MAP),
LintId::of(methods::UNNECESSARY_FOLD), LintId::of(methods::UNNECESSARY_FOLD),
LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
LintId::of(methods::UNNECESSARY_TO_OWNED), LintId::of(methods::UNNECESSARY_TO_OWNED),
...@@ -231,6 +236,7 @@ ...@@ -231,6 +236,7 @@
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(octal_escapes::OCTAL_ESCAPES), LintId::of(octal_escapes::OCTAL_ESCAPES),
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
LintId::of(matches::MATCH_AS_REF), LintId::of(matches::MATCH_AS_REF),
LintId::of(matches::MATCH_SINGLE_BINDING), LintId::of(matches::MATCH_SINGLE_BINDING),
LintId::of(matches::NEEDLESS_MATCH),
LintId::of(matches::WILDCARD_IN_OR_PATTERNS), LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
LintId::of(methods::BIND_INSTEAD_OF_MAP), LintId::of(methods::BIND_INSTEAD_OF_MAP),
LintId::of(methods::CLONE_ON_COPY), LintId::of(methods::CLONE_ON_COPY),
...@@ -49,6 +50,7 @@ ...@@ -49,6 +50,7 @@
LintId::of(methods::SEARCH_IS_SOME), LintId::of(methods::SEARCH_IS_SOME),
LintId::of(methods::SKIP_WHILE_NEXT), LintId::of(methods::SKIP_WHILE_NEXT),
LintId::of(methods::UNNECESSARY_FILTER_MAP), LintId::of(methods::UNNECESSARY_FILTER_MAP),
LintId::of(methods::UNNECESSARY_FIND_MAP),
LintId::of(methods::USELESS_ASREF), LintId::of(methods::USELESS_ASREF),
LintId::of(misc::SHORT_CIRCUIT_STATEMENT), LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
...@@ -63,6 +65,7 @@ ...@@ -63,6 +65,7 @@
LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
LintId::of(no_effect::NO_EFFECT), LintId::of(no_effect::NO_EFFECT),
LintId::of(no_effect::UNNECESSARY_OPERATION), LintId::of(no_effect::UNNECESSARY_OPERATION),
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
LintId::of(precedence::PRECEDENCE), LintId::of(precedence::PRECEDENCE),
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
LintId::of(bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
LintId::of(booleans::LOGIC_BUG), LintId::of(booleans::LOGIC_BUG),
LintId::of(casts::CAST_REF_TO_MUT), LintId::of(casts::CAST_REF_TO_MUT),
LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
LintId::of(copies::IFS_SAME_COND), LintId::of(copies::IFS_SAME_COND),
LintId::of(copies::IF_SAME_THEN_ELSE), LintId::of(copies::IF_SAME_THEN_ELSE),
LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_HASH_XOR_EQ),
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS), LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS),
LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM), LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE), LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE),
LintId::of(utils::internal_lints::MISSING_MSRV_ATTR_IMPL),
LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA), LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA),
LintId::of(utils::internal_lints::PRODUCE_ICE), LintId::of(utils::internal_lints::PRODUCE_ICE),
LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR), LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR),
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE, utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
utils::internal_lints::MISSING_MSRV_ATTR_IMPL,
#[cfg(feature = "internal")]
utils::internal_lints::OUTER_EXPN_EXPN_DATA, utils::internal_lints::OUTER_EXPN_EXPN_DATA,
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
utils::internal_lints::PRODUCE_ICE, utils::internal_lints::PRODUCE_ICE,
...@@ -42,6 +44,7 @@ ...@@ -42,6 +44,7 @@
assign_ops::ASSIGN_OP_PATTERN, assign_ops::ASSIGN_OP_PATTERN,
assign_ops::MISREFACTORED_ASSIGN_OP, assign_ops::MISREFACTORED_ASSIGN_OP,
async_yields_async::ASYNC_YIELDS_ASYNC, async_yields_async::ASYNC_YIELDS_ASYNC,
attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON,
attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
attrs::DEPRECATED_CFG_ATTR, attrs::DEPRECATED_CFG_ATTR,
attrs::DEPRECATED_SEMVER, attrs::DEPRECATED_SEMVER,
...@@ -75,6 +78,7 @@ ...@@ -75,6 +78,7 @@
casts::CAST_PTR_ALIGNMENT, casts::CAST_PTR_ALIGNMENT,
casts::CAST_REF_TO_MUT, casts::CAST_REF_TO_MUT,
casts::CAST_SIGN_LOSS, casts::CAST_SIGN_LOSS,
casts::CAST_SLICE_DIFFERENT_SIZES,
casts::CHAR_LIT_AS_U8, casts::CHAR_LIT_AS_U8,
casts::FN_TO_NUMERIC_CAST, casts::FN_TO_NUMERIC_CAST,
casts::FN_TO_NUMERIC_CAST_ANY, casts::FN_TO_NUMERIC_CAST_ANY,
...@@ -219,6 +223,7 @@ ...@@ -219,6 +223,7 @@
loops::ITER_NEXT_LOOP, loops::ITER_NEXT_LOOP,
loops::MANUAL_FLATTEN, loops::MANUAL_FLATTEN,
loops::MANUAL_MEMCPY, loops::MANUAL_MEMCPY,
loops::MISSING_SPIN_LOOP,
loops::MUT_RANGE_BOUND, loops::MUT_RANGE_BOUND,
loops::NEEDLESS_COLLECT, loops::NEEDLESS_COLLECT,
loops::NEEDLESS_RANGE_LOOP, loops::NEEDLESS_RANGE_LOOP,
...@@ -255,6 +260,7 @@ ...@@ -255,6 +260,7 @@
matches::MATCH_SINGLE_BINDING, matches::MATCH_SINGLE_BINDING,
matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
matches::MATCH_WILD_ERR_ARM, matches::MATCH_WILD_ERR_ARM,
matches::NEEDLESS_MATCH,
matches::REDUNDANT_PATTERN_MATCHING, matches::REDUNDANT_PATTERN_MATCHING,
matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
matches::SINGLE_MATCH, matches::SINGLE_MATCH,
...@@ -296,6 +302,7 @@ ...@@ -296,6 +302,7 @@
methods::ITER_NTH_ZERO, methods::ITER_NTH_ZERO,
methods::ITER_OVEREAGER_CLONED, methods::ITER_OVEREAGER_CLONED,
methods::ITER_SKIP_NEXT, methods::ITER_SKIP_NEXT,
methods::ITER_WITH_DRAIN,
methods::MANUAL_FILTER_MAP, methods::MANUAL_FILTER_MAP,
methods::MANUAL_FIND_MAP, methods::MANUAL_FIND_MAP,
methods::MANUAL_SATURATING_ARITHMETIC, methods::MANUAL_SATURATING_ARITHMETIC,
...@@ -323,6 +330,7 @@ ...@@ -323,6 +330,7 @@
methods::SUSPICIOUS_SPLITN, methods::SUSPICIOUS_SPLITN,
methods::UNINIT_ASSUMED_INIT, methods::UNINIT_ASSUMED_INIT,
methods::UNNECESSARY_FILTER_MAP, methods::UNNECESSARY_FILTER_MAP,
methods::UNNECESSARY_FIND_MAP,
methods::UNNECESSARY_FOLD, methods::UNNECESSARY_FOLD,
methods::UNNECESSARY_LAZY_EVALUATIONS, methods::UNNECESSARY_LAZY_EVALUATIONS,
methods::UNNECESSARY_TO_OWNED, methods::UNNECESSARY_TO_OWNED,
...@@ -392,6 +400,7 @@ ...@@ -392,6 +400,7 @@
non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY, non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY,
nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES, nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
octal_escapes::OCTAL_ESCAPES, octal_escapes::OCTAL_ESCAPES,
only_used_in_recursion::ONLY_USED_IN_RECURSION,
open_options::NONSENSICAL_OPEN_OPTIONS, open_options::NONSENSICAL_OPEN_OPTIONS,
option_env_unwrap::OPTION_ENV_UNWRAP, option_env_unwrap::OPTION_ENV_UNWRAP,
option_if_let_else::OPTION_IF_LET_ELSE, option_if_let_else::OPTION_IF_LET_ELSE,
......
...@@ -10,11 +10,13 @@ ...@@ -10,11 +10,13 @@
LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
LintId::of(loops::MANUAL_MEMCPY), LintId::of(loops::MANUAL_MEMCPY),
LintId::of(loops::MISSING_SPIN_LOOP),
LintId::of(loops::NEEDLESS_COLLECT), LintId::of(loops::NEEDLESS_COLLECT),
LintId::of(methods::EXPECT_FUN_CALL), LintId::of(methods::EXPECT_FUN_CALL),
LintId::of(methods::EXTEND_WITH_DRAIN), LintId::of(methods::EXTEND_WITH_DRAIN),
LintId::of(methods::ITER_NTH), LintId::of(methods::ITER_NTH),
LintId::of(methods::ITER_OVEREAGER_CLONED), LintId::of(methods::ITER_OVEREAGER_CLONED),
LintId::of(methods::ITER_WITH_DRAIN),
LintId::of(methods::MANUAL_STR_REPEAT), LintId::of(methods::MANUAL_STR_REPEAT),
LintId::of(methods::OR_FUN_CALL), LintId::of(methods::OR_FUN_CALL),
LintId::of(methods::SINGLE_CHAR_PATTERN), LintId::of(methods::SINGLE_CHAR_PATTERN),
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
LintId::of(as_conversions::AS_CONVERSIONS), LintId::of(as_conversions::AS_CONVERSIONS),
LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
LintId::of(casts::FN_TO_NUMERIC_CAST_ANY), LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
LintId::of(create_dir::CREATE_DIR), LintId::of(create_dir::CREATE_DIR),
LintId::of(dbg_macro::DBG_MACRO), LintId::of(dbg_macro::DBG_MACRO),
......
...@@ -318,6 +318,7 @@ ...@@ -318,6 +318,7 @@
mod non_send_fields_in_send_ty; mod non_send_fields_in_send_ty;
mod nonstandard_macro_braces; mod nonstandard_macro_braces;
mod octal_escapes; mod octal_escapes;
mod only_used_in_recursion;
mod open_options; mod open_options;
mod option_env_unwrap; mod option_env_unwrap;
mod option_if_let_else; mod option_if_let_else;
...@@ -505,6 +506,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ...@@ -505,6 +506,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(utils::internal_lints::LintWithoutLintPass::default())); store.register_late_pass(|| Box::new(utils::internal_lints::LintWithoutLintPass::default()));
store.register_late_pass(|| Box::new(utils::internal_lints::MatchTypeOnDiagItem)); store.register_late_pass(|| Box::new(utils::internal_lints::MatchTypeOnDiagItem));
store.register_late_pass(|| Box::new(utils::internal_lints::OuterExpnDataPass)); store.register_late_pass(|| Box::new(utils::internal_lints::OuterExpnDataPass));
store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl));
} }
store.register_late_pass(|| Box::new(utils::author::Author)); store.register_late_pass(|| Box::new(utils::author::Author));
...@@ -856,6 +858,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ...@@ -856,6 +858,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv))); store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv))); store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
store.register_late_pass(|| Box::new(dbg_macro::DbgMacro)); store.register_late_pass(|| Box::new(dbg_macro::DbgMacro));
let cargo_ignore_publish = conf.cargo_ignore_publish; let cargo_ignore_publish = conf.cargo_ignore_publish;
store.register_late_pass(move || { store.register_late_pass(move || {
......
use super::MISSING_SPIN_LOOP;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_no_std_crate;
use rustc_errors::Applicability;
use rustc_hir::{Block, Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::sym;
fn unpack_cond<'tcx>(cond: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
match &cond.kind {
ExprKind::Block(
Block {
stmts: [],
expr: Some(e),
..
},
_,
)
| ExprKind::Unary(_, e) => unpack_cond(e),
ExprKind::Binary(_, l, r) => {
let l = unpack_cond(l);
if let ExprKind::MethodCall(..) = l.kind {
l
} else {
unpack_cond(r)
}
},
_ => cond,
}
}
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &'tcx Expr<'_>) {
if_chain! {
if let ExprKind::Block(Block { stmts: [], expr: None, ..}, _) = body.kind;
if let ExprKind::MethodCall(method, [callee, ..], _) = unpack_cond(cond).kind;
if [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name);
if let ty::Adt(def, _substs) = cx.typeck_results().expr_ty(callee).kind();
if cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did());
then {
span_lint_and_sugg(
cx,
MISSING_SPIN_LOOP,
body.span,
"busy-waiting loop should at least have a spin loop hint",
"try this",
(if is_no_std_crate(cx) {
"{ core::hint::spin_loop() }"
} else {
"{ std::hint::spin_loop() }"
}).into(),
Applicability::MachineApplicable
);
}
}
}
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
mod iter_next_loop; mod iter_next_loop;
mod manual_flatten; mod manual_flatten;
mod manual_memcpy; mod manual_memcpy;
mod missing_spin_loop;
mod mut_range_bound; mod mut_range_bound;
mod needless_collect; mod needless_collect;
mod needless_range_loop; mod needless_range_loop;
...@@ -560,6 +561,42 @@ ...@@ -560,6 +561,42 @@
"for loops over `Option`s or `Result`s with a single expression can be simplified" "for loops over `Option`s or `Result`s with a single expression can be simplified"
} }
declare_clippy_lint! {
/// ### What it does
/// Check for empty spin loops
///
/// ### Why is this bad?
/// The loop body should have something like `thread::park()` or at least
/// `std::hint::spin_loop()` to avoid needlessly burning cycles and conserve
/// energy. Perhaps even better use an actual lock, if possible.
///
/// ### Known problems
/// This lint doesn't currently trigger on `while let` or
/// `loop { match .. { .. } }` loops, which would be considered idiomatic in
/// combination with e.g. `AtomicBool::compare_exchange_weak`.
///
/// ### Example
///
/// ```ignore
/// use core::sync::atomic::{AtomicBool, Ordering};
/// let b = AtomicBool::new(true);
/// // give a ref to `b` to another thread,wait for it to become false
/// while b.load(Ordering::Acquire) {};
/// ```
/// Use instead:
/// ```rust,no_run
///# use core::sync::atomic::{AtomicBool, Ordering};
///# let b = AtomicBool::new(true);
/// while b.load(Ordering::Acquire) {
/// std::hint::spin_loop()
/// }
/// ```
#[clippy::version = "1.59.0"]
pub MISSING_SPIN_LOOP,
perf,
"An empty busy waiting loop"
}
declare_lint_pass!(Loops => [ declare_lint_pass!(Loops => [
MANUAL_MEMCPY, MANUAL_MEMCPY,
MANUAL_FLATTEN, MANUAL_FLATTEN,
...@@ -579,6 +616,7 @@ ...@@ -579,6 +616,7 @@
WHILE_IMMUTABLE_CONDITION, WHILE_IMMUTABLE_CONDITION,
SAME_ITEM_PUSH, SAME_ITEM_PUSH,
SINGLE_ELEMENT_LOOP, SINGLE_ELEMENT_LOOP,
MISSING_SPIN_LOOP,
]); ]);
impl<'tcx> LateLintPass<'tcx> for Loops { impl<'tcx> LateLintPass<'tcx> for Loops {
...@@ -628,6 +666,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { ...@@ -628,6 +666,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let Some(higher::While { condition, body }) = higher::While::hir(expr) { if let Some(higher::While { condition, body }) = higher::While::hir(expr) {
while_immutable_condition::check(cx, condition, body); while_immutable_condition::check(cx, condition, body);
missing_spin_loop::check(cx, condition, body);
} }
needless_collect::check(expr, cx); needless_collect::check(expr, cx);
......
...@@ -35,7 +35,8 @@ struct PathAndSpan { ...@@ -35,7 +35,8 @@ struct PathAndSpan {
span: Span, span: Span,
} }
/// `MacroRefData` includes the name of the macro. /// `MacroRefData` includes the name of the macro
/// and the path from `SourceMap::span_to_filename`.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct MacroRefData { pub struct MacroRefData {
name: String, name: String,
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
mod match_single_binding; mod match_single_binding;
mod match_wild_enum; mod match_wild_enum;
mod match_wild_err_arm; mod match_wild_err_arm;
mod needless_match;
mod overlapping_arms; mod overlapping_arms;
mod redundant_pattern_match; mod redundant_pattern_match;
mod rest_pat_in_fully_bound_struct; mod rest_pat_in_fully_bound_struct;
...@@ -566,6 +567,49 @@ ...@@ -566,6 +567,49 @@
"`match` with identical arm bodies" "`match` with identical arm bodies"
} }
declare_clippy_lint! {
/// ### What it does
/// Checks for unnecessary `match` or match-like `if let` returns for `Option` and `Result`
/// when function signatures are the same.
///
/// ### Why is this bad?
/// This `match` block does nothing and might not be what the coder intended.
///
/// ### Example
/// ```rust,ignore
/// fn foo() -> Result<(), i32> {
/// match result {
/// Ok(val) => Ok(val),
/// Err(err) => Err(err),
/// }
/// }
///
/// fn bar() -> Option<i32> {
/// if let Some(val) = option {
/// Some(val)
/// } else {
/// None
/// }
/// }
/// ```
///
/// Could be replaced as
///
/// ```rust,ignore
/// fn foo() -> Result<(), i32> {
/// result
/// }
///
/// fn bar() -> Option<i32> {
/// option
/// }
/// ```
#[clippy::version = "1.61.0"]
pub NEEDLESS_MATCH,
complexity,
"`match` or match-like `if let` that are unnecessary"
}
#[derive(Default)] #[derive(Default)]
pub struct Matches { pub struct Matches {
msrv: Option<RustcVersion>, msrv: Option<RustcVersion>,
...@@ -599,6 +643,7 @@ pub fn new(msrv: Option<RustcVersion>) -> Self { ...@@ -599,6 +643,7 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
REDUNDANT_PATTERN_MATCHING, REDUNDANT_PATTERN_MATCHING,
MATCH_LIKE_MATCHES_MACRO, MATCH_LIKE_MATCHES_MACRO,
MATCH_SAME_ARMS, MATCH_SAME_ARMS,
NEEDLESS_MATCH,
]); ]);
impl<'tcx> LateLintPass<'tcx> for Matches { impl<'tcx> LateLintPass<'tcx> for Matches {
...@@ -622,6 +667,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { ...@@ -622,6 +667,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
overlapping_arms::check(cx, ex, arms); overlapping_arms::check(cx, ex, arms);
match_wild_enum::check(cx, ex, arms); match_wild_enum::check(cx, ex, arms);
match_as_ref::check(cx, ex, arms, expr); match_as_ref::check(cx, ex, arms, expr);
needless_match::check_match(cx, ex, arms);
if self.infallible_destructuring_match_linted { if self.infallible_destructuring_match_linted {
self.infallible_destructuring_match_linted = false; self.infallible_destructuring_match_linted = false;
...@@ -640,6 +686,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { ...@@ -640,6 +686,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
match_like_matches::check(cx, expr); match_like_matches::check(cx, expr);
} }
redundant_pattern_match::check(cx, expr); redundant_pattern_match::check(cx, expr);
needless_match::check(cx, expr);
} }
} }
......
use super::NEEDLESS_MATCH;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{eq_expr_value, get_parent_expr, higher, is_else_clause, is_lang_ctor, peel_blocks_with_stmt};
use rustc_errors::Applicability;
use rustc_hir::LangItem::OptionNone;
use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, Pat, PatKind, Path, PathSegment, QPath, UnOp};
use rustc_lint::LateContext;
use rustc_span::sym;
pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
// This is for avoiding collision with `match_single_binding`.
if arms.len() < 2 {
return;
}
for arm in arms {
if let PatKind::Wild = arm.pat.kind {
let ret_expr = strip_return(arm.body);
if !eq_expr_value(cx, ex, ret_expr) {
return;
}
} else if !pat_same_as_expr(arm.pat, arm.body) {
return;
}
}
if let Some(match_expr) = get_parent_expr(cx, ex) {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
NEEDLESS_MATCH,
match_expr.span,
"this match expression is unnecessary",
"replace it with",
snippet_with_applicability(cx, ex.span, "..", &mut applicability).to_string(),
applicability,
);
}
}
/// Check for nop `if let` expression that assembled as unnecessary match
///
/// ```rust,ignore
/// if let Some(a) = option {
/// Some(a)
/// } else {
/// None
/// }
/// ```
/// OR
/// ```rust,ignore
/// if let SomeEnum::A = some_enum {
/// SomeEnum::A
/// } else if let SomeEnum::B = some_enum {
/// SomeEnum::B
/// } else {
/// some_enum
/// }
/// ```
pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) {
if_chain! {
if let Some(ref if_let) = higher::IfLet::hir(cx, ex);
if !is_else_clause(cx.tcx, ex);
if check_if_let(cx, if_let);
then {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
NEEDLESS_MATCH,
ex.span,
"this if-let expression is unnecessary",
"replace it with",
snippet_with_applicability(cx, if_let.let_expr.span, "..", &mut applicability).to_string(),
applicability,
);
}
}
}
fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
if let Some(if_else) = if_let.if_else {
if !pat_same_as_expr(if_let.let_pat, peel_blocks_with_stmt(if_let.if_then)) {
return false;
}
// Recurrsively check for each `else if let` phrase,
if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) {
return check_if_let(cx, nested_if_let);
}
if matches!(if_else.kind, ExprKind::Block(..)) {
let else_expr = peel_blocks_with_stmt(if_else);
let ret = strip_return(else_expr);
let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr);
if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) {
if let ExprKind::Path(ref qpath) = ret.kind {
return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret);
}
} else {
return eq_expr_value(cx, if_let.let_expr, ret);
}
return true;
}
}
false
}
/// Strip `return` keyword if the expression type is `ExprKind::Ret`.
fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
if let ExprKind::Ret(Some(ret)) = expr.kind {
ret
} else {
expr
}
}
fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
let expr = strip_return(expr);
match (&pat.kind, &expr.kind) {
// Example: `Some(val) => Some(val)`
(
PatKind::TupleStruct(QPath::Resolved(_, path), [first_pat, ..], _),
ExprKind::Call(call_expr, [first_param, ..]),
) => {
if let ExprKind::Path(QPath::Resolved(_, call_path)) = call_expr.kind {
if has_identical_segments(path.segments, call_path.segments)
&& has_same_non_ref_symbol(first_pat, first_param)
{
return true;
}
}
},
// Example: `val => val`, or `ref val => *val`
(PatKind::Binding(annot, _, pat_ident, _), _) => {
let new_expr = if let (
BindingAnnotation::Ref | BindingAnnotation::RefMut,
ExprKind::Unary(UnOp::Deref, operand_expr),
) = (annot, &expr.kind)
{
operand_expr
} else {
expr
};
if let ExprKind::Path(QPath::Resolved(
_,
Path {
segments: [first_seg, ..],
..
},
)) = new_expr.kind
{
return pat_ident.name == first_seg.ident.name;
}
},
// Example: `Custom::TypeA => Custom::TypeB`, or `None => None`
(PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => {
return has_identical_segments(p_path.segments, e_path.segments);
},
// Example: `5 => 5`
(PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => {
if let ExprKind::Lit(pat_spanned) = &pat_lit_expr.kind {
return pat_spanned.node == expr_spanned.node;
}
},
_ => {},
}
false
}
fn has_identical_segments(left_segs: &[PathSegment<'_>], right_segs: &[PathSegment<'_>]) -> bool {
if left_segs.len() != right_segs.len() {
return false;
}
for i in 0..left_segs.len() {
if left_segs[i].ident.name != right_segs[i].ident.name {
return false;
}
}
true
}
fn has_same_non_ref_symbol(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
if_chain! {
if let PatKind::Binding(annot, _, pat_ident, _) = pat.kind;
if !matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
if let ExprKind::Path(QPath::Resolved(_, Path {segments: [first_seg, ..], .. })) = expr.kind;
then {
return pat_ident.name == first_seg.ident.name;
}
}
false
}
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_integer_const;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{
higher::{self, Range},
SpanlessEq,
};
use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::LateContext;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use super::ITER_WITH_DRAIN;
const DRAIN_TYPES: &[Symbol] = &[sym::Vec, sym::VecDeque];
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: &Expr<'_>) {
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
if let Some(drained_type) = DRAIN_TYPES.iter().find(|&&sym| is_type_diagnostic_item(cx, ty, sym)) {
// Refuse to emit `into_iter` suggestion on draining struct fields due
// to the strong possibility of processing unmovable field.
if let ExprKind::Field(..) = recv.kind {
return;
}
if let Some(range) = higher::Range::hir(arg) {
let left_full = match range {
Range { start: Some(start), .. } if is_integer_const(cx, start, 0) => true,
Range { start: None, .. } => true,
_ => false,
};
let full = left_full
&& match range {
Range {
end: Some(end),
limits: RangeLimits::HalfOpen,
..
} => {
// `x.drain(..x.len())` call
if_chain! {
if let ExprKind::MethodCall(len_path, len_args, _) = end.kind;
if len_path.ident.name == sym::len && len_args.len() == 1;
if let ExprKind::Path(QPath::Resolved(_, drain_path)) = recv.kind;
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;
if SpanlessEq::new(cx).eq_path(drain_path, len_path);
then { true }
else { false }
}
},
Range {
end: None,
limits: RangeLimits::HalfOpen,
..
} => true,
_ => false,
};
if full {
span_lint_and_sugg(
cx,
ITER_WITH_DRAIN,
span.with_hi(expr.span.hi()),
&format!("`drain(..)` used on a `{}`", drained_type),
"try this",
"into_iter()".to_string(),
Applicability::MaybeIncorrect,
);
}
}
}
}
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
mod iter_nth_zero; mod iter_nth_zero;
mod iter_overeager_cloned; mod iter_overeager_cloned;
mod iter_skip_next; mod iter_skip_next;
mod iter_with_drain;
mod iterator_step_by_zero; mod iterator_step_by_zero;
mod manual_saturating_arithmetic; mod manual_saturating_arithmetic;
mod manual_str_repeat; mod manual_str_repeat;
...@@ -1118,6 +1119,31 @@ ...@@ -1118,6 +1119,31 @@
"using `.skip(x).next()` on an iterator" "using `.skip(x).next()` on an iterator"
} }
declare_clippy_lint! {
/// ### What it does
/// Checks for use of `.drain(..)` on `Vec` and `VecDeque` for iteration.
///
/// ### Why is this bad?
/// `.into_iter()` is simpler with better performance.
///
/// ### Example
/// ```rust
/// # use std::collections::HashSet;
/// let mut foo = vec![0, 1, 2, 3];
/// let bar: HashSet<usize> = foo.drain(..).collect();
/// ```
/// Use instead:
/// ```rust
/// # use std::collections::HashSet;
/// let foo = vec![0, 1, 2, 3];
/// let bar: HashSet<usize> = foo.into_iter().collect();
/// ```
#[clippy::version = "1.61.0"]
pub ITER_WITH_DRAIN,
perf,
"replace `.drain(..)` with `.into_iter()`"
}
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for use of `.get().unwrap()` (or /// Checks for use of `.get().unwrap()` (or
...@@ -1309,7 +1335,7 @@ ...@@ -1309,7 +1335,7 @@
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for `filter_map` calls which could be replaced by `filter` or `map`. /// Checks for `filter_map` calls that could be replaced by `filter` or `map`.
/// More specifically it checks if the closure provided is only performing one of the /// More specifically it checks if the closure provided is only performing one of the
/// filter or map operations and suggests the appropriate option. /// filter or map operations and suggests the appropriate option.
/// ///
...@@ -1337,6 +1363,36 @@ ...@@ -1337,6 +1363,36 @@
"using `filter_map` when a more succinct alternative exists" "using `filter_map` when a more succinct alternative exists"
} }
declare_clippy_lint! {
/// ### What it does
/// Checks for `find_map` calls that could be replaced by `find` or `map`. More
/// specifically it checks if the closure provided is only performing one of the
/// find or map operations and suggests the appropriate option.
///
/// ### Why is this bad?
/// Complexity. The intent is also clearer if only a single
/// operation is being performed.
///
/// ### Example
/// ```rust
/// let _ = (0..3).find_map(|x| if x > 2 { Some(x) } else { None });
///
/// // As there is no transformation of the argument this could be written as:
/// let _ = (0..3).find(|&x| x > 2);
/// ```
///
/// ```rust
/// let _ = (0..4).find_map(|x| Some(x + 1));
///
/// // As there is no conditional check on the argument this could be written as:
/// let _ = (0..4).map(|x| x + 1).next();
/// ```
#[clippy::version = "1.61.0"]
pub UNNECESSARY_FIND_MAP,
complexity,
"using `find_map` when a more succinct alternative exists"
}
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for `into_iter` calls on references which should be replaced by `iter` /// Checks for `into_iter` calls on references which should be replaced by `iter`
...@@ -2017,9 +2073,11 @@ pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Sel ...@@ -2017,9 +2073,11 @@ pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Sel
GET_UNWRAP, GET_UNWRAP,
STRING_EXTEND_CHARS, STRING_EXTEND_CHARS,
ITER_CLONED_COLLECT, ITER_CLONED_COLLECT,
ITER_WITH_DRAIN,
USELESS_ASREF, USELESS_ASREF,
UNNECESSARY_FOLD, UNNECESSARY_FOLD,
UNNECESSARY_FILTER_MAP, UNNECESSARY_FILTER_MAP,
UNNECESSARY_FIND_MAP,
INTO_ITER_ON_REF, INTO_ITER_ON_REF,
SUSPICIOUS_MAP, SUSPICIOUS_MAP,
UNINIT_ASSUMED_INIT, UNINIT_ASSUMED_INIT,
...@@ -2296,6 +2354,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ...@@ -2296,6 +2354,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg), Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
_ => {}, _ => {},
}, },
("drain", [arg]) => {
iter_with_drain::check(cx, expr, recv, span, arg);
},
("expect", [_]) => match method_call(recv) { ("expect", [_]) => match method_call(recv) {
Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
_ => expect_used::check(cx, expr, recv), _ => expect_used::check(cx, expr, recv),
...@@ -2305,9 +2366,12 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ...@@ -2305,9 +2366,12 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
extend_with_drain::check(cx, expr, recv, arg); extend_with_drain::check(cx, expr, recv, arg);
}, },
("filter_map", [arg]) => { ("filter_map", [arg]) => {
unnecessary_filter_map::check(cx, expr, arg); unnecessary_filter_map::check(cx, expr, arg, name);
filter_map_identity::check(cx, expr, arg, span); filter_map_identity::check(cx, expr, arg, span);
}, },
("find_map", [arg]) => {
unnecessary_filter_map::check(cx, expr, arg, name);
},
("flat_map", [arg]) => { ("flat_map", [arg]) => {
flat_map_identity::check(cx, expr, arg, span); flat_map_identity::check(cx, expr, arg, span);
flat_map_option::check(cx, expr, arg, span); flat_map_option::check(cx, expr, arg, span);
......
...@@ -14,10 +14,7 @@ ...@@ -14,10 +14,7 @@
// The expression inside a closure may or may not have surrounding braces // The expression inside a closure may or may not have surrounding braces
// which causes problems when generating a suggestion. // which causes problems when generating a suggestion.
fn reduce_unit_expression<'a>( fn reduce_unit_expression<'a>(expr: &'a hir::Expr<'_>) -> Option<(&'a hir::Expr<'a>, &'a [hir::Expr<'a>])> {
cx: &LateContext<'_>,
expr: &'a hir::Expr<'_>,
) -> Option<(&'a hir::Expr<'a>, &'a [hir::Expr<'a>])> {
match expr.kind { match expr.kind {
hir::ExprKind::Call(func, arg_char) => Some((func, arg_char)), hir::ExprKind::Call(func, arg_char) => Some((func, arg_char)),
hir::ExprKind::Block(block, _) => { hir::ExprKind::Block(block, _) => {
...@@ -25,7 +22,7 @@ fn reduce_unit_expression<'a>( ...@@ -25,7 +22,7 @@ fn reduce_unit_expression<'a>(
(&[], Some(inner_expr)) => { (&[], Some(inner_expr)) => {
// If block only contains an expression, // If block only contains an expression,
// reduce `|x| { x + 1 }` to `|x| x + 1` // reduce `|x| { x + 1 }` to `|x| x + 1`
reduce_unit_expression(cx, inner_expr) reduce_unit_expression(inner_expr)
}, },
_ => None, _ => None,
} }
...@@ -77,7 +74,7 @@ pub(super) fn check<'tcx>( ...@@ -77,7 +74,7 @@ pub(super) fn check<'tcx>(
if let hir::ExprKind::Closure(_, _, id, span, _) = map_arg.kind; if let hir::ExprKind::Closure(_, _, id, span, _) = map_arg.kind;
let arg_snippet = snippet(cx, span, ".."); let arg_snippet = snippet(cx, span, "..");
let body = cx.tcx.hir().body(id); let body = cx.tcx.hir().body(id);
if let Some((func, [arg_char])) = reduce_unit_expression(cx, &body.value); if let Some((func, [arg_char])) = reduce_unit_expression(&body.value);
if let Some(id) = path_def_id(cx, func).and_then(|ctor_id| cx.tcx.parent(ctor_id)); if let Some(id) = path_def_id(cx, func).and_then(|ctor_id| cx.tcx.parent(ctor_id));
if Some(id) == cx.tcx.lang_items().option_some_variant(); if Some(id) == cx.tcx.lang_items().option_some_variant();
then { then {
......
use super::utils::clone_or_copy_needed;
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::is_copy;
use clippy_utils::usage::mutated_variables; use clippy_utils::usage::mutated_variables;
use clippy_utils::{is_lang_ctor, is_trait_method, path_to_local_id}; use clippy_utils::{is_lang_ctor, is_trait_method, path_to_local_id};
use rustc_hir as hir; use rustc_hir as hir;
...@@ -9,8 +11,9 @@ ...@@ -9,8 +11,9 @@
use rustc_span::sym; use rustc_span::sym;
use super::UNNECESSARY_FILTER_MAP; use super::UNNECESSARY_FILTER_MAP;
use super::UNNECESSARY_FIND_MAP;
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) { pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, name: &str) {
if !is_trait_method(cx, expr, sym::Iterator) { if !is_trait_method(cx, expr, sym::Iterator) {
return; return;
} }
...@@ -20,6 +23,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< ...@@ -20,6 +23,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<
let arg_id = body.params[0].pat.hir_id; let arg_id = body.params[0].pat.hir_id;
let mutates_arg = let mutates_arg =
mutated_variables(&body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id)); mutated_variables(&body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id));
let (clone_or_copy_needed, _) = clone_or_copy_needed(cx, body.params[0].pat, &body.value);
let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, &body.value); let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, &body.value);
...@@ -28,13 +32,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< ...@@ -28,13 +32,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<
found_mapping |= return_visitor.found_mapping; found_mapping |= return_visitor.found_mapping;
found_filtering |= return_visitor.found_filtering; found_filtering |= return_visitor.found_filtering;
let in_ty = cx.typeck_results().node_type(body.params[0].hir_id);
let sugg = if !found_filtering { let sugg = if !found_filtering {
"map" if name == "filter_map" { "map" } else { "map(..).next()" }
} else if !found_mapping && !mutates_arg { } else if !found_mapping && !mutates_arg && (!clone_or_copy_needed || is_copy(cx, in_ty)) {
let in_ty = cx.typeck_results().node_type(body.params[0].hir_id);
match cx.typeck_results().expr_ty(&body.value).kind() { match cx.typeck_results().expr_ty(&body.value).kind() {
ty::Adt(adt, subst) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && in_ty == subst.type_at(0) => { ty::Adt(adt, subst)
"filter" if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && in_ty == subst.type_at(0) =>
{
if name == "filter_map" { "filter" } else { "find" }
}, },
_ => return, _ => return,
} }
...@@ -43,9 +49,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< ...@@ -43,9 +49,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<
}; };
span_lint( span_lint(
cx, cx,
UNNECESSARY_FILTER_MAP, if name == "filter_map" {
UNNECESSARY_FILTER_MAP
} else {
UNNECESSARY_FIND_MAP
},
expr.span, expr.span,
&format!("this `.filter_map` can be written more simply using `.{}`", sugg), &format!("this `.{}` can be written more simply using `.{}`", name, sugg),
); );
} }
} }
......
use super::utils::clone_or_copy_needed;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::ForLoop; use clippy_utils::higher::ForLoop;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait}; use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait};
use clippy_utils::{fn_def_id, get_parent_expr, path_to_local_id, usage}; use clippy_utils::{fn_def_id, get_parent_expr};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{def_id::DefId, Expr, ExprKind, LangItem};
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, HirId, LangItem, Mutability, Pat};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty;
use rustc_span::{sym, Symbol}; use rustc_span::{sym, Symbol};
use super::UNNECESSARY_TO_OWNED; use super::UNNECESSARY_TO_OWNED;
...@@ -100,89 +98,6 @@ pub fn check_for_loop_iter( ...@@ -100,89 +98,6 @@ pub fn check_for_loop_iter(
false false
} }
/// The core logic of `check_for_loop_iter` above, this function wraps a use of
/// `CloneOrCopyVisitor`.
fn clone_or_copy_needed<'tcx>(
cx: &LateContext<'tcx>,
pat: &Pat<'tcx>,
body: &'tcx Expr<'tcx>,
) -> (bool, Vec<&'tcx Expr<'tcx>>) {
let mut visitor = CloneOrCopyVisitor {
cx,
binding_hir_ids: pat_bindings(pat),
clone_or_copy_needed: false,
addr_of_exprs: Vec::new(),
};
visitor.visit_expr(body);
(visitor.clone_or_copy_needed, visitor.addr_of_exprs)
}
/// Returns a vector of all `HirId`s bound by the pattern.
fn pat_bindings(pat: &Pat<'_>) -> Vec<HirId> {
let mut collector = usage::ParamBindingIdCollector {
binding_hir_ids: Vec::new(),
};
collector.visit_pat(pat);
collector.binding_hir_ids
}
/// `clone_or_copy_needed` will be false when `CloneOrCopyVisitor` is done visiting if the only
/// operations performed on `binding_hir_ids` are:
/// * to take non-mutable references to them
/// * to use them as non-mutable `&self` in method calls
/// If any of `binding_hir_ids` is used in any other way, then `clone_or_copy_needed` will be true
/// when `CloneOrCopyVisitor` is done visiting.
struct CloneOrCopyVisitor<'cx, 'tcx> {
cx: &'cx LateContext<'tcx>,
binding_hir_ids: Vec<HirId>,
clone_or_copy_needed: bool,
addr_of_exprs: Vec<&'tcx Expr<'tcx>>,
}
impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn nested_visit_map(&mut self) -> Self::Map {
self.cx.tcx.hir()
}
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
walk_expr(self, expr);
if self.is_binding(expr) {
if let Some(parent) = get_parent_expr(self.cx, expr) {
match parent.kind {
ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) => {
self.addr_of_exprs.push(parent);
return;
},
ExprKind::MethodCall(_, args, _) => {
if_chain! {
if args.iter().skip(1).all(|arg| !self.is_binding(arg));
if let Some(method_def_id) = self.cx.typeck_results().type_dependent_def_id(parent.hir_id);
let method_ty = self.cx.tcx.type_of(method_def_id);
let self_ty = method_ty.fn_sig(self.cx.tcx).input(0).skip_binder();
if matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Not));
then {
return;
}
}
},
_ => {},
}
}
self.clone_or_copy_needed = true;
}
}
}
impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> {
fn is_binding(&self, expr: &Expr<'tcx>) -> bool {
self.binding_hir_ids
.iter()
.any(|hir_id| path_to_local_id(expr, *hir_id))
}
}
/// Returns true if the named method is `IntoIterator::into_iter`. /// Returns true if the named method is `IntoIterator::into_iter`.
pub fn is_into_iter(cx: &LateContext<'_>, callee_def_id: DefId) -> bool { pub fn is_into_iter(cx: &LateContext<'_>, callee_def_id: DefId) -> bool {
cx.tcx.lang_items().require(LangItem::IntoIterIntoIter) == Ok(callee_def_id) cx.tcx.lang_items().require(LangItem::IntoIterIntoIter) == Ok(callee_def_id)
......
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{get_parent_expr, path_to_local_id, usage};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast; use rustc_ast::ast;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{self, Ty};
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
...@@ -79,3 +83,86 @@ pub(super) fn get_hint_if_single_char_arg( ...@@ -79,3 +83,86 @@ pub(super) fn get_hint_if_single_char_arg(
} }
} }
} }
/// The core logic of `check_for_loop_iter` in `unnecessary_iter_cloned.rs`, this function wraps a
/// use of `CloneOrCopyVisitor`.
pub(super) fn clone_or_copy_needed<'tcx>(
cx: &LateContext<'tcx>,
pat: &Pat<'tcx>,
body: &'tcx Expr<'tcx>,
) -> (bool, Vec<&'tcx Expr<'tcx>>) {
let mut visitor = CloneOrCopyVisitor {
cx,
binding_hir_ids: pat_bindings(pat),
clone_or_copy_needed: false,
addr_of_exprs: Vec::new(),
};
visitor.visit_expr(body);
(visitor.clone_or_copy_needed, visitor.addr_of_exprs)
}
/// Returns a vector of all `HirId`s bound by the pattern.
fn pat_bindings(pat: &Pat<'_>) -> Vec<HirId> {
let mut collector = usage::ParamBindingIdCollector {
binding_hir_ids: Vec::new(),
};
collector.visit_pat(pat);
collector.binding_hir_ids
}
/// `clone_or_copy_needed` will be false when `CloneOrCopyVisitor` is done visiting if the only
/// operations performed on `binding_hir_ids` are:
/// * to take non-mutable references to them
/// * to use them as non-mutable `&self` in method calls
/// If any of `binding_hir_ids` is used in any other way, then `clone_or_copy_needed` will be true
/// when `CloneOrCopyVisitor` is done visiting.
struct CloneOrCopyVisitor<'cx, 'tcx> {
cx: &'cx LateContext<'tcx>,
binding_hir_ids: Vec<HirId>,
clone_or_copy_needed: bool,
addr_of_exprs: Vec<&'tcx Expr<'tcx>>,
}
impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn nested_visit_map(&mut self) -> Self::Map {
self.cx.tcx.hir()
}
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
walk_expr(self, expr);
if self.is_binding(expr) {
if let Some(parent) = get_parent_expr(self.cx, expr) {
match parent.kind {
ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) => {
self.addr_of_exprs.push(parent);
return;
},
ExprKind::MethodCall(_, args, _) => {
if_chain! {
if args.iter().skip(1).all(|arg| !self.is_binding(arg));
if let Some(method_def_id) = self.cx.typeck_results().type_dependent_def_id(parent.hir_id);
let method_ty = self.cx.tcx.type_of(method_def_id);
let self_ty = method_ty.fn_sig(self.cx.tcx).input(0).skip_binder();
if matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Not));
then {
return;
}
}
},
_ => {},
}
}
self.clone_or_copy_needed = true;
}
}
}
impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> {
fn is_binding(&self, expr: &Expr<'tcx>) -> bool {
self.binding_hir_ids
.iter()
.any(|hir_id| path_to_local_id(expr, *hir_id))
}
}
...@@ -7,7 +7,8 @@ ...@@ -7,7 +7,8 @@
use clippy_utils::attrs::is_doc_hidden; use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint; 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_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty; use rustc_middle::ty;
...@@ -57,6 +58,20 @@ fn doc_hidden(&self) -> bool { ...@@ -57,6 +58,20 @@ fn doc_hidden(&self) -> bool {
*self.doc_hidden_stack.last().expect("empty doc_hidden_stack") *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( fn check_missing_docs_attrs(
&self, &self,
cx: &LateContext<'_>, cx: &LateContext<'_>,
...@@ -80,7 +95,9 @@ fn check_missing_docs_attrs( ...@@ -80,7 +95,9 @@ fn check_missing_docs_attrs(
return; return;
} }
let has_doc = attrs.iter().any(|a| a.doc_str().is_some()); let has_doc = attrs
.iter()
.any(|a| a.doc_str().is_some() || Self::has_include(a.meta()));
if !has_doc { if !has_doc {
span_lint( span_lint(
cx, cx,
......
...@@ -92,10 +92,6 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { ...@@ -92,10 +92,6 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
self.found = true; self.found = true;
return; return;
}, },
ExprKind::If(..) => {
self.found = true;
return;
},
ExprKind::Path(_) => { ExprKind::Path(_) => {
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) { if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
if adj if adj
......
...@@ -199,7 +199,12 @@ fn check_fn( ...@@ -199,7 +199,12 @@ fn check_fn(
let sugg = |diag: &mut Diagnostic| { let sugg = |diag: &mut Diagnostic| {
if let ty::Adt(def, ..) = ty.kind() { if let ty::Adt(def, ..) = ty.kind() {
if let Some(span) = cx.tcx.hir().span_if_local(def.did()) { if let Some(span) = cx.tcx.hir().span_if_local(def.did()) {
if can_type_implement_copy(cx.tcx, cx.param_env, ty, traits::ObligationCause::dummy_with_span(span)).is_ok() { if can_type_implement_copy(
cx.tcx,
cx.param_env,
ty,
traits::ObligationCause::dummy_with_span(span),
).is_ok() {
diag.span_help(span, "consider marking this type as `Copy`"); diag.span_help(span, "consider marking this type as `Copy`");
} }
} }
......
此差异已折叠。
...@@ -123,7 +123,7 @@ fn check_if_let_some_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<' ...@@ -123,7 +123,7 @@ fn check_if_let_some_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'
} }
fn result_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool { fn result_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool {
Self::is_result(cx, expr) && Self::expression_returns_unmodified_err(cx, nested_expr, expr) Self::is_result(cx, expr) && Self::expression_returns_unmodified_err(nested_expr, expr)
} }
fn option_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool { fn option_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool {
...@@ -156,9 +156,9 @@ fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool ...@@ -156,9 +156,9 @@ fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool
} }
} }
fn expression_returns_unmodified_err(cx: &LateContext<'_>, expr: &Expr<'_>, cond_expr: &Expr<'_>) -> bool { fn expression_returns_unmodified_err(expr: &Expr<'_>, cond_expr: &Expr<'_>) -> bool {
match peel_blocks_with_stmt(expr).kind { match peel_blocks_with_stmt(expr).kind {
ExprKind::Ret(Some(ret_expr)) => Self::expression_returns_unmodified_err(cx, ret_expr, cond_expr), ExprKind::Ret(Some(ret_expr)) => Self::expression_returns_unmodified_err(ret_expr, cond_expr),
ExprKind::Path(_) => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr), ExprKind::Path(_) => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr),
_ => false, _ => false,
} }
......
...@@ -330,7 +330,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: ...@@ -330,7 +330,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args:
// `.iter()` and `.len()` called on same `Path` // `.iter()` and `.len()` called on same `Path`
if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind; if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind;
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind; if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;
if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments); if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
then { then {
span_lint(cx, span_lint(cx,
RANGE_ZIP_WITH_LEN, RANGE_ZIP_WITH_LEN,
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth}; use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth};
use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_data_structures::{fx::FxHashMap, transitive_relation::TransitiveRelation}; use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{def_id, Body, FnDecl, HirId}; use rustc_hir::{def_id, Body, FnDecl, HirId};
...@@ -512,7 +512,7 @@ fn call_return_effect( ...@@ -512,7 +512,7 @@ fn call_return_effect(
/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c` /// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
/// possible borrowers of `a`. /// possible borrowers of `a`.
struct PossibleBorrowerVisitor<'a, 'tcx> { struct PossibleBorrowerVisitor<'a, 'tcx> {
possible_borrower: TransitiveRelation<mir::Local>, possible_borrower: TransitiveRelation,
body: &'a mir::Body<'tcx>, body: &'a mir::Body<'tcx>,
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>, possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
...@@ -543,18 +543,10 @@ fn into_map( ...@@ -543,18 +543,10 @@ fn into_map(
continue; continue;
} }
let borrowers = self.possible_borrower.reachable_from(row); let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len());
borrowers.remove(mir::Local::from_usize(0));
if !borrowers.is_empty() { if !borrowers.is_empty() {
let mut bs = HybridBitSet::new_empty(self.body.local_decls.len()); map.insert(row, borrowers);
for c in borrowers {
if c != mir::Local::from_usize(0) {
bs.insert(c);
}
}
if !bs.is_empty() {
map.insert(row, bs);
}
} }
} }
...@@ -644,7 +636,7 @@ fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Loca ...@@ -644,7 +636,7 @@ fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Loca
/// For exampel, `_1 = &mut _2` generate _1: {_2,...} /// For exampel, `_1 = &mut _2` generate _1: {_2,...}
/// Known Problems: not sure all borrowed are tracked /// Known Problems: not sure all borrowed are tracked
struct PossibleOriginVisitor<'a, 'tcx> { struct PossibleOriginVisitor<'a, 'tcx> {
possible_origin: TransitiveRelation<mir::Local>, possible_origin: TransitiveRelation,
body: &'a mir::Body<'tcx>, body: &'a mir::Body<'tcx>,
} }
...@@ -663,18 +655,10 @@ fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, HybridBitSet< ...@@ -663,18 +655,10 @@ fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, HybridBitSet<
continue; continue;
} }
let borrowers = self.possible_origin.reachable_from(row); let mut borrowers = self.possible_origin.reachable_from(row, self.body.local_decls.len());
borrowers.remove(mir::Local::from_usize(0));
if !borrowers.is_empty() { if !borrowers.is_empty() {
let mut bs = HybridBitSet::new_empty(self.body.local_decls.len()); map.insert(row, borrowers);
for c in borrowers {
if c != mir::Local::from_usize(0) {
bs.insert(c);
}
}
if !bs.is_empty() {
map.insert(row, bs);
}
} }
} }
map map
...@@ -766,3 +750,28 @@ fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { ...@@ -766,3 +750,28 @@ fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {
self.maybe_live.contains(local) self.maybe_live.contains(local)
} }
} }
#[derive(Default)]
struct TransitiveRelation {
relations: FxHashMap<mir::Local, Vec<mir::Local>>,
}
impl TransitiveRelation {
fn add(&mut self, a: mir::Local, b: mir::Local) {
self.relations.entry(a).or_default().push(b);
}
fn reachable_from(&self, a: mir::Local, domain_size: usize) -> HybridBitSet<mir::Local> {
let mut seen = HybridBitSet::new_empty(domain_size);
let mut stack = vec![a];
while let Some(u) = stack.pop() {
if let Some(edges) = self.relations.get(&u) {
for &v in edges {
if seen.insert(v) {
stack.push(v);
}
}
}
}
seen
}
}
...@@ -399,9 +399,9 @@ fn if_statment_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> { ...@@ -399,9 +399,9 @@ fn if_statment_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> {
fn append_opt_vecs<A>(target_opt: Option<Vec<A>>, source_opt: Option<Vec<A>>) -> Option<Vec<A>> { fn append_opt_vecs<A>(target_opt: Option<Vec<A>>, source_opt: Option<Vec<A>>) -> Option<Vec<A>> {
match (target_opt, source_opt) { match (target_opt, source_opt) {
(Some(mut target), Some(mut source)) => { (Some(mut target), Some(source)) => {
target.reserve(source.len()); target.reserve(source.len());
for op in source.drain(..) { for op in source {
target.push(op); target.push(op);
} }
Some(target) Some(target)
...@@ -436,9 +436,9 @@ fn chained_binops_helper<'expr>(left_outer: &'expr Expr, right_outer: &'expr Exp ...@@ -436,9 +436,9 @@ fn chained_binops_helper<'expr>(left_outer: &'expr Expr, right_outer: &'expr Exp
chained_binops_helper(left_left, left_right), chained_binops_helper(left_left, left_right),
chained_binops_helper(right_left, right_right), chained_binops_helper(right_left, right_right),
) { ) {
(Some(mut left_ops), Some(mut right_ops)) => { (Some(mut left_ops), Some(right_ops)) => {
left_ops.reserve(right_ops.len()); left_ops.reserve(right_ops.len());
for op in right_ops.drain(..) { for op in right_ops {
left_ops.push(op); left_ops.push(op);
} }
Some(left_ops) Some(left_ops)
......
...@@ -364,6 +364,10 @@ ...@@ -364,6 +364,10 @@
/// ### Why is this bad? /// ### Why is this bad?
/// The results of such a transmute are not defined. /// The results of such a transmute are not defined.
/// ///
/// ### Known problems
/// This lint has had multiple problems in the past and was moved to `nursery`. See issue
/// [#8496](https://github.com/rust-lang/rust-clippy/issues/8496) for more details.
///
/// ### Example /// ### Example
/// ```rust /// ```rust
/// struct Foo<T>(u32, T); /// struct Foo<T>(u32, T);
......
...@@ -67,59 +67,51 @@ struct SortByKeyDetection { ...@@ -67,59 +67,51 @@ struct SortByKeyDetection {
/// Detect if the two expressions are mirrored (identical, except one /// Detect if the two expressions are mirrored (identical, except one
/// contains a and the other replaces it with b) /// contains a and the other replaces it with b)
fn mirrored_exprs( fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool {
cx: &LateContext<'_>,
a_expr: &Expr<'_>,
a_ident: &Ident,
b_expr: &Expr<'_>,
b_ident: &Ident,
) -> bool {
match (&a_expr.kind, &b_expr.kind) { match (&a_expr.kind, &b_expr.kind) {
// Two boxes with mirrored contents // Two boxes with mirrored contents
(ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => { (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => {
mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
}, },
// Two arrays with mirrored contents // Two arrays with mirrored contents
(ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => { (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => {
iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
}, },
// The two exprs are function calls. // The two exprs are function calls.
// Check to see that the function itself and its arguments are mirrored // Check to see that the function itself and its arguments are mirrored
(ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => { (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => {
mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
&& iter::zip(*left_args, *right_args) && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
.all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident))
}, },
// The two exprs are method calls. // The two exprs are method calls.
// Check to see that the function is the same and the arguments are mirrored // Check to see that the function is the same and the arguments are mirrored
// This is enough because the receiver of the method is listed in the arguments // This is enough because the receiver of the method is listed in the arguments
(ExprKind::MethodCall(left_segment, left_args, _), ExprKind::MethodCall(right_segment, right_args, _)) => { (ExprKind::MethodCall(left_segment, left_args, _), ExprKind::MethodCall(right_segment, right_args, _)) => {
left_segment.ident == right_segment.ident left_segment.ident == right_segment.ident
&& iter::zip(*left_args, *right_args) && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
.all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident))
}, },
// Two tuples with mirrored contents // Two tuples with mirrored contents
(ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => { (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => {
iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
}, },
// Two binary ops, which are the same operation and which have mirrored arguments // Two binary ops, which are the same operation and which have mirrored arguments
(ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => { (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => {
left_op.node == right_op.node left_op.node == right_op.node
&& mirrored_exprs(cx, left_left, a_ident, right_left, b_ident) && mirrored_exprs(left_left, a_ident, right_left, b_ident)
&& mirrored_exprs(cx, left_right, a_ident, right_right, b_ident) && mirrored_exprs(left_right, a_ident, right_right, b_ident)
}, },
// Two unary ops, which are the same operation and which have the same argument // Two unary ops, which are the same operation and which have the same argument
(ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => { (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => {
left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) left_op == right_op && mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
}, },
// The two exprs are literals of some kind // The two exprs are literals of some kind
(ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node, (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node,
(ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(cx, left, a_ident, right, b_ident), (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(left, a_ident, right, b_ident),
(ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => { (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => {
mirrored_exprs(cx, left_block, a_ident, right_block, b_ident) mirrored_exprs(left_block, a_ident, right_block, b_ident)
}, },
(ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => { (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => {
left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident) left_ident.name == right_ident.name && mirrored_exprs(left_expr, a_ident, right_expr, right_ident)
}, },
// Two paths: either one is a and the other is b, or they're identical to each other // Two paths: either one is a and the other is b, or they're identical to each other
( (
...@@ -151,11 +143,9 @@ fn mirrored_exprs( ...@@ -151,11 +143,9 @@ fn mirrored_exprs(
( (
ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), ExprKind::AddrOf(left_kind, Mutability::Not, left_expr),
ExprKind::AddrOf(right_kind, Mutability::Not, right_expr), ExprKind::AddrOf(right_kind, Mutability::Not, right_expr),
) => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), ) => left_kind == right_kind && mirrored_exprs(left_expr, a_ident, right_expr, b_ident),
(_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => { (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => mirrored_exprs(a_expr, a_ident, right_expr, b_ident),
mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident) (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(left_expr, a_ident, b_expr, b_ident),
},
(ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident),
_ => false, _ => false,
} }
} }
...@@ -176,14 +166,13 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> { ...@@ -176,14 +166,13 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
if method_path.ident.name == sym::cmp; if method_path.ident.name == sym::cmp;
then { then {
let (closure_body, closure_arg, reverse) = if mirrored_exprs( let (closure_body, closure_arg, reverse) = if mirrored_exprs(
cx,
left_expr, left_expr,
left_ident, left_ident,
right_expr, right_expr,
right_ident right_ident
) { ) {
(Sugg::hir(cx, left_expr, "..").to_string(), left_ident.name.to_string(), false) (Sugg::hir(cx, left_expr, "..").to_string(), left_ident.name.to_string(), false)
} else if mirrored_exprs(cx, left_expr, right_ident, right_expr, left_ident) { } else if mirrored_exprs(left_expr, right_ident, right_expr, left_ident) {
(Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true) (Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true)
} else { } else {
return None; return None;
...@@ -239,7 +228,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { ...@@ -239,7 +228,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if trigger.unstable { "_unstable" } else { "" }, if trigger.unstable { "_unstable" } else { "" },
trigger.closure_arg, trigger.closure_arg,
if trigger.reverse { if trigger.reverse {
format!("Reverse({})", trigger.closure_body) format!("std::cmp::Reverse({})", trigger.closure_body)
} else { } else {
trigger.closure_body.to_string() trigger.closure_body.to_string()
}, },
......
...@@ -9,7 +9,8 @@ ...@@ -9,7 +9,8 @@
def::{CtorOf, DefKind, Res}, def::{CtorOf, DefKind, Res},
def_id::LocalDefId, def_id::LocalDefId,
intravisit::{walk_inf, walk_ty, Visitor}, intravisit::{walk_inf, walk_ty, Visitor},
Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Path, QPath, TyKind, Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath,
TyKind,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_semver::RustcVersion; use rustc_semver::RustcVersion;
...@@ -252,6 +253,22 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { ...@@ -252,6 +253,22 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
} }
} }
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
if_chain! {
if !pat.span.from_expansion();
if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
if let PatKind::Path(QPath::Resolved(_, path)) = pat.kind;
if !matches!(path.res, Res::SelfTy { .. } | Res::Def(DefKind::TyParam, _));
if cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id);
if let [first, ..] = path.segments;
if let Some(hir_id) = first.hir_id;
then {
span_lint(cx, cx.tcx.hir().span(hir_id));
}
}
}
extract_msrv_attr!(LateContext); extract_msrv_attr!(LateContext);
} }
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
use rustc_middle::mir::interpret::ConstValue; use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::ty; use rustc_middle::ty::{self, subst::GenericArgKind};
use rustc_semver::RustcVersion; use rustc_semver::RustcVersion;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Spanned; use rustc_span::source_map::Spanned;
...@@ -337,6 +337,15 @@ ...@@ -337,6 +337,15 @@
"found clippy lint without `clippy::version` attribute" "found clippy lint without `clippy::version` attribute"
} }
declare_clippy_lint! {
/// ### What it does
/// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV.
///
pub MISSING_MSRV_ATTR_IMPL,
internal,
"checking if all necessary steps were taken when adding a MSRV to a lint"
}
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
impl EarlyLintPass for ClippyLintsInternal { impl EarlyLintPass for ClippyLintsInternal {
...@@ -1314,3 +1323,46 @@ fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: S ...@@ -1314,3 +1323,46 @@ fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: S
span.parent(), span.parent(),
) )
} }
declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]);
impl LateLintPass<'_> for MsrvAttrImpl {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
if_chain! {
if let hir::ItemKind::Impl(hir::Impl {
of_trait: Some(lint_pass_trait_ref),
self_ty,
items,
..
}) = &item.kind;
if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id();
let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS);
if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS);
let self_ty = hir_ty_to_ty(cx.tcx, self_ty);
if let ty::Adt(self_ty_def, _) = self_ty.kind();
if self_ty_def.is_struct();
if self_ty_def.all_fields().any(|f| {
cx.tcx
.type_of(f.did)
.walk()
.filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
.any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION))
});
if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs));
then {
let context = if is_late_pass { "LateContext" } else { "EarlyContext" };
let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" };
let span = cx.sess().source_map().span_through_char(item.span, '{');
span_lint_and_sugg(
cx,
MISSING_MSRV_ATTR_IMPL,
span,
&format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"),
&format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"),
format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")),
Applicability::MachineApplicable,
);
}
}
}
}
...@@ -473,7 +473,7 @@ fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) { ...@@ -473,7 +473,7 @@ fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) {
/// ``` /// ```
fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) {
if let Some(args) = match_lint_emission(cx, expr) { if let Some(args) = match_lint_emission(cx, expr) {
let mut emission_info = extract_emission_info(cx, args); let emission_info = extract_emission_info(cx, args);
if emission_info.is_empty() { if emission_info.is_empty() {
// See: // See:
// - src/misc.rs:734:9 // - src/misc.rs:734:9
...@@ -483,7 +483,7 @@ fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) { ...@@ -483,7 +483,7 @@ fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) {
return; return;
} }
for (lint_name, applicability, is_multi_part) in emission_info.drain(..) { for (lint_name, applicability, is_multi_part) in emission_info {
let app_info = self.applicability_info.entry(lint_name).or_default(); let app_info = self.applicability_info.entry(lint_name).or_default();
app_info.applicability = applicability; app_info.applicability = applicability;
app_info.is_multi_part_suggestion = is_multi_part; app_info.is_multi_part_suggestion = is_multi_part;
...@@ -693,7 +693,7 @@ fn extract_emission_info<'hir>( ...@@ -693,7 +693,7 @@ fn extract_emission_info<'hir>(
} }
lints lints
.drain(..) .into_iter()
.map(|lint_name| (lint_name, applicability, multi_part)) .map(|lint_name| (lint_name, applicability, multi_part))
.collect() .collect()
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
use rustc_ast::ast::{Expr, ExprKind, Impl, Item, ItemKind, MacCall, Path, StrLit, StrStyle}; use rustc_ast::ast::{Expr, ExprKind, Impl, Item, ItemKind, MacCall, Path, StrLit, StrStyle};
use rustc_ast::token::{self, LitKind}; use rustc_ast::token::{self, LitKind};
use rustc_ast::tokenstream::TokenStream; use rustc_ast::tokenstream::TokenStream;
use rustc_errors::Applicability; use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_lexer::unescape::{self, EscapeError}; use rustc_lexer::unescape::{self, EscapeError};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_parse::parser; use rustc_parse::parser;
...@@ -534,7 +534,7 @@ fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) ...@@ -534,7 +534,7 @@ fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool)
match parser match parser
.parse_expr() .parse_expr()
.map(rustc_ast::ptr::P::into_inner) .map(rustc_ast::ptr::P::into_inner)
.map_err(|e| e.cancel()) .map_err(DiagnosticBuilder::cancel)
{ {
// write!(e, ...) // write!(e, ...)
Ok(p) if parser.eat(&token::Comma) => Some(p), Ok(p) if parser.eat(&token::Comma) => Some(p),
...@@ -563,7 +563,7 @@ fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) ...@@ -563,7 +563,7 @@ fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool)
} }
let comma_span = parser.prev_token.span; let comma_span = parser.prev_token.span;
let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|err| err.cancel()) { let token_expr = if let Ok(expr) = parser.parse_expr().map_err(DiagnosticBuilder::cancel) {
expr expr
} else { } else {
return (Some(fmtstr), None); return (Some(fmtstr), None);
......
...@@ -279,8 +279,22 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { ...@@ -279,8 +279,22 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
(ForeignMod(l), ForeignMod(r)) => { (ForeignMod(l), ForeignMod(r)) => {
both(&l.abi, &r.abi, eq_str_lit) && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind)) both(&l.abi, &r.abi, eq_str_lit) && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
}, },
(TyAlias(box ast::TyAlias { defaultness: ld, generics: lg, bounds: lb, ty: lt, .. }), (
TyAlias(box ast::TyAlias { defaultness: rd, generics: rg, bounds: rb, ty: rt, .. })) => { TyAlias(box ast::TyAlias {
defaultness: ld,
generics: lg,
bounds: lb,
ty: lt,
..
}),
TyAlias(box ast::TyAlias {
defaultness: rd,
generics: rg,
bounds: rb,
ty: rt,
..
}),
) => {
eq_defaultness(*ld, *rd) eq_defaultness(*ld, *rd)
&& eq_generics(lg, rg) && eq_generics(lg, rg)
&& over(lb, rb, eq_generic_bound) && over(lb, rb, eq_generic_bound)
...@@ -370,8 +384,22 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { ...@@ -370,8 +384,22 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
) => { ) => {
eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r)) eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
}, },
(TyAlias(box ast::TyAlias { defaultness: ld, generics: lg, bounds: lb, ty: lt, .. }), (
TyAlias(box ast::TyAlias { defaultness: rd, generics: rg, bounds: rb, ty: rt, .. })) => { TyAlias(box ast::TyAlias {
defaultness: ld,
generics: lg,
bounds: lb,
ty: lt,
..
}),
TyAlias(box ast::TyAlias {
defaultness: rd,
generics: rg,
bounds: rb,
ty: rt,
..
}),
) => {
eq_defaultness(*ld, *rd) eq_defaultness(*ld, *rd)
&& eq_generics(lg, rg) && eq_generics(lg, rg)
&& over(lb, rb, eq_generic_bound) && over(lb, rb, eq_generic_bound)
...@@ -402,8 +430,22 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { ...@@ -402,8 +430,22 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
) => { ) => {
eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r)) eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
}, },
(TyAlias(box ast::TyAlias { defaultness: ld, generics: lg, bounds: lb, ty: lt, .. }), (
TyAlias(box ast::TyAlias { defaultness: rd, generics: rg, bounds: rb, ty: rt, .. })) => { TyAlias(box ast::TyAlias {
defaultness: ld,
generics: lg,
bounds: lb,
ty: lt,
..
}),
TyAlias(box ast::TyAlias {
defaultness: rd,
generics: rg,
bounds: rb,
ty: rt,
..
}),
) => {
eq_defaultness(*ld, *rd) eq_defaultness(*ld, *rd)
&& eq_generics(lg, rg) && eq_generics(lg, rg)
&& over(lb, rb, eq_generic_bound) && over(lb, rb, eq_generic_bound)
......
...@@ -593,7 +593,8 @@ pub fn miri_to_const(result: ty::Const<'_>) -> Option<Constant> { ...@@ -593,7 +593,8 @@ pub fn miri_to_const(result: ty::Const<'_>) -> Option<Constant> {
ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty().kind() { ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty().kind() {
ty::Ref(_, tam, _) => match tam.kind() { ty::Ref(_, tam, _) => match tam.kind() {
ty::Str => String::from_utf8( ty::Str => String::from_utf8(
data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end) data.inner()
.inspect_with_uninit_and_ptr_outside_interpreter(start..end)
.to_owned(), .to_owned(),
) )
.ok() .ok()
...@@ -605,7 +606,8 @@ pub fn miri_to_const(result: ty::Const<'_>) -> Option<Constant> { ...@@ -605,7 +606,8 @@ pub fn miri_to_const(result: ty::Const<'_>) -> Option<Constant> {
ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty().kind() { ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty().kind() {
ty::Array(sub_type, len) => match sub_type.kind() { ty::Array(sub_type, len) => match sub_type.kind() {
ty::Float(FloatTy::F32) => match miri_to_const(*len) { ty::Float(FloatTy::F32) => match miri_to_const(*len) {
Some(Constant::Int(len)) => alloc.inner() Some(Constant::Int(len)) => alloc
.inner()
.inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize)) .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize))
.to_owned() .to_owned()
.chunks(4) .chunks(4)
...@@ -619,7 +621,8 @@ pub fn miri_to_const(result: ty::Const<'_>) -> Option<Constant> { ...@@ -619,7 +621,8 @@ pub fn miri_to_const(result: ty::Const<'_>) -> Option<Constant> {
_ => None, _ => None,
}, },
ty::Float(FloatTy::F64) => match miri_to_const(*len) { ty::Float(FloatTy::F64) => match miri_to_const(*len) {
Some(Constant::Int(len)) => alloc.inner() Some(Constant::Int(len)) => alloc
.inner()
.inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize)) .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize))
.to_owned() .to_owned()
.chunks(8) .chunks(8)
......
...@@ -74,6 +74,10 @@ pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { ...@@ -74,6 +74,10 @@ pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
self.inter_expr().eq_expr(left, right) self.inter_expr().eq_expr(left, right)
} }
pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
self.inter_expr().eq_path(left, right)
}
pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool { pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
self.inter_expr().eq_path_segment(left, right) self.inter_expr().eq_path_segment(left, right)
} }
...@@ -362,7 +366,7 @@ fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool { ...@@ -362,7 +366,7 @@ fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
} }
} }
fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool { pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
match (left.res, right.res) { match (left.res, right.res) {
(Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r), (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r),
(Res::Local(_), _) | (_, Res::Local(_)) => false, (Res::Local(_), _) | (_, Res::Local(_)) => false,
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
1,46,0 { CONST_IF_MATCH } 1,46,0 { CONST_IF_MATCH }
1,45,0 { STR_STRIP_PREFIX } 1,45,0 { STR_STRIP_PREFIX }
1,43,0 { LOG2_10, LOG10_2 } 1,43,0 { LOG2_10, LOG10_2 }
1,42,0 { MATCHES_MACRO, SLICE_PATTERNS } 1,42,0 { MATCHES_MACRO, SLICE_PATTERNS, PTR_SLICE_RAW_PARTS }
1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE } 1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF } 1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
1,38,0 { POINTER_CAST } 1,38,0 { POINTER_CAST }
......
...@@ -32,6 +32,8 @@ ...@@ -32,6 +32,8 @@
pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
#[cfg(feature = "internal")]
pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"];
pub const EXIT: [&str; 3] = ["std", "process", "exit"]; pub const EXIT: [&str; 3] = ["std", "process", "exit"];
pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"]; pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"]; pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
...@@ -67,6 +69,8 @@ ...@@ -67,6 +69,8 @@
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"];
#[cfg(feature = "internal")]
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"]; pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
...@@ -126,6 +130,8 @@ ...@@ -126,6 +130,8 @@
pub const RESULT: [&str; 3] = ["core", "result", "Result"]; pub const RESULT: [&str; 3] = ["core", "result", "Result"];
pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"]; pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"]; pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
#[cfg(feature = "internal")]
pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"];
pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"]; pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"];
pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"]; pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
......
...@@ -294,7 +294,11 @@ pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symb ...@@ -294,7 +294,11 @@ pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symb
/// Returns `false` if the `LangItem` is not defined. /// Returns `false` if the `LangItem` is not defined.
pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool { pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
match ty.kind() { match ty.kind() {
ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).map_or(false, |li| li == adt.did()), ty::Adt(adt, _) => cx
.tcx
.lang_items()
.require(lang_item)
.map_or(false, |li| li == adt.did()),
_ => false, _ => false,
} }
} }
......
...@@ -2,6 +2,6 @@ max_width = 120 ...@@ -2,6 +2,6 @@ max_width = 120
comment_width = 100 comment_width = 100
match_block_trailing_comma = true match_block_trailing_comma = true
wrap_comments = true wrap_comments = true
edition = "2018" edition = "2021"
error_on_line_overflow = true error_on_line_overflow = true
version = "Two" version = "Two"
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
"syn", "syn",
"tokio", "tokio",
"parking_lot", "parking_lot",
"rustc_semver",
]; ];
// Test dependencies may need an `extern crate` here to ensure that they show up // Test dependencies may need an `extern crate` here to ensure that they show up
...@@ -53,6 +54,8 @@ ...@@ -53,6 +54,8 @@
#[allow(unused_extern_crates)] #[allow(unused_extern_crates)]
extern crate quote; extern crate quote;
#[allow(unused_extern_crates)] #[allow(unused_extern_crates)]
extern crate rustc_semver;
#[allow(unused_extern_crates)]
extern crate syn; extern crate syn;
#[allow(unused_extern_crates)] #[allow(unused_extern_crates)]
extern crate tokio; extern crate tokio;
...@@ -165,7 +168,11 @@ fn run_ui() { ...@@ -165,7 +168,11 @@ fn run_ui() {
let _threads = VarGuard::set( let _threads = VarGuard::set(
"RUST_TEST_THREADS", "RUST_TEST_THREADS",
// if RUST_TEST_THREADS is set, adhere to it, otherwise override it // if RUST_TEST_THREADS is set, adhere to it, otherwise override it
env::var("RUST_TEST_THREADS").unwrap_or_else(|_| num_cpus::get().to_string()), env::var("RUST_TEST_THREADS").unwrap_or_else(|_| {
std::thread::available_parallelism()
.map_or(1, std::num::NonZeroUsize::get)
.to_string()
}),
); );
compiletest::run_tests(&config); compiletest::run_tests(&config);
} }
......
// run-rustfix
#![deny(clippy::internal)]
#![allow(clippy::missing_clippy_version_attribute)]
#![feature(rustc_private)]
extern crate rustc_ast;
extern crate rustc_hir;
extern crate rustc_lint;
extern crate rustc_middle;
#[macro_use]
extern crate rustc_session;
use clippy_utils::extract_msrv_attr;
use rustc_hir::Expr;
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
use rustc_semver::RustcVersion;
declare_lint! {
pub TEST_LINT,
Warn,
""
}
struct Pass {
msrv: Option<RustcVersion>,
}
impl_lint_pass!(Pass => [TEST_LINT]);
impl LateLintPass<'_> for Pass {
extract_msrv_attr!(LateContext);
fn check_expr(&mut self, _: &LateContext<'_>, _: &Expr<'_>) {}
}
impl EarlyLintPass for Pass {
extract_msrv_attr!(EarlyContext);
fn check_expr(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Expr) {}
}
fn main() {}
// run-rustfix
#![deny(clippy::internal)]
#![allow(clippy::missing_clippy_version_attribute)]
#![feature(rustc_private)]
extern crate rustc_ast;
extern crate rustc_hir;
extern crate rustc_lint;
extern crate rustc_middle;
#[macro_use]
extern crate rustc_session;
use clippy_utils::extract_msrv_attr;
use rustc_hir::Expr;
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
use rustc_semver::RustcVersion;
declare_lint! {
pub TEST_LINT,
Warn,
""
}
struct Pass {
msrv: Option<RustcVersion>,
}
impl_lint_pass!(Pass => [TEST_LINT]);
impl LateLintPass<'_> for Pass {
fn check_expr(&mut self, _: &LateContext<'_>, _: &Expr<'_>) {}
}
impl EarlyLintPass for Pass {
fn check_expr(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Expr) {}
}
fn main() {}
error: `extract_msrv_attr!` macro missing from `LateLintPass` implementation
--> $DIR/invalid_msrv_attr_impl.rs:30:1
|
LL | impl LateLintPass<'_> for Pass {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/invalid_msrv_attr_impl.rs:3:9
|
LL | #![deny(clippy::internal)]
| ^^^^^^^^^^^^^^^^
= note: `#[deny(clippy::missing_msrv_attr_impl)]` implied by `#[deny(clippy::internal)]`
help: add `extract_msrv_attr!(LateContext)` to the `LateLintPass` implementation
|
LL + impl LateLintPass<'_> for Pass {
LL + extract_msrv_attr!(LateContext);
|
error: `extract_msrv_attr!` macro missing from `EarlyLintPass` implementation
--> $DIR/invalid_msrv_attr_impl.rs:34:1
|
LL | impl EarlyLintPass for Pass {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: add `extract_msrv_attr!(EarlyContext)` to the `EarlyLintPass` implementation
|
LL + impl EarlyLintPass for Pass {
LL + extract_msrv_attr!(EarlyContext);
|
error: aborting due to 2 previous errors
#![feature(lint_reasons)]
#![deny(clippy::allow_attributes_without_reason)]
// These should trigger the lint
#[allow(dead_code)]
#[allow(dead_code, deprecated)]
// These should be fine
#[allow(dead_code, reason = "This should be allowed")]
#[warn(dyn_drop, reason = "Warnings can also have reasons")]
#[warn(deref_nullptr)]
#[deny(deref_nullptr)]
#[forbid(deref_nullptr)]
fn main() {}
error: `allow` attribute without specifying a reason
--> $DIR/allow_attributes_without_reason.rs:5:1
|
LL | #[allow(dead_code)]
| ^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/allow_attributes_without_reason.rs:2:9
|
LL | #![deny(clippy::allow_attributes_without_reason)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: try adding a reason at the end with `, reason = ".."`
error: `allow` attribute without specifying a reason
--> $DIR/allow_attributes_without_reason.rs:6:1
|
LL | #[allow(dead_code, deprecated)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: try adding a reason at the end with `, reason = ".."`
error: aborting due to 2 previous errors
fn main() {
let x: [i32; 3] = [1_i32, 2, 3];
let r_x = &x;
// Check casting through multiple bindings
// Because it's separate, it does not check the cast back to something of the same size
let a = r_x as *const [i32];
let b = a as *const [u8];
let c = b as *const [u32];
// loses data
let loss = r_x as *const [i32] as *const [u8];
// Cast back to same size but different type loses no data, just type conversion
// This is weird code but there's no reason for this lint specifically to fire *twice* on it
let restore = r_x as *const [i32] as *const [u8] as *const [u32];
// Check casting through blocks is detected
let loss_block_1 = { r_x as *const [i32] } as *const [u8];
let loss_block_2 = {
let _ = ();
r_x as *const [i32]
} as *const [u8];
// Check that resores of the same size are detected through blocks
let restore_block_1 = { r_x as *const [i32] } as *const [u8] as *const [u32];
let restore_block_2 = { ({ r_x as *const [i32] }) as *const [u8] } as *const [u32];
let restore_block_3 = {
let _ = ();
({
let _ = ();
r_x as *const [i32]
}) as *const [u8]
} as *const [u32];
// Check that the result of a long chain of casts is detected
let long_chain_loss = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8];
let long_chain_restore =
r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8] as *const [u32];
}
error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
--> $DIR/cast_slice_different_sizes.rs:7:13
|
LL | let b = a as *const [u8];
| ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(a as *const u8, ..)`
|
= note: `#[deny(clippy::cast_slice_different_sizes)]` on by default
error: casting between raw pointers to `[u8]` (element size 1) and `[u32]` (element size 4) does not adjust the count
--> $DIR/cast_slice_different_sizes.rs:8:13
|
LL | let c = b as *const [u32];
| ^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(b as *const u32, ..)`
error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
--> $DIR/cast_slice_different_sizes.rs:11:16
|
LL | let loss = r_x as *const [i32] as *const [u8];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)`
error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
--> $DIR/cast_slice_different_sizes.rs:18:24
|
LL | let loss_block_1 = { r_x as *const [i32] } as *const [u8];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts({ r_x as *const [i32] } as *const u8, ..)`
error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
--> $DIR/cast_slice_different_sizes.rs:19:24
|
LL | let loss_block_2 = {
| ________________________^
LL | | let _ = ();
LL | | r_x as *const [i32]
LL | | } as *const [u8];
| |____________________^
|
help: replace with `ptr::slice_from_raw_parts`
|
LL ~ let loss_block_2 = core::ptr::slice_from_raw_parts({
LL + let _ = ();
LL + r_x as *const [i32]
LL ~ } as *const u8, ..);
|
error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
--> $DIR/cast_slice_different_sizes.rs:36:27
|
LL | let long_chain_loss = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const u8, ..)`
error: aborting due to 6 previous errors
...@@ -12,12 +12,12 @@ ...@@ -12,12 +12,12 @@
#[warn(clippy::main_recursion)] #[warn(clippy::main_recursion)]
#[start] #[start]
fn main(argc: isize, argv: *const *const u8) -> isize { fn main(_argc: isize, _argv: *const *const u8) -> isize {
let x = N.load(Ordering::Relaxed); let x = N.load(Ordering::Relaxed);
N.store(x + 1, Ordering::Relaxed); N.store(x + 1, Ordering::Relaxed);
if x < 3 { if x < 3 {
main(argc, argv); main(_argc, _argv);
} }
0 0
......
// compile-flags: --test
#![warn(clippy::dbg_macro)] #![warn(clippy::dbg_macro)]
fn foo(n: u32) -> u32 { fn foo(n: u32) -> u32 {
...@@ -40,3 +41,8 @@ fn foo<'b>(&self) { ...@@ -40,3 +41,8 @@ fn foo<'b>(&self) {
dbg!(2); dbg!(2);
}); });
} }
#[test]
pub fn issue8481() {
dbg!(1);
}
error: `dbg!` macro is intended as a debugging tool error: `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:4:22 --> $DIR/dbg_macro.rs:5:22
| |
LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
...@@ -11,7 +11,7 @@ LL | if let Some(n) = n.checked_sub(4) { n } else { n } ...@@ -11,7 +11,7 @@ LL | if let Some(n) = n.checked_sub(4) { n } else { n }
| ~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~
error: `dbg!` macro is intended as a debugging tool error: `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:8:8 --> $DIR/dbg_macro.rs:9:8
| |
LL | if dbg!(n <= 1) { LL | if dbg!(n <= 1) {
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
...@@ -22,7 +22,7 @@ LL | if n <= 1 { ...@@ -22,7 +22,7 @@ LL | if n <= 1 {
| ~~~~~~ | ~~~~~~
error: `dbg!` macro is intended as a debugging tool error: `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:9:9 --> $DIR/dbg_macro.rs:10:9
| |
LL | dbg!(1) LL | dbg!(1)
| ^^^^^^^ | ^^^^^^^
...@@ -33,7 +33,7 @@ LL | 1 ...@@ -33,7 +33,7 @@ LL | 1
| |
error: `dbg!` macro is intended as a debugging tool error: `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:11:9 --> $DIR/dbg_macro.rs:12:9
| |
LL | dbg!(n * factorial(n - 1)) LL | dbg!(n * factorial(n - 1))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
...@@ -44,7 +44,7 @@ LL | n * factorial(n - 1) ...@@ -44,7 +44,7 @@ LL | n * factorial(n - 1)
| |
error: `dbg!` macro is intended as a debugging tool error: `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:16:5 --> $DIR/dbg_macro.rs:17:5
| |
LL | dbg!(42); LL | dbg!(42);
| ^^^^^^^^ | ^^^^^^^^
...@@ -55,7 +55,7 @@ LL | 42; ...@@ -55,7 +55,7 @@ LL | 42;
| ~~ | ~~
error: `dbg!` macro is intended as a debugging tool error: `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:17:5 --> $DIR/dbg_macro.rs:18:5
| |
LL | dbg!(dbg!(dbg!(42))); LL | dbg!(dbg!(dbg!(42)));
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
...@@ -66,7 +66,7 @@ LL | dbg!(dbg!(42)); ...@@ -66,7 +66,7 @@ LL | dbg!(dbg!(42));
| ~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~
error: `dbg!` macro is intended as a debugging tool error: `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:18:14 --> $DIR/dbg_macro.rs:19:14
| |
LL | foo(3) + dbg!(factorial(4)); LL | foo(3) + dbg!(factorial(4));
| ^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^
...@@ -77,7 +77,7 @@ LL | foo(3) + factorial(4); ...@@ -77,7 +77,7 @@ LL | foo(3) + factorial(4);
| ~~~~~~~~~~~~ | ~~~~~~~~~~~~
error: `dbg!` macro is intended as a debugging tool error: `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:19:5 --> $DIR/dbg_macro.rs:20:5
| |
LL | dbg!(1, 2, dbg!(3, 4)); LL | dbg!(1, 2, dbg!(3, 4));
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
...@@ -88,7 +88,7 @@ LL | (1, 2, dbg!(3, 4)); ...@@ -88,7 +88,7 @@ LL | (1, 2, dbg!(3, 4));
| ~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~
error: `dbg!` macro is intended as a debugging tool error: `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:20:5 --> $DIR/dbg_macro.rs:21:5
| |
LL | dbg!(1, 2, 3, 4, 5); LL | dbg!(1, 2, 3, 4, 5);
| ^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^
...@@ -99,7 +99,7 @@ LL | (1, 2, 3, 4, 5); ...@@ -99,7 +99,7 @@ LL | (1, 2, 3, 4, 5);
| ~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~
error: `dbg!` macro is intended as a debugging tool error: `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:40:9 --> $DIR/dbg_macro.rs:41:9
| |
LL | dbg!(2); LL | dbg!(2);
| ^^^^^^^ | ^^^^^^^
......
// run-rustfix // run-rustfix
#![warn(clippy::extend_with_drain)] #![warn(clippy::extend_with_drain)]
#![allow(clippy::iter_with_drain)]
use std::collections::BinaryHeap; use std::collections::BinaryHeap;
fn main() { fn main() {
//gets linted //gets linted
let mut vec1 = vec![0u8; 1024]; let mut vec1 = vec![0u8; 1024];
let mut vec2: std::vec::Vec<u8> = Vec::new(); let mut vec2: std::vec::Vec<u8> = Vec::new();
vec2.append(&mut vec1); vec2.append(&mut vec1);
let mut vec3 = vec![0u8; 1024]; let mut vec3 = vec![0u8; 1024];
...@@ -17,7 +17,7 @@ fn main() { ...@@ -17,7 +17,7 @@ fn main() {
vec11.append(&mut return_vector()); vec11.append(&mut return_vector());
//won't get linted it dosen't move the entire content of a vec into another //won't get linted it doesn't move the entire content of a vec into another
let mut test1 = vec![0u8, 10]; let mut test1 = vec![0u8, 10];
let mut test2: std::vec::Vec<u8> = Vec::new(); let mut test2: std::vec::Vec<u8> = Vec::new();
......
// run-rustfix // run-rustfix
#![warn(clippy::extend_with_drain)] #![warn(clippy::extend_with_drain)]
#![allow(clippy::iter_with_drain)]
use std::collections::BinaryHeap; use std::collections::BinaryHeap;
fn main() { fn main() {
//gets linted //gets linted
let mut vec1 = vec![0u8; 1024]; let mut vec1 = vec![0u8; 1024];
let mut vec2: std::vec::Vec<u8> = Vec::new(); let mut vec2: std::vec::Vec<u8> = Vec::new();
vec2.extend(vec1.drain(..)); vec2.extend(vec1.drain(..));
let mut vec3 = vec![0u8; 1024]; let mut vec3 = vec![0u8; 1024];
...@@ -17,7 +17,7 @@ fn main() { ...@@ -17,7 +17,7 @@ fn main() {
vec11.extend(return_vector().drain(..)); vec11.extend(return_vector().drain(..));
//won't get linted it dosen't move the entire content of a vec into another //won't get linted it doesn't move the entire content of a vec into another
let mut test1 = vec![0u8, 10]; let mut test1 = vec![0u8, 10];
let mut test2: std::vec::Vec<u8> = Vec::new(); let mut test2: std::vec::Vec<u8> = Vec::new();
......
// run-rustfix
// will emits unused mut warnings after fixing
#![allow(unused_mut)]
// will emits needless collect warnings after fixing
#![allow(clippy::needless_collect)]
#![warn(clippy::iter_with_drain)]
use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
fn full() {
let mut a = vec!["aaa".to_string(), "bbb".to_string()];
let mut a: BinaryHeap<_> = a.into_iter().collect();
let mut a: HashSet<_> = a.drain().collect();
let mut a: VecDeque<_> = a.drain().collect();
let mut a: Vec<_> = a.into_iter().collect();
let mut a: HashMap<_, _> = a.into_iter().map(|x| (x.clone(), x)).collect();
let _: Vec<(String, String)> = a.drain().collect();
}
fn closed() {
let mut a = vec!["aaa".to_string(), "bbb".to_string()];
let mut a: BinaryHeap<_> = a.into_iter().collect();
let mut a: HashSet<_> = a.drain().collect();
let mut a: VecDeque<_> = a.drain().collect();
let mut a: Vec<_> = a.into_iter().collect();
let mut a: HashMap<_, _> = a.into_iter().map(|x| (x.clone(), x)).collect();
let _: Vec<(String, String)> = a.drain().collect();
}
fn should_not_help() {
let mut a = vec!["aaa".to_string(), "bbb".to_string()];
let mut a: BinaryHeap<_> = a.drain(1..).collect();
let mut a: HashSet<_> = a.drain().collect();
let mut a: VecDeque<_> = a.drain().collect();
let mut a: Vec<_> = a.drain(..a.len() - 1).collect();
let mut a: HashMap<_, _> = a.drain(1..a.len() - 1).map(|x| (x.clone(), x)).collect();
let _: Vec<(String, String)> = a.drain().collect();
let mut b = vec!["aaa".to_string(), "bbb".to_string()];
let _: Vec<_> = b.drain(0..a.len()).collect();
}
#[derive(Default)]
struct Bomb {
fire: Vec<u8>,
}
fn should_not_help_0(bomb: &mut Bomb) {
let _: Vec<u8> = bomb.fire.drain(..).collect();
}
fn main() {
full();
closed();
should_not_help();
should_not_help_0(&mut Bomb::default());
}
// run-rustfix
// will emits unused mut warnings after fixing
#![allow(unused_mut)]
// will emits needless collect warnings after fixing
#![allow(clippy::needless_collect)]
#![warn(clippy::iter_with_drain)]
use std::collections::{BinaryHeap, HashMap, HashSet, VecDeque};
fn full() {
let mut a = vec!["aaa".to_string(), "bbb".to_string()];
let mut a: BinaryHeap<_> = a.drain(..).collect();
let mut a: HashSet<_> = a.drain().collect();
let mut a: VecDeque<_> = a.drain().collect();
let mut a: Vec<_> = a.drain(..).collect();
let mut a: HashMap<_, _> = a.drain(..).map(|x| (x.clone(), x)).collect();
let _: Vec<(String, String)> = a.drain().collect();
}
fn closed() {
let mut a = vec!["aaa".to_string(), "bbb".to_string()];
let mut a: BinaryHeap<_> = a.drain(0..).collect();
let mut a: HashSet<_> = a.drain().collect();
let mut a: VecDeque<_> = a.drain().collect();
let mut a: Vec<_> = a.drain(..a.len()).collect();
let mut a: HashMap<_, _> = a.drain(0..a.len()).map(|x| (x.clone(), x)).collect();
let _: Vec<(String, String)> = a.drain().collect();
}
fn should_not_help() {
let mut a = vec!["aaa".to_string(), "bbb".to_string()];
let mut a: BinaryHeap<_> = a.drain(1..).collect();
let mut a: HashSet<_> = a.drain().collect();
let mut a: VecDeque<_> = a.drain().collect();
let mut a: Vec<_> = a.drain(..a.len() - 1).collect();
let mut a: HashMap<_, _> = a.drain(1..a.len() - 1).map(|x| (x.clone(), x)).collect();
let _: Vec<(String, String)> = a.drain().collect();
let mut b = vec!["aaa".to_string(), "bbb".to_string()];
let _: Vec<_> = b.drain(0..a.len()).collect();
}
#[derive(Default)]
struct Bomb {
fire: Vec<u8>,
}
fn should_not_help_0(bomb: &mut Bomb) {
let _: Vec<u8> = bomb.fire.drain(..).collect();
}
fn main() {
full();
closed();
should_not_help();
should_not_help_0(&mut Bomb::default());
}
error: `drain(..)` used on a `Vec`
--> $DIR/iter_with_drain.rs:11:34
|
LL | let mut a: BinaryHeap<_> = a.drain(..).collect();
| ^^^^^^^^^ help: try this: `into_iter()`
|
= note: `-D clippy::iter-with-drain` implied by `-D warnings`
error: `drain(..)` used on a `VecDeque`
--> $DIR/iter_with_drain.rs:14:27
|
LL | let mut a: Vec<_> = a.drain(..).collect();
| ^^^^^^^^^ help: try this: `into_iter()`
error: `drain(..)` used on a `Vec`
--> $DIR/iter_with_drain.rs:15:34
|
LL | let mut a: HashMap<_, _> = a.drain(..).map(|x| (x.clone(), x)).collect();
| ^^^^^^^^^ help: try this: `into_iter()`
error: `drain(..)` used on a `Vec`
--> $DIR/iter_with_drain.rs:21:34
|
LL | let mut a: BinaryHeap<_> = a.drain(0..).collect();
| ^^^^^^^^^^ help: try this: `into_iter()`
error: `drain(..)` used on a `VecDeque`
--> $DIR/iter_with_drain.rs:24:27
|
LL | let mut a: Vec<_> = a.drain(..a.len()).collect();
| ^^^^^^^^^^^^^^^^ help: try this: `into_iter()`
error: `drain(..)` used on a `Vec`
--> $DIR/iter_with_drain.rs:25:34
|
LL | let mut a: HashMap<_, _> = a.drain(0..a.len()).map(|x| (x.clone(), x)).collect();
| ^^^^^^^^^^^^^^^^^ help: try this: `into_iter()`
error: aborting due to 6 previous errors
...@@ -148,6 +148,7 @@ fn main() { ...@@ -148,6 +148,7 @@ fn main() {
// #7077 // #7077
let s = &String::new(); let s = &String::new();
#[allow(clippy::needless_match)]
let _: Option<&str> = match Some(s) { let _: Option<&str> = match Some(s) {
Some(s) => Some(s), Some(s) => Some(s),
None => None, None => None,
......
...@@ -214,6 +214,7 @@ const fn f4() { ...@@ -214,6 +214,7 @@ const fn f4() {
// #7077 // #7077
let s = &String::new(); let s = &String::new();
#[allow(clippy::needless_match)]
let _: Option<&str> = match Some(s) { let _: Option<&str> = match Some(s) {
Some(s) => Some(s), Some(s) => Some(s),
None => None, None => None,
......
// run-rustfix
#![warn(clippy::missing_spin_loop)]
#![allow(clippy::bool_comparison)]
#![allow(unused_braces)]
use core::sync::atomic::{AtomicBool, Ordering};
fn main() {
let b = AtomicBool::new(true);
// Those should lint
while b.load(Ordering::Acquire) { std::hint::spin_loop() }
while !b.load(Ordering::SeqCst) { std::hint::spin_loop() }
while b.load(Ordering::Acquire) == false { std::hint::spin_loop() }
while { true == b.load(Ordering::Acquire) } { std::hint::spin_loop() }
while b.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed) != Ok(true) { std::hint::spin_loop() }
while Ok(false) != b.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) { std::hint::spin_loop() }
// This is OK, as the body is not empty
while b.load(Ordering::Acquire) {
std::hint::spin_loop()
}
// TODO: also match on loop+match or while let
}
// run-rustfix
#![warn(clippy::missing_spin_loop)]
#![allow(clippy::bool_comparison)]
#![allow(unused_braces)]
use core::sync::atomic::{AtomicBool, Ordering};
fn main() {
let b = AtomicBool::new(true);
// Those should lint
while b.load(Ordering::Acquire) {}
while !b.load(Ordering::SeqCst) {}
while b.load(Ordering::Acquire) == false {}
while { true == b.load(Ordering::Acquire) } {}
while b.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed) != Ok(true) {}
while Ok(false) != b.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) {}
// This is OK, as the body is not empty
while b.load(Ordering::Acquire) {
std::hint::spin_loop()
}
// TODO: also match on loop+match or while let
}
error: busy-waiting loop should at least have a spin loop hint
--> $DIR/missing_spin_loop.rs:11:37
|
LL | while b.load(Ordering::Acquire) {}
| ^^ help: try this: `{ std::hint::spin_loop() }`
|
= note: `-D clippy::missing-spin-loop` implied by `-D warnings`
error: busy-waiting loop should at least have a spin loop hint
--> $DIR/missing_spin_loop.rs:13:37
|
LL | while !b.load(Ordering::SeqCst) {}
| ^^ help: try this: `{ std::hint::spin_loop() }`
error: busy-waiting loop should at least have a spin loop hint
--> $DIR/missing_spin_loop.rs:15:46
|
LL | while b.load(Ordering::Acquire) == false {}
| ^^ help: try this: `{ std::hint::spin_loop() }`
error: busy-waiting loop should at least have a spin loop hint
--> $DIR/missing_spin_loop.rs:17:49
|
LL | while { true == b.load(Ordering::Acquire) } {}
| ^^ help: try this: `{ std::hint::spin_loop() }`
error: busy-waiting loop should at least have a spin loop hint
--> $DIR/missing_spin_loop.rs:19:93
|
LL | while b.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed) != Ok(true) {}
| ^^ help: try this: `{ std::hint::spin_loop() }`
error: busy-waiting loop should at least have a spin loop hint
--> $DIR/missing_spin_loop.rs:21:94
|
LL | while Ok(false) != b.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) {}
| ^^ help: try this: `{ std::hint::spin_loop() }`
error: aborting due to 6 previous errors
// run-rustfix
#![warn(clippy::missing_spin_loop)]
#![feature(lang_items, start, libc)]
#![no_std]
use core::sync::atomic::{AtomicBool, Ordering};
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
// This should trigger the lint
let b = AtomicBool::new(true);
// This should lint with `core::hint::spin_loop()`
while b.load(Ordering::Acquire) { core::hint::spin_loop() }
0
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
// run-rustfix
#![warn(clippy::missing_spin_loop)]
#![feature(lang_items, start, libc)]
#![no_std]
use core::sync::atomic::{AtomicBool, Ordering};
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
// This should trigger the lint
let b = AtomicBool::new(true);
// This should lint with `core::hint::spin_loop()`
while b.load(Ordering::Acquire) {}
0
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
error: busy-waiting loop should at least have a spin loop hint
--> $DIR/missing_spin_loop_no_std.rs:13:37
|
LL | while b.load(Ordering::Acquire) {}
| ^^ help: try this: `{ core::hint::spin_loop() }`
|
= note: `-D clippy::missing-spin-loop` implied by `-D warnings`
error: aborting due to previous error
// run-rustfix
#![warn(clippy::needless_match)]
#![allow(clippy::manual_map)]
#![allow(dead_code)]
#[derive(Clone, Copy)]
enum Choice {
A,
B,
C,
D,
}
#[allow(unused_mut)]
fn useless_match() {
let mut i = 10;
let _: i32 = i;
let _: i32 = i;
let mut _i_mut = i;
let s = "test";
let _: &str = s;
}
fn custom_type_match(se: Choice) {
let _: Choice = se;
// Don't trigger
let _: Choice = match se {
Choice::A => Choice::A,
Choice::B => Choice::B,
_ => Choice::C,
};
// Mingled, don't trigger
let _: Choice = match se {
Choice::A => Choice::B,
Choice::B => Choice::C,
Choice::C => Choice::D,
Choice::D => Choice::A,
};
}
fn option_match(x: Option<i32>) {
let _: Option<i32> = x;
// Don't trigger, this is the case for manual_map_option
let _: Option<i32> = match x {
Some(a) => Some(-a),
None => None,
};
}
fn func_ret_err<T>(err: T) -> Result<i32, T> {
Err(err)
}
fn result_match() {
let _: Result<i32, i32> = Ok(1);
let _: Result<i32, i32> = func_ret_err(0_i32);
}
fn if_let_option() -> Option<i32> {
Some(1)
}
fn if_let_result(x: Result<(), i32>) {
let _: Result<(), i32> = x;
let _: Result<(), i32> = x;
// Input type mismatch, don't trigger
let _: Result<(), i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
}
fn if_let_custom_enum(x: Choice) {
let _: Choice = x;
// Don't trigger
let _: Choice = if let Choice::A = x {
Choice::A
} else if true {
Choice::B
} else {
x
};
}
fn main() {}
// run-rustfix
#![warn(clippy::needless_match)]
#![allow(clippy::manual_map)]
#![allow(dead_code)]
#[derive(Clone, Copy)]
enum Choice {
A,
B,
C,
D,
}
#[allow(unused_mut)]
fn useless_match() {
let mut i = 10;
let _: i32 = match i {
0 => 0,
1 => 1,
2 => 2,
_ => i,
};
let _: i32 = match i {
0 => 0,
1 => 1,
ref i => *i,
};
let mut _i_mut = match i {
0 => 0,
1 => 1,
ref mut i => *i,
};
let s = "test";
let _: &str = match s {
"a" => "a",
"b" => "b",
s => s,
};
}
fn custom_type_match(se: Choice) {
let _: Choice = match se {
Choice::A => Choice::A,
Choice::B => Choice::B,
Choice::C => Choice::C,
Choice::D => Choice::D,
};
// Don't trigger
let _: Choice = match se {
Choice::A => Choice::A,
Choice::B => Choice::B,
_ => Choice::C,
};
// Mingled, don't trigger
let _: Choice = match se {
Choice::A => Choice::B,
Choice::B => Choice::C,
Choice::C => Choice::D,
Choice::D => Choice::A,
};
}
fn option_match(x: Option<i32>) {
let _: Option<i32> = match x {
Some(a) => Some(a),
None => None,
};
// Don't trigger, this is the case for manual_map_option
let _: Option<i32> = match x {
Some(a) => Some(-a),
None => None,
};
}
fn func_ret_err<T>(err: T) -> Result<i32, T> {
Err(err)
}
fn result_match() {
let _: Result<i32, i32> = match Ok(1) {
Ok(a) => Ok(a),
Err(err) => Err(err),
};
let _: Result<i32, i32> = match func_ret_err(0_i32) {
Err(err) => Err(err),
Ok(a) => Ok(a),
};
}
fn if_let_option() -> Option<i32> {
if let Some(a) = Some(1) { Some(a) } else { None }
}
fn if_let_result(x: Result<(), i32>) {
let _: Result<(), i32> = if let Err(e) = x { Err(e) } else { x };
let _: Result<(), i32> = if let Ok(val) = x { Ok(val) } else { x };
// Input type mismatch, don't trigger
let _: Result<(), i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
}
fn if_let_custom_enum(x: Choice) {
let _: Choice = if let Choice::A = x {
Choice::A
} else if let Choice::B = x {
Choice::B
} else if let Choice::C = x {
Choice::C
} else {
x
};
// Don't trigger
let _: Choice = if let Choice::A = x {
Choice::A
} else if true {
Choice::B
} else {
x
};
}
fn main() {}
error: this match expression is unnecessary
--> $DIR/needless_match.rs:17:18
|
LL | let _: i32 = match i {
| __________________^
LL | | 0 => 0,
LL | | 1 => 1,
LL | | 2 => 2,
LL | | _ => i,
LL | | };
| |_____^ help: replace it with: `i`
|
= note: `-D clippy::needless-match` implied by `-D warnings`
error: this match expression is unnecessary
--> $DIR/needless_match.rs:23:18
|
LL | let _: i32 = match i {
| __________________^
LL | | 0 => 0,
LL | | 1 => 1,
LL | | ref i => *i,
LL | | };
| |_____^ help: replace it with: `i`
error: this match expression is unnecessary
--> $DIR/needless_match.rs:28:22
|
LL | let mut _i_mut = match i {
| ______________________^
LL | | 0 => 0,
LL | | 1 => 1,
LL | | ref mut i => *i,
LL | | };
| |_____^ help: replace it with: `i`
error: this match expression is unnecessary
--> $DIR/needless_match.rs:35:19
|
LL | let _: &str = match s {
| ___________________^
LL | | "a" => "a",
LL | | "b" => "b",
LL | | s => s,
LL | | };
| |_____^ help: replace it with: `s`
error: this match expression is unnecessary
--> $DIR/needless_match.rs:43:21
|
LL | let _: Choice = match se {
| _____________________^
LL | | Choice::A => Choice::A,
LL | | Choice::B => Choice::B,
LL | | Choice::C => Choice::C,
LL | | Choice::D => Choice::D,
LL | | };
| |_____^ help: replace it with: `se`
error: this match expression is unnecessary
--> $DIR/needless_match.rs:65:26
|
LL | let _: Option<i32> = match x {
| __________________________^
LL | | Some(a) => Some(a),
LL | | None => None,
LL | | };
| |_____^ help: replace it with: `x`
error: this match expression is unnecessary
--> $DIR/needless_match.rs:81:31
|
LL | let _: Result<i32, i32> = match Ok(1) {
| _______________________________^
LL | | Ok(a) => Ok(a),
LL | | Err(err) => Err(err),
LL | | };
| |_____^ help: replace it with: `Ok(1)`
error: this match expression is unnecessary
--> $DIR/needless_match.rs:85:31
|
LL | let _: Result<i32, i32> = match func_ret_err(0_i32) {
| _______________________________^
LL | | Err(err) => Err(err),
LL | | Ok(a) => Ok(a),
LL | | };
| |_____^ help: replace it with: `func_ret_err(0_i32)`
error: this if-let expression is unnecessary
--> $DIR/needless_match.rs:92:5
|
LL | if let Some(a) = Some(1) { Some(a) } else { None }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)`
error: this if-let expression is unnecessary
--> $DIR/needless_match.rs:96:30
|
LL | let _: Result<(), i32> = if let Err(e) = x { Err(e) } else { x };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
error: this if-let expression is unnecessary
--> $DIR/needless_match.rs:97:30
|
LL | let _: Result<(), i32> = if let Ok(val) = x { Ok(val) } else { x };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
error: this if-let expression is unnecessary
--> $DIR/needless_match.rs:103:21
|
LL | let _: Choice = if let Choice::A = x {
| _____________________^
LL | | Choice::A
LL | | } else if let Choice::B = x {
LL | | Choice::B
... |
LL | | x
LL | | };
| |_____^ help: replace it with: `x`
error: aborting due to 12 previous errors
#![warn(clippy::only_used_in_recursion)]
fn simple(a: usize, b: usize) -> usize {
if a == 0 { 1 } else { simple(a - 1, b) }
}
fn with_calc(a: usize, b: isize) -> usize {
if a == 0 { 1 } else { with_calc(a - 1, -b + 1) }
}
fn tuple((a, b): (usize, usize)) -> usize {
if a == 0 { 1 } else { tuple((a - 1, b + 1)) }
}
fn let_tuple(a: usize, b: usize) -> usize {
let (c, d) = (a, b);
if c == 0 { 1 } else { let_tuple(c - 1, d + 1) }
}
fn array([a, b]: [usize; 2]) -> usize {
if a == 0 { 1 } else { array([a - 1, b + 1]) }
}
fn index(a: usize, mut b: &[usize], c: usize) -> usize {
if a == 0 { 1 } else { index(a - 1, b, c + b[0]) }
}
fn break_(a: usize, mut b: usize, mut c: usize) -> usize {
let c = loop {
b += 1;
c += 1;
if c == 10 {
break b;
}
};
if a == 0 { 1 } else { break_(a - 1, c, c) }
}
// this has a side effect
fn mut_ref(a: usize, b: &mut usize) -> usize {
*b = 1;
if a == 0 { 1 } else { mut_ref(a - 1, b) }
}
fn mut_ref2(a: usize, b: &mut usize) -> usize {
let mut c = *b;
if a == 0 { 1 } else { mut_ref2(a - 1, &mut c) }
}
fn not_primitive(a: usize, b: String) -> usize {
if a == 0 { 1 } else { not_primitive(a - 1, b) }
}
// this doesn't have a side effect,
// but `String` is not primitive.
fn not_primitive_op(a: usize, b: String, c: &str) -> usize {
if a == 1 { 1 } else { not_primitive_op(a, b + c, c) }
}
struct A;
impl A {
fn method(a: usize, b: usize) -> usize {
if a == 0 { 1 } else { A::method(a - 1, b - 1) }
}
fn method2(&self, a: usize, b: usize) -> usize {
if a == 0 { 1 } else { self.method2(a - 1, b + 1) }
}
}
trait B {
fn hello(a: usize, b: usize) -> usize;
fn hello2(&self, a: usize, b: usize) -> usize;
}
impl B for A {
fn hello(a: usize, b: usize) -> usize {
if a == 0 { 1 } else { A::hello(a - 1, b + 1) }
}
fn hello2(&self, a: usize, b: usize) -> usize {
if a == 0 { 1 } else { self.hello2(a - 1, b + 1) }
}
}
trait C {
fn hello(a: usize, b: usize) -> usize {
if a == 0 { 1 } else { Self::hello(a - 1, b + 1) }
}
fn hello2(&self, a: usize, b: usize) -> usize {
if a == 0 { 1 } else { self.hello2(a - 1, b + 1) }
}
}
fn ignore(a: usize, _: usize) -> usize {
if a == 1 { 1 } else { ignore(a - 1, 0) }
}
fn ignore2(a: usize, _b: usize) -> usize {
if a == 1 { 1 } else { ignore2(a - 1, _b) }
}
fn f1(a: u32) -> u32 {
a
}
fn f2(a: u32) -> u32 {
f1(a)
}
fn inner_fn(a: u32) -> u32 {
fn inner_fn(a: u32) -> u32 {
a
}
inner_fn(a)
}
fn main() {}
error: parameter is only used in recursion
--> $DIR/only_used_in_recursion.rs:3:21
|
LL | fn simple(a: usize, b: usize) -> usize {
| ^ help: if this is intentional, prefix with an underscore: `_b`
|
= note: `-D clippy::only-used-in-recursion` implied by `-D warnings`
error: parameter is only used in recursion
--> $DIR/only_used_in_recursion.rs:7:24
|
LL | fn with_calc(a: usize, b: isize) -> usize {
| ^ help: if this is intentional, prefix with an underscore: `_b`
error: parameter is only used in recursion
--> $DIR/only_used_in_recursion.rs:11:14
|
LL | fn tuple((a, b): (usize, usize)) -> usize {
| ^ help: if this is intentional, prefix with an underscore: `_b`
error: parameter is only used in recursion
--> $DIR/only_used_in_recursion.rs:15:24
|
LL | fn let_tuple(a: usize, b: usize) -> usize {
| ^ help: if this is intentional, prefix with an underscore: `_b`
error: parameter is only used in recursion
--> $DIR/only_used_in_recursion.rs:20:14
|
LL | fn array([a, b]: [usize; 2]) -> usize {
| ^ help: if this is intentional, prefix with an underscore: `_b`
error: parameter is only used in recursion
--> $DIR/only_used_in_recursion.rs:24:20
|
LL | fn index(a: usize, mut b: &[usize], c: usize) -> usize {
| ^^^^^ help: if this is intentional, prefix with an underscore: `_b`
error: parameter is only used in recursion
--> $DIR/only_used_in_recursion.rs:24:37
|
LL | fn index(a: usize, mut b: &[usize], c: usize) -> usize {
| ^ help: if this is intentional, prefix with an underscore: `_c`
error: parameter is only used in recursion
--> $DIR/only_used_in_recursion.rs:28:21
|
LL | fn break_(a: usize, mut b: usize, mut c: usize) -> usize {
| ^^^^^ help: if this is intentional, prefix with an underscore: `_b`
error: parameter is only used in recursion
--> $DIR/only_used_in_recursion.rs:46:23
|
LL | fn mut_ref2(a: usize, b: &mut usize) -> usize {
| ^ help: if this is intentional, prefix with an underscore: `_b`
error: parameter is only used in recursion
--> $DIR/only_used_in_recursion.rs:51:28
|
LL | fn not_primitive(a: usize, b: String) -> usize {
| ^ help: if this is intentional, prefix with an underscore: `_b`
error: parameter is only used in recursion
--> $DIR/only_used_in_recursion.rs:68:33
|
LL | fn method2(&self, a: usize, b: usize) -> usize {
| ^ help: if this is intentional, prefix with an underscore: `_b`
error: parameter is only used in recursion
--> $DIR/only_used_in_recursion.rs:90:24
|
LL | fn hello(a: usize, b: usize) -> usize {
| ^ help: if this is intentional, prefix with an underscore: `_b`
error: parameter is only used in recursion
--> $DIR/only_used_in_recursion.rs:94:32
|
LL | fn hello2(&self, a: usize, b: usize) -> usize {
| ^ help: if this is intentional, prefix with an underscore: `_b`
error: aborting due to 13 previous errors
// run-rustfix // run-rustfix
#![allow(unused_parens)] #![allow(unused_parens)]
#![allow(clippy::iter_with_drain)]
fn f() -> usize { fn f() -> usize {
42 42
} }
......
// run-rustfix // run-rustfix
#![allow(unused_parens)] #![allow(unused_parens)]
#![allow(clippy::iter_with_drain)]
fn f() -> usize { fn f() -> usize {
42 42
} }
......
#![warn(clippy::temporary_assignment)] #![warn(clippy::temporary_assignment)]
#![allow(const_item_mutation)]
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
......
error: assignment to temporary error: assignment to temporary
--> $DIR/temporary_assignment.rs:48:5 --> $DIR/temporary_assignment.rs:47:5
| |
LL | Struct { field: 0 }.field = 1; LL | Struct { field: 0 }.field = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...@@ -7,7 +7,7 @@ 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` = note: `-D clippy::temporary-assignment` implied by `-D warnings`
error: assignment to temporary error: assignment to temporary
--> $DIR/temporary_assignment.rs:49:5 --> $DIR/temporary_assignment.rs:48:5
| |
LL | / MultiStruct { LL | / MultiStruct {
LL | | structure: Struct { field: 0 }, LL | | structure: Struct { field: 0 },
...@@ -17,13 +17,13 @@ LL | | .field = 1; ...@@ -17,13 +17,13 @@ LL | | .field = 1;
| |______________^ | |______________^
error: assignment to temporary error: assignment to temporary
--> $DIR/temporary_assignment.rs:54:5 --> $DIR/temporary_assignment.rs:53:5
| |
LL | ArrayStruct { array: [0] }.array[0] = 1; LL | ArrayStruct { array: [0] }.array[0] = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: assignment to temporary error: assignment to temporary
--> $DIR/temporary_assignment.rs:55:5 --> $DIR/temporary_assignment.rs:54:5
| |
LL | (0, 0).0 = 1; LL | (0, 0).0 = 1;
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
......
...@@ -25,8 +25,8 @@ fn main() { ...@@ -25,8 +25,8 @@ fn main() {
let slice_ptr = &[0, 1, 2, 3] as *const [i32]; let slice_ptr = &[0, 1, 2, 3] as *const [i32];
// ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast
let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u16] }; let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u32] };
let _ptr_to_unsized = slice_ptr as *const [u16]; let _ptr_to_unsized = slice_ptr as *const [u32];
// TODO: We could try testing vtable casts here too, but maybe // TODO: We could try testing vtable casts here too, but maybe
// we should wait until std::raw::TraitObject is stabilized? // we should wait until std::raw::TraitObject is stabilized?
......
...@@ -25,8 +25,8 @@ fn main() { ...@@ -25,8 +25,8 @@ fn main() {
let slice_ptr = &[0, 1, 2, 3] as *const [i32]; let slice_ptr = &[0, 1, 2, 3] as *const [i32];
// ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast
let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) }; let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u32]>(slice_ptr) };
let _ptr_to_unsized = slice_ptr as *const [u16]; let _ptr_to_unsized = slice_ptr as *const [u32];
// TODO: We could try testing vtable casts here too, but maybe // TODO: We could try testing vtable casts here too, but maybe
// we should wait until std::raw::TraitObject is stabilized? // we should wait until std::raw::TraitObject is stabilized?
......
...@@ -17,8 +17,8 @@ LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr ...@@ -17,8 +17,8 @@ LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr
error: transmute from a pointer to a pointer error: transmute from a pointer to a pointer
--> $DIR/transmutes_expressible_as_ptr_casts.rs:28:46 --> $DIR/transmutes_expressible_as_ptr_casts.rs:28:46
| |
LL | let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) }; LL | let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u32]>(slice_ptr) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u32]`
error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead
--> $DIR/transmutes_expressible_as_ptr_casts.rs:34:50 --> $DIR/transmutes_expressible_as_ptr_casts.rs:34:50
......
#![allow(dead_code)]
fn main() { fn main() {
let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None });
let _ = (0..4).filter_map(|x| { let _ = (0..4).filter_map(|x| {
...@@ -19,3 +21,130 @@ fn main() { ...@@ -19,3 +21,130 @@ fn main() {
fn filter_map_none_changes_item_type() -> impl Iterator<Item = bool> { fn filter_map_none_changes_item_type() -> impl Iterator<Item = bool> {
"".chars().filter_map(|_| None) "".chars().filter_map(|_| None)
} }
// https://github.com/rust-lang/rust-clippy/issues/4433#issue-483920107
mod comment_483920107 {
enum Severity {
Warning,
Other,
}
struct ServerError;
impl ServerError {
fn severity(&self) -> Severity {
Severity::Warning
}
}
struct S {
warnings: Vec<ServerError>,
}
impl S {
fn foo(&mut self, server_errors: Vec<ServerError>) {
#[allow(unused_variables)]
let errors: Vec<ServerError> = server_errors
.into_iter()
.filter_map(|se| match se.severity() {
Severity::Warning => {
self.warnings.push(se);
None
},
_ => Some(se),
})
.collect();
}
}
}
// https://github.com/rust-lang/rust-clippy/issues/4433#issuecomment-611006622
mod comment_611006622 {
struct PendingRequest {
reply_to: u8,
token: u8,
expires: u8,
group_id: u8,
}
enum Value {
Null,
}
struct Node;
impl Node {
fn send_response(&self, _reply_to: u8, _token: u8, _value: Value) -> &Self {
self
}
fn on_error_warn(&self) -> &Self {
self
}
}
struct S {
pending_requests: Vec<PendingRequest>,
}
impl S {
fn foo(&mut self, node: Node, now: u8, group_id: u8) {
// "drain_filter"
self.pending_requests = self
.pending_requests
.drain(..)
.filter_map(|pending| {
if pending.expires <= now {
return None; // Expired, remove
}
if pending.group_id == group_id {
// Matched - reuse strings and remove
node.send_response(pending.reply_to, pending.token, Value::Null)
.on_error_warn();
None
} else {
// Keep waiting
Some(pending)
}
})
.collect();
}
}
}
// https://github.com/rust-lang/rust-clippy/issues/4433#issuecomment-621925270
// This extrapolation doesn't reproduce the false positive. Additional context seems necessary.
mod comment_621925270 {
struct Signature(u8);
fn foo(sig_packets: impl Iterator<Item = Result<Signature, ()>>) -> impl Iterator<Item = u8> {
sig_packets.filter_map(|res| match res {
Ok(Signature(sig_packet)) => Some(sig_packet),
_ => None,
})
}
}
// https://github.com/rust-lang/rust-clippy/issues/4433#issuecomment-1052978898
mod comment_1052978898 {
#![allow(clippy::redundant_closure)]
pub struct S(u8);
impl S {
pub fn consume(self) {
println!("yum");
}
}
pub fn filter_owned() -> impl Iterator<Item = S> {
(0..10).map(|i| S(i)).filter_map(|s| {
if s.0 & 1 == 0 {
s.consume();
None
} else {
Some(s)
}
})
}
}
error: this `.filter_map` can be written more simply using `.filter` error: this `.filter_map` can be written more simply using `.filter`
--> $DIR/unnecessary_filter_map.rs:2:13 --> $DIR/unnecessary_filter_map.rs:4:13
| |
LL | let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); LL | let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...@@ -7,7 +7,7 @@ LL | let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None }); ...@@ -7,7 +7,7 @@ LL | let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None });
= note: `-D clippy::unnecessary-filter-map` implied by `-D warnings` = note: `-D clippy::unnecessary-filter-map` implied by `-D warnings`
error: this `.filter_map` can be written more simply using `.filter` error: this `.filter_map` can be written more simply using `.filter`
--> $DIR/unnecessary_filter_map.rs:3:13 --> $DIR/unnecessary_filter_map.rs:5:13
| |
LL | let _ = (0..4).filter_map(|x| { LL | let _ = (0..4).filter_map(|x| {
| _____________^ | _____________^
...@@ -19,7 +19,7 @@ LL | | }); ...@@ -19,7 +19,7 @@ LL | | });
| |______^ | |______^
error: this `.filter_map` can be written more simply using `.filter` error: this `.filter_map` can be written more simply using `.filter`
--> $DIR/unnecessary_filter_map.rs:9:13 --> $DIR/unnecessary_filter_map.rs:11:13
| |
LL | let _ = (0..4).filter_map(|x| match x { LL | let _ = (0..4).filter_map(|x| match x {
| _____________^ | _____________^
...@@ -29,7 +29,7 @@ LL | | }); ...@@ -29,7 +29,7 @@ LL | | });
| |______^ | |______^
error: this `.filter_map` can be written more simply using `.map` error: this `.filter_map` can be written more simply using `.map`
--> $DIR/unnecessary_filter_map.rs:14:13 --> $DIR/unnecessary_filter_map.rs:16:13
| |
LL | let _ = (0..4).filter_map(|x| Some(x + 1)); LL | let _ = (0..4).filter_map(|x| Some(x + 1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
......
#![allow(dead_code)]
fn main() {
let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None });
let _ = (0..4).find_map(|x| {
if x > 1 {
return Some(x);
};
None
});
let _ = (0..4).find_map(|x| match x {
0 | 1 => None,
_ => Some(x),
});
let _ = (0..4).find_map(|x| Some(x + 1));
let _ = (0..4).find_map(i32::checked_abs);
}
fn find_map_none_changes_item_type() -> Option<bool> {
"".chars().find_map(|_| None)
}
error: this `.find_map` can be written more simply using `.find`
--> $DIR/unnecessary_find_map.rs:4:13
|
LL | let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::unnecessary-find-map` implied by `-D warnings`
error: this `.find_map` can be written more simply using `.find`
--> $DIR/unnecessary_find_map.rs:5:13
|
LL | let _ = (0..4).find_map(|x| {
| _____________^
LL | | if x > 1 {
LL | | return Some(x);
LL | | };
LL | | None
LL | | });
| |______^
error: this `.find_map` can be written more simply using `.find`
--> $DIR/unnecessary_find_map.rs:11:13
|
LL | let _ = (0..4).find_map(|x| match x {
| _____________^
LL | | 0 | 1 => None,
LL | | _ => Some(x),
LL | | });
| |______^
error: this `.find_map` can be written more simply using `.map(..).next()`
--> $DIR/unnecessary_find_map.rs:16:13
|
LL | let _ = (0..4).find_map(|x| Some(x + 1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 4 previous errors
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
#![allow(clippy::stable_sort_primitive)] #![allow(clippy::stable_sort_primitive)]
use std::cell::Ref; use std::cell::Ref;
use std::cmp::Reverse;
fn unnecessary_sort_by() { fn unnecessary_sort_by() {
fn id(x: isize) -> isize { fn id(x: isize) -> isize {
...@@ -18,8 +17,8 @@ fn unnecessary_sort_by() { ...@@ -18,8 +17,8 @@ fn unnecessary_sort_by() {
vec.sort_unstable_by_key(|a| id(-a)); vec.sort_unstable_by_key(|a| id(-a));
// Reverse examples // Reverse examples
vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow
vec.sort_by_key(|b| Reverse((b + 5).abs())); vec.sort_by_key(|b| std::cmp::Reverse((b + 5).abs()));
vec.sort_unstable_by_key(|b| Reverse(id(-b))); vec.sort_unstable_by_key(|b| std::cmp::Reverse(id(-b)));
// Negative examples (shouldn't be changed) // Negative examples (shouldn't be changed)
let c = &7; let c = &7;
vec.sort_by(|a, b| (b - a).cmp(&(a - b))); vec.sort_by(|a, b| (b - a).cmp(&(a - b)));
...@@ -76,7 +75,6 @@ mod issue_5754 { ...@@ -76,7 +75,6 @@ mod issue_5754 {
// The closure parameter is not dereferenced anymore, so non-Copy types can be linted // The closure parameter is not dereferenced anymore, so non-Copy types can be linted
mod issue_6001 { mod issue_6001 {
use super::*;
struct Test(String); struct Test(String);
impl Test { impl Test {
...@@ -93,8 +91,8 @@ mod issue_6001 { ...@@ -93,8 +91,8 @@ mod issue_6001 {
args.sort_by_key(|a| a.name()); args.sort_by_key(|a| a.name());
args.sort_unstable_by_key(|a| a.name()); args.sort_unstable_by_key(|a| a.name());
// Reverse // Reverse
args.sort_by_key(|b| Reverse(b.name())); args.sort_by_key(|b| std::cmp::Reverse(b.name()));
args.sort_unstable_by_key(|b| Reverse(b.name())); args.sort_unstable_by_key(|b| std::cmp::Reverse(b.name()));
} }
} }
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
#![allow(clippy::stable_sort_primitive)] #![allow(clippy::stable_sort_primitive)]
use std::cell::Ref; use std::cell::Ref;
use std::cmp::Reverse;
fn unnecessary_sort_by() { fn unnecessary_sort_by() {
fn id(x: isize) -> isize { fn id(x: isize) -> isize {
...@@ -76,7 +75,6 @@ pub fn test() { ...@@ -76,7 +75,6 @@ pub fn test() {
// The closure parameter is not dereferenced anymore, so non-Copy types can be linted // The closure parameter is not dereferenced anymore, so non-Copy types can be linted
mod issue_6001 { mod issue_6001 {
use super::*;
struct Test(String); struct Test(String);
impl Test { impl Test {
......
error: use Vec::sort here instead error: use Vec::sort here instead
--> $DIR/unnecessary_sort_by.rs:15:5 --> $DIR/unnecessary_sort_by.rs:14:5
| |
LL | vec.sort_by(|a, b| a.cmp(b)); LL | vec.sort_by(|a, b| a.cmp(b));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()`
...@@ -7,70 +7,70 @@ LL | vec.sort_by(|a, b| a.cmp(b)); ...@@ -7,70 +7,70 @@ LL | vec.sort_by(|a, b| a.cmp(b));
= note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings`
error: use Vec::sort here instead error: use Vec::sort here instead
--> $DIR/unnecessary_sort_by.rs:16:5 --> $DIR/unnecessary_sort_by.rs:15:5
| |
LL | vec.sort_unstable_by(|a, b| a.cmp(b)); LL | vec.sort_unstable_by(|a, b| a.cmp(b));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable()` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable()`
error: use Vec::sort_by_key here instead error: use Vec::sort_by_key here instead
--> $DIR/unnecessary_sort_by.rs:17:5 --> $DIR/unnecessary_sort_by.rs:16:5
| |
LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (a + 5).abs())` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (a + 5).abs())`
error: use Vec::sort_by_key here instead error: use Vec::sort_by_key here instead
--> $DIR/unnecessary_sort_by.rs:18:5 --> $DIR/unnecessary_sort_by.rs:17:5
| |
LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b)));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| id(-a))` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| id(-a))`
error: use Vec::sort_by_key here instead error: use Vec::sort_by_key here instead
--> $DIR/unnecessary_sort_by.rs:21:5 --> $DIR/unnecessary_sort_by.rs:20:5
| |
LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|b| Reverse((b + 5).abs()))` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|b| std::cmp::Reverse((b + 5).abs()))`
error: use Vec::sort_by_key here instead error: use Vec::sort_by_key here instead
--> $DIR/unnecessary_sort_by.rs:22:5 --> $DIR/unnecessary_sort_by.rs:21:5
| |
LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a)));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|b| Reverse(id(-b)))` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|b| std::cmp::Reverse(id(-b)))`
error: use Vec::sort_by_key here instead error: use Vec::sort_by_key here instead
--> $DIR/unnecessary_sort_by.rs:32:5 --> $DIR/unnecessary_sort_by.rs:31:5
| |
LL | vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); LL | vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (***a).abs())` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (***a).abs())`
error: use Vec::sort_by_key here instead error: use Vec::sort_by_key here instead
--> $DIR/unnecessary_sort_by.rs:33:5 --> $DIR/unnecessary_sort_by.rs:32:5
| |
LL | vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); LL | vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| (***a).abs())` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| (***a).abs())`
error: use Vec::sort_by_key here instead error: use Vec::sort_by_key here instead
--> $DIR/unnecessary_sort_by.rs:93:9 --> $DIR/unnecessary_sort_by.rs:91:9
| |
LL | args.sort_by(|a, b| a.name().cmp(&b.name())); LL | args.sort_by(|a, b| a.name().cmp(&b.name()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|a| a.name())` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|a| a.name())`
error: use Vec::sort_by_key here instead error: use Vec::sort_by_key here instead
--> $DIR/unnecessary_sort_by.rs:94:9 --> $DIR/unnecessary_sort_by.rs:92:9
| |
LL | args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); LL | args.sort_unstable_by(|a, b| a.name().cmp(&b.name()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|a| a.name())` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|a| a.name())`
error: use Vec::sort_by_key here instead error: use Vec::sort_by_key here instead
--> $DIR/unnecessary_sort_by.rs:96:9 --> $DIR/unnecessary_sort_by.rs:94:9
| |
LL | args.sort_by(|a, b| b.name().cmp(&a.name())); LL | args.sort_by(|a, b| b.name().cmp(&a.name()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|b| Reverse(b.name()))` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|b| std::cmp::Reverse(b.name()))`
error: use Vec::sort_by_key here instead error: use Vec::sort_by_key here instead
--> $DIR/unnecessary_sort_by.rs:97:9 --> $DIR/unnecessary_sort_by.rs:95:9
| |
LL | args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); LL | args.sort_unstable_by(|a, b| b.name().cmp(&a.name()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|b| Reverse(b.name()))` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|b| std::cmp::Reverse(b.name()))`
error: aborting due to 12 previous errors error: aborting due to 12 previous errors
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// aux-build:proc_macro_derive.rs // aux-build:proc_macro_derive.rs
#![warn(clippy::use_self)] #![warn(clippy::use_self)]
#![allow(dead_code)] #![allow(dead_code, unreachable_code)]
#![allow( #![allow(
clippy::should_implement_trait, clippy::should_implement_trait,
clippy::upper_case_acronyms, clippy::upper_case_acronyms,
...@@ -519,3 +519,26 @@ mod self_is_ty_param { ...@@ -519,3 +519,26 @@ mod self_is_ty_param {
} }
} }
} }
mod use_self_in_pat {
enum Foo {
Bar,
Baz,
}
impl Foo {
fn do_stuff(self) {
match self {
Self::Bar => unimplemented!(),
Self::Baz => unimplemented!(),
}
match Some(1) {
Some(_) => unimplemented!(),
None => unimplemented!(),
}
if let Self::Bar = self {
unimplemented!()
}
}
}
}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// aux-build:proc_macro_derive.rs // aux-build:proc_macro_derive.rs
#![warn(clippy::use_self)] #![warn(clippy::use_self)]
#![allow(dead_code)] #![allow(dead_code, unreachable_code)]
#![allow( #![allow(
clippy::should_implement_trait, clippy::should_implement_trait,
clippy::upper_case_acronyms, clippy::upper_case_acronyms,
...@@ -519,3 +519,26 @@ fn test() { ...@@ -519,3 +519,26 @@ fn test() {
} }
} }
} }
mod use_self_in_pat {
enum Foo {
Bar,
Baz,
}
impl Foo {
fn do_stuff(self) {
match self {
Foo::Bar => unimplemented!(),
Foo::Baz => unimplemented!(),
}
match Some(1) {
Some(_) => unimplemented!(),
None => unimplemented!(),
}
if let Foo::Bar = self {
unimplemented!()
}
}
}
}
...@@ -168,5 +168,23 @@ error: unnecessary structure name repetition ...@@ -168,5 +168,23 @@ error: unnecessary structure name repetition
LL | S2::new() LL | S2::new()
| ^^ help: use the applicable keyword: `Self` | ^^ help: use the applicable keyword: `Self`
error: aborting due to 28 previous errors error: unnecessary structure name repetition
--> $DIR/use_self.rs:532:17
|
LL | Foo::Bar => unimplemented!(),
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
--> $DIR/use_self.rs:533:17
|
LL | Foo::Baz => unimplemented!(),
| ^^^ help: use the applicable keyword: `Self`
error: unnecessary structure name repetition
--> $DIR/use_self.rs:539:20
|
LL | if let Foo::Bar = self {
| ^^^ help: use the applicable keyword: `Self`
error: aborting due to 31 previous errors
...@@ -25,7 +25,56 @@ Otherwise, have a great day =^.^= ...@@ -25,7 +25,56 @@ Otherwise, have a great day =^.^=
blockquote { font-size: 1em; } blockquote { font-size: 1em; }
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; } [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; }
.form-inline .checkbox { margin-right: 0.6em } .dropdown-menu {
color: var(--fg);
background: var(--theme-popup-bg);
border: 1px solid var(--theme-popup-border);
}
.dropdown-menu .divider {
background-color: var(--theme-popup-border);
}
.dropdown-menu .checkbox {
display: block;
white-space: nowrap;
margin: 0;
}
.dropdown-menu .checkbox label {
padding: 3px 20px;
width: 100%;
}
.dropdown-menu .checkbox input {
position: relative;
margin: 0 0.5rem 0;
padding: 0;
}
.dropdown-menu .checkbox:hover {
background-color: var(--theme-hover);
}
div.panel div.panel-body button.dropdown-toggle {
background: var(--searchbar-bg);
color: var(--searchbar-fg);
border-color: var(--theme-popup-border);
}
div.panel div.panel-body button.dropdown-toggle:hover {
box-shadow: 0 0 3px var(--searchbar-shadow-color);
}
div.panel div.panel-body .open button.dropdown-toggle {
background: var(--searchbar-bg);
color: var(--searchbar-fg);
border-color: var(--theme-popup-border);
filter: brightness(90%);
}
.dropdown-toggle .badge {
background-color: #777;
}
.panel-heading { cursor: pointer; } .panel-heading { cursor: pointer; }
...@@ -38,6 +87,16 @@ Otherwise, have a great day =^.^= ...@@ -38,6 +87,16 @@ Otherwise, have a great day =^.^=
.panel .panel-title-name .anchor { display: none; } .panel .panel-title-name .anchor { display: none; }
.panel:hover .panel-title-name .anchor { display: inline;} .panel:hover .panel-title-name .anchor { display: inline;}
.search-control {
margin-top: 15px;
}
@media (min-width: 992px) {
.search-control {
margin-top: 0;
}
}
.label { .label {
padding-top: 0.3em; padding-top: 0.3em;
padding-bottom: 0.3em; padding-bottom: 0.3em;
...@@ -143,13 +202,17 @@ Otherwise, have a great day =^.^= ...@@ -143,13 +202,17 @@ Otherwise, have a great day =^.^=
--inline-code-bg: #191f26; --inline-code-bg: #191f26;
} }
.theme-dropdown {
position: absolute;
margin: 0.7em;
z-index: 10;
}
/* Applying the mdBook theme */ /* Applying the mdBook theme */
.theme-icon { .theme-icon {
position: absolute;
text-align: center; text-align: center;
width: 2em; width: 2em;
height: 2em; height: 2em;
margin: 0.7em;
line-height: 2em; line-height: 2em;
border: solid 1px var(--icons); border: solid 1px var(--icons);
border-radius: 5px; border-radius: 5px;
...@@ -160,23 +223,28 @@ Otherwise, have a great day =^.^= ...@@ -160,23 +223,28 @@ Otherwise, have a great day =^.^=
background: var(--theme-hover); background: var(--theme-hover);
} }
.theme-choice { .theme-choice {
position: absolute; display: none;
margin-top: calc(2em + 0.7em);
margin-left: 0.7em;
list-style: none; list-style: none;
border: 1px solid var(--theme-popup-border); border: 1px solid var(--theme-popup-border);
border-radius: 5px; border-radius: 5px;
color: var(--fg); color: var(--fg);
background: var(--theme-popup-bg); background: var(--theme-popup-bg);
padding: 0 0; padding: 0 0;
overflow: hidden;
} }
.theme-dropdown.open .theme-choice {
display: block;
}
.theme-choice > li { .theme-choice > li {
padding: 5px 10px; padding: 5px 10px;
font-size: 0.8em; font-size: 0.8em;
user-select: none; user-select: none;
cursor: pointer; cursor: pointer;
} }
.theme-choice > li:hover {
.theme-choice>li:hover {
background: var(--theme-hover); background: var(--theme-hover);
} }
...@@ -240,17 +308,15 @@ Otherwise, have a great day =^.^= ...@@ -240,17 +308,15 @@ Otherwise, have a great day =^.^=
</style> </style>
</head> </head>
<body> <body ng-app="clippy" ng-controller="lintList">
<div id="theme-icon" class="theme-icon">&#128396;</div> <div theme-dropdown class="theme-dropdown">
<ul id="theme-menu" class="theme-choice" style="display: none;"> <div id="theme-icon" class="theme-icon">&#128396;</div>
<li id="light">Light</li> <ul id="theme-menu" class="theme-choice">
<li id="rust">Rust</li> <li id="{{id}}" ng-repeat="(id, name) in themes" ng-click="selectTheme(id)">{{name}}</li>
<li id="coal">Coal</li> </ul>
<li id="navy">Navy</li> </div>
<li id="ayu">Ayu</li>
</ul> <div class="container">
<div class="container" ng-app="clippy" ng-controller="lintList">
<div class="page-header"> <div class="page-header">
<h1>Clippy Lints</h1> <h1>Clippy Lints</h1>
</div> </div>
...@@ -271,38 +337,62 @@ Otherwise, have a great day =^.^= ...@@ -271,38 +337,62 @@ Otherwise, have a great day =^.^=
</div> </div>
<div class="panel panel-default" ng-show="data"> <div class="panel panel-default" ng-show="data">
<div class="panel-body row filter-panel"> <div class="panel-body row">
<div class="col-md-6 form-inline"> <div class="col-12 col-md-4">
<div class="form-group form-group-lg"> <div class="btn-group" filter-dropdown>
<p class="h4"> <button type="button" class="btn btn-default dropdown-toggle">
Lint levels Lint levels <span class="badge">{{selectedValuesCount(levels)}}</span> <span class="caret"></span>
<a href="https://doc.rust-lang.org/rustc/lints/levels.html">(?)</a> </button>
</p> <ul class="dropdown-menu">
<div class="checkbox" ng-repeat="(level, enabled) in levels"> <li class="checkbox">
<label class="text-capitalize"> <label ng-click="toggleLevels(true)">
<input type="checkbox" ng-model="levels[level]" /> <input type="checkbox" class="invisible" />
{{level}} All
</label> </label>
</div> </li>
<li class="checkbox">
<label ng-click="toggleLevels(false)">
<input type="checkbox" class="invisible" />
None
</label>
</li>
<li role="separator" class="divider"></li>
<li class="checkbox" ng-repeat="(level, enabled) in levels">
<label class="text-capitalize">
<input type="checkbox" ng-model="levels[level]" />
{{level}}
</label>
</li>
</ul>
</div> </div>
</div> <div class="btn-group" filter-dropdown>
<div class="col-md-6 form-inline"> <button type="button" class="btn btn-default dropdown-toggle">
<div class="form-group form-group-lg"> Lint groups <span class="badge">{{selectedValuesCount(groups)}}</span> <span class="caret"></span>
<p class="h4"> </button>
Lint groups <ul class="dropdown-menu">
<a href="https://github.com/rust-lang/rust-clippy/#clippy">(?)</a> <li class="checkbox">
</p> <label ng-click="toggleGroups(true)">
<div class="checkbox" ng-repeat="(group, enabled) in groups"> <input type="checkbox" class="invisible" />
<label class="text-capitalize"> All
<input type="checkbox" ng-model="groups[group]" /> </label>
{{group}} </li>
</label> <li class="checkbox">
</div> <label ng-click="toggleGroups(false)">
<input type="checkbox" class="invisible" />
None
</label>
</li>
<li role="separator" class="divider"></li>
<li class="checkbox" ng-repeat="(group, enabled) in groups">
<label class="text-capitalize">
<input type="checkbox" ng-model="groups[group]" />
{{group}}
</label>
</li>
</ul>
</div> </div>
</div> </div>
</div> <div class="col-12 col-md-8 search-control">
<div class="panel-body row">
<div class="col-md-12 form-horizontal">
<div class="input-group"> <div class="input-group">
<label class="input-group-addon" id="filter-label" for="filter-input">Filter:</label> <label class="input-group-addon" id="filter-label" for="filter-input">Filter:</label>
<input type="text" class="form-control" placeholder="Keywords or search string" id="filter-input" ng-model="search" ng-model-options="{debounce: 50}"/> <input type="text" class="form-control" placeholder="Keywords or search string" id="filter-input" ng-model="search" ng-model-options="{debounce: 50}"/>
...@@ -336,7 +426,7 @@ Otherwise, have a great day =^.^= ...@@ -336,7 +426,7 @@ Otherwise, have a great day =^.^=
</h2> </h2>
</header> </header>
<div class="list-group lint-docs" ng-class="{collapse: true, in: open[lint.id]}"> <div class="list-group lint-docs" ng-if="open[lint.id]" ng-class="{collapse: true, in: open[lint.id]}">
<div class="list-group-item lint-doc-md" ng-bind-html="lint.docs | markdown"></div> <div class="list-group-item lint-doc-md" ng-bind-html="lint.docs | markdown"></div>
<div class="lint-additional-info-container"> <div class="lint-additional-info-container">
<!-- Applicability --> <!-- Applicability -->
...@@ -365,7 +455,7 @@ Otherwise, have a great day =^.^= ...@@ -365,7 +455,7 @@ Otherwise, have a great day =^.^=
</div> </div>
<a href="https://github.com/rust-lang/rust-clippy"> <a href="https://github.com/rust-lang/rust-clippy">
<img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on Github"/> <img style="position: absolute; top: 0; right: 0; border: 0; clip-path: polygon(0% 0%, 100% 0%, 100% 100%);" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on Github"/>
</a> </a>
<script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/12.3.2/markdown-it.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/12.3.2/markdown-it.min.js"></script>
...@@ -429,6 +519,46 @@ Otherwise, have a great day =^.^= ...@@ -429,6 +519,46 @@ Otherwise, have a great day =^.^=
); );
}; };
}) })
.directive('themeDropdown', function ($document) {
return {
restrict: 'A',
link: function ($scope, $element, $attr) {
$element.bind('click', function () {
$element.toggleClass('open');
$element.addClass('open-recent');
});
$document.bind('click', function () {
if (!$element.hasClass('open-recent')) {
$element.removeClass('open');
}
$element.removeClass('open-recent');
})
}
}
})
.directive('filterDropdown', function ($document) {
return {
restrict: 'A',
link: function ($scope, $element, $attr) {
$element.bind('click', function (event) {
if (event.target.closest('button')) {
$element.toggleClass('open');
} else {
$element.addClass('open');
}
$element.addClass('open-recent');
});
$document.bind('click', function () {
if (!$element.hasClass('open-recent')) {
$element.removeClass('open');
}
$element.removeClass('open-recent');
})
}
}
})
.directive('onFinishRender', function ($timeout) { .directive('onFinishRender', function ($timeout) {
return { return {
restrict: 'A', restrict: 'A',
...@@ -462,6 +592,38 @@ Otherwise, have a great day =^.^= ...@@ -462,6 +592,38 @@ Otherwise, have a great day =^.^=
suspicious: true, suspicious: true,
}; };
$scope.groups = GROUPS_FILTER_DEFAULT; $scope.groups = GROUPS_FILTER_DEFAULT;
const THEMES_DEFAULT = {
light: "Light",
rust: "Rust",
coal: "Coal",
navy: "Navy",
ayu: "Ayu"
};
$scope.themes = THEMES_DEFAULT;
$scope.selectTheme = function (theme) {
setTheme(theme, true);
}
$scope.toggleLevels = function (value) {
const levels = $scope.levels;
for (const key in levels) {
if (levels.hasOwnProperty(key)) {
levels[key] = value;
}
}
};
$scope.toggleGroups = function (value) {
const groups = $scope.groups;
for (const key in groups) {
if (groups.hasOwnProperty(key)) {
groups[key] = value;
}
}
};
$scope.selectedValuesCount = function (obj) {
return Object.values(obj).filter(x => x).length;
}
$scope.byGroups = function (lint) { $scope.byGroups = function (lint) {
return $scope.groups[lint.group]; return $scope.groups[lint.group];
}; };
...@@ -558,28 +720,6 @@ Otherwise, have a great day =^.^= ...@@ -558,28 +720,6 @@ Otherwise, have a great day =^.^=
} }
} }
function setupListeners() {
let themeIcon = document.getElementById("theme-icon");
let themeMenu = document.getElementById("theme-menu");
themeIcon.addEventListener("click", function(e) {
if (themeMenu.style.display == "none") {
themeMenu.style.display = "block";
} else {
themeMenu.style.display = "none";
}
});
let children = themeMenu.children;
for (let index = 0; index < children.length; index++) {
let child = children[index];
child.addEventListener("click", function(e) {
setTheme(child.id, true);
});
}
}
setupListeners();
function setTheme(theme, store) { function setTheme(theme, store) {
let enableHighlight = false; let enableHighlight = false;
let enableNight = false; let enableNight = false;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册