1
Fork 0

Rollup merge of #136645 - flip1995:clippy-subtree-update, r=Manishearth

Clippy subtree update

r? `@Manishearth`
This commit is contained in:
Matthias Krüger 2025-02-06 21:56:28 +01:00 committed by GitHub
commit b1be2d5494
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
106 changed files with 2681 additions and 566 deletions

View file

@ -5765,6 +5765,7 @@ Released 2018-09-13
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
[`manual_ok_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_err
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
[`manual_option_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_option_as_slice
[`manual_pattern_char_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
[`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns
@ -5773,6 +5774,7 @@ Released 2018-09-13
[`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain
[`manual_rotate`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rotate
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
[`manual_slice_fill`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_fill
[`manual_slice_size_calculation`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_size_calculation
[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
@ -5954,6 +5956,7 @@ Released 2018-09-13
[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
[`precedence_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence_bits
[`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl
[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
@ -6026,6 +6029,7 @@ Released 2018-09-13
[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else
[`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err
[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used
[`return_and_then`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_and_then
[`return_self_not_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use
[`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop
[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges

View file

@ -5,7 +5,7 @@ To do this, Clippy provides attributes that can be applied to items in the 3rd p
## `#[clippy::format_args]`
_Available since Clippy v1.84_
_Available since Clippy v1.85_
This attribute can be added to a macro that supports `format!`, `println!`, or similar syntax.
It tells Clippy that the macro is a formatting macro, and that the arguments to the macro

View file

@ -752,6 +752,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
* [`manual_rem_euclid`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid)
* [`manual_repeat_n`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n)
* [`manual_retain`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain)
* [`manual_slice_fill`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_fill)
* [`manual_split_once`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once)
* [`manual_str_repeat`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat)
* [`manual_strip`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip)

View file

@ -101,5 +101,18 @@ The `clippy::cargo` group gives you suggestions on how to improve your
your crate and are not sure if you have all useful information in your
`Cargo.toml`.
## Nursery
The `clippy::nursery` group contains lints which are buggy or need more work. It is **not**
recommended to enable the whole group, but rather cherry-pick lints that are useful for your
code base and your use case.
## Deprecated
The `clippy::deprecated` is empty lints that exist to ensure that `#[allow(lintname)]` still
compiles after the lint was deprecated. Deprecation "removes" lints by removing their
functionality and marking them as deprecated, which may cause further warnings but cannot
cause a compiler error.
[Clippy lint documentation]: https://rust-lang.github.io/rust-clippy/
[Clippy 1.0 RFC]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories

View file

@ -621,6 +621,7 @@ define_Conf! {
manual_rem_euclid,
manual_repeat_n,
manual_retain,
manual_slice_fill,
manual_split_once,
manual_str_repeat,
manual_strip,

View file

@ -255,8 +255,9 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
let name_camel = to_camel_case(lint.name);
let name_upper = lint_name.to_uppercase();
result.push_str(&if enable_msrv {
formatdoc!(
if enable_msrv {
let _: fmt::Result = writedoc!(
result,
r"
use clippy_utils::msrvs::{{self, Msrv}};
use clippy_config::Conf;
@ -265,22 +266,24 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
use rustc_session::impl_lint_pass;
"
)
);
} else {
formatdoc!(
let _: fmt::Result = writedoc!(
result,
r"
{pass_import}
use rustc_lint::{{{context_import}, {pass_type}}};
use rustc_session::declare_lint_pass;
"
)
});
);
}
let _: fmt::Result = writeln!(result, "{}", get_lint_declaration(&name_upper, category));
result.push_str(&if enable_msrv {
formatdoc!(
if enable_msrv {
let _: fmt::Result = writedoc!(
result,
r"
pub struct {name_camel} {{
msrv: Msrv,
@ -301,16 +304,17 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
// TODO: Add MSRV level to `clippy_config/src/msrvs.rs` if needed.
// TODO: Update msrv config comment in `clippy_config/src/conf.rs`
"
)
);
} else {
formatdoc!(
let _: fmt::Result = writedoc!(
result,
r"
declare_lint_pass!({name_camel} => [{name_upper}]);
impl {pass_type}{pass_lifetimes} for {name_camel} {{}}
"
)
});
);
}
result
}

View file

@ -985,17 +985,23 @@ mod tests {
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
];
let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
expected.insert("group1".to_string(), vec![
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
]);
expected.insert("group2".to_string(), vec![Lint::new(
"should_assert_eq2",
"group2",
"\"abc\"",
"module_name",
Range::default(),
)]);
expected.insert(
"group1".to_string(),
vec![
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
],
);
expected.insert(
"group2".to_string(),
vec![Lint::new(
"should_assert_eq2",
"group2",
"\"abc\"",
"module_name",
Range::default(),
)],
);
assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
}
}

View file

@ -292,6 +292,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::loops::MANUAL_FIND_INFO,
crate::loops::MANUAL_FLATTEN_INFO,
crate::loops::MANUAL_MEMCPY_INFO,
crate::loops::MANUAL_SLICE_FILL_INFO,
crate::loops::MANUAL_WHILE_LET_SOME_INFO,
crate::loops::MISSING_SPIN_LOOP_INFO,
crate::loops::MUT_RANGE_BOUND_INFO,
@ -321,6 +322,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO,
crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
crate::manual_option_as_slice::MANUAL_OPTION_AS_SLICE_INFO,
crate::manual_range_patterns::MANUAL_RANGE_PATTERNS_INFO,
crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO,
crate::manual_retain::MANUAL_RETAIN_INFO,
@ -463,6 +465,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::methods::REPEAT_ONCE_INFO,
crate::methods::RESULT_FILTER_MAP_INFO,
crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO,
crate::methods::RETURN_AND_THEN_INFO,
crate::methods::SEARCH_IS_SOME_INFO,
crate::methods::SEEK_FROM_CURRENT_INFO,
crate::methods::SEEK_TO_START_INSTEAD_OF_REWIND_INFO,
@ -626,6 +629,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO,
crate::pointers_in_nomem_asm_block::POINTERS_IN_NOMEM_ASM_BLOCK_INFO,
crate::precedence::PRECEDENCE_INFO,
crate::precedence::PRECEDENCE_BITS_INFO,
crate::ptr::CMP_NULL_INFO,
crate::ptr::INVALID_NULL_PTR_USAGE_INFO,
crate::ptr::MUT_FROM_REF_INFO,

View file

@ -291,10 +291,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
&& let Some(ty) = use_node.defined_ty(cx)
&& TyCoercionStability::for_defined_ty(cx, ty, use_node.is_return()).is_deref_stable()
{
self.state = Some((State::ExplicitDeref { mutability: None }, StateData {
first_expr: expr,
adjusted_ty,
}));
self.state = Some((
State::ExplicitDeref { mutability: None },
StateData {
first_expr: expr,
adjusted_ty,
},
));
}
},
RefOp::Method { mutbl, is_ufcs }
@ -456,10 +459,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
&& next_adjust.is_none_or(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
&& iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
{
self.state = Some((State::Borrow { mutability }, StateData {
first_expr: expr,
adjusted_ty,
}));
self.state = Some((
State::Borrow { mutability },
StateData {
first_expr: expr,
adjusted_ty,
},
));
}
},
_ => {},
@ -503,10 +509,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
let stability = state.stability;
report(cx, expr, State::DerefedBorrow(state), data, typeck);
if stability.is_deref_stable() {
self.state = Some((State::Borrow { mutability }, StateData {
first_expr: expr,
adjusted_ty,
}));
self.state = Some((
State::Borrow { mutability },
StateData {
first_expr: expr,
adjusted_ty,
},
));
}
},
(Some((State::DerefedBorrow(state), data)), RefOp::Deref) => {
@ -531,10 +540,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
} else if stability.is_deref_stable()
&& let Some(parent) = get_parent_expr(cx, expr)
{
self.state = Some((State::ExplicitDeref { mutability: None }, StateData {
first_expr: parent,
adjusted_ty,
}));
self.state = Some((
State::ExplicitDeref { mutability: None },
StateData {
first_expr: parent,
adjusted_ty,
},
));
}
},
@ -1124,17 +1136,20 @@ impl<'tcx> Dereferencing<'tcx> {
if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
if let Some(pat) = outer_pat {
// Check for auto-deref
if !matches!(cx.typeck_results().expr_adjustments(e), [
Adjustment {
kind: Adjust::Deref(_),
if !matches!(
cx.typeck_results().expr_adjustments(e),
[
Adjustment {
kind: Adjust::Deref(_),
..
},
Adjustment {
kind: Adjust::Deref(_),
..
},
..
},
Adjustment {
kind: Adjust::Deref(_),
..
},
..
]) {
]
) {
match get_parent_expr(cx, e) {
// Field accesses are the same no matter the number of references.
Some(Expr {

View file

@ -233,10 +233,13 @@ fn check_gaps(cx: &LateContext<'_>, gaps: &[Gap<'_>]) -> bool {
if let Some(owner) = cx.last_node_with_lint_attrs.as_owner() {
let def_id = owner.to_def_id();
let def_descr = cx.tcx.def_descr(def_id);
diag.span_label(cx.tcx.def_span(def_id), match kind {
StopKind::Attr => format!("the attribute applies to this {def_descr}"),
StopKind::Doc(_) => format!("the comment documents this {def_descr}"),
});
diag.span_label(
cx.tcx.def_span(def_id),
match kind {
StopKind::Attr => format!("the attribute applies to this {def_descr}"),
StopKind::Doc(_) => format!("the comment documents this {def_descr}"),
},
);
}
diag.multipart_suggestion_with_style(

View file

@ -6,6 +6,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::Ty;
use rustc_session::declare_lint_pass;
use rustc_span::Symbol;
use std::fmt::Write;
declare_clippy_lint! {
/// ### What it does
@ -183,7 +184,7 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix
help_str.push_str("either of ");
}
help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix)));
write!(help_str, "`{ty}::{}` ", lint.as_name(prefix)).unwrap();
if i != len && !only_one {
help_str.push_str("or ");

View file

@ -1,13 +1,13 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::higher::VecArgs;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::{snippet_opt, snippet_with_applicability};
use clippy_utils::ty::get_type_diagnostic_name;
use clippy_utils::usage::{local_used_after_expr, local_used_in};
use clippy_utils::{
get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id,
};
use rustc_errors::Applicability;
use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Safety, TyKind};
use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{
@ -239,6 +239,11 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
&& !cx.tcx.has_attr(method_def_id, sym::track_caller)
&& check_sig(closure_sig, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder())
{
let mut app = Applicability::MachineApplicable;
let generic_args = match path.args.and_then(GenericArgs::span_ext) {
Some(span) => format!("::{}", snippet_with_applicability(cx, span, "<..>", &mut app)),
None => String::new(),
};
span_lint_and_then(
cx,
REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
@ -251,8 +256,8 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
diag.span_suggestion(
expr.span,
"replace the closure with the method itself",
format!("{}::{}", type_name, path.ident.name),
Applicability::MachineApplicable,
format!("{}::{}{}", type_name, path.ident.name, generic_args),
app,
);
},
);

View file

@ -124,7 +124,9 @@ struct NestingVisitor<'conf, 'cx> {
impl NestingVisitor<'_, '_> {
fn check_indent(&mut self, span: Span, id: NodeId) -> bool {
if self.nest_level > self.conf.excessive_nesting_threshold && !span.in_external_macro(self.cx.sess().source_map()) {
if self.nest_level > self.conf.excessive_nesting_threshold
&& !span.in_external_macro(self.cx.sess().source_map())
{
self.conf.nodes.insert(id);
return true;

View file

@ -11,7 +11,7 @@ declare_clippy_lint! {
/// Detects cases where the result of a `format!` call is
/// appended to an existing `String`.
///
/// ### Why restrict this?
/// ### Why is this bad?
/// Introduces an extra, avoidable heap allocation.
///
/// ### Known problems
@ -35,7 +35,7 @@ declare_clippy_lint! {
/// ```
#[clippy::version = "1.62.0"]
pub FORMAT_PUSH_STRING,
restriction,
pedantic,
"`format!(..)` appended to existing `String`"
}
declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]);

View file

@ -7,8 +7,8 @@ use clippy_utils::macros::macro_backtrace;
use clippy_utils::source::snippet;
use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty;
use rustc_middle::ty::layout::LayoutOf;
use rustc_session::impl_lint_pass;
use rustc_span::{Span, sym};

View file

@ -217,6 +217,7 @@ mod manual_is_power_of_two;
mod manual_let_else;
mod manual_main_separator_str;
mod manual_non_exhaustive;
mod manual_option_as_slice;
mod manual_range_patterns;
mod manual_rem_euclid;
mod manual_retain;
@ -976,5 +977,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern));
store.register_late_pass(|_| Box::<unnecessary_semicolon::UnnecessarySemicolon>::default());
store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf)));
store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf)));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View file

@ -0,0 +1,111 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
use clippy_utils::macros::span_is_local;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{HasSession, snippet_with_applicability};
use clippy_utils::ty::implements_trait;
use clippy_utils::{higher, peel_blocks_with_stmt, span_contains_comment};
use rustc_ast::ast::LitKind;
use rustc_ast::{RangeLimits, UnOp};
use rustc_data_structures::packed::Pu128;
use rustc_errors::Applicability;
use rustc_hir::QPath::Resolved;
use rustc_hir::def::Res;
use rustc_hir::{Expr, ExprKind, Pat};
use rustc_lint::LateContext;
use rustc_span::source_map::Spanned;
use rustc_span::sym;
use super::MANUAL_SLICE_FILL;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
pat: &'tcx Pat<'_>,
arg: &'tcx Expr<'_>,
body: &'tcx Expr<'_>,
expr: &'tcx Expr<'_>,
msrv: &Msrv,
) {
if !msrv.meets(msrvs::SLICE_FILL) {
return;
}
// `for _ in 0..slice.len() { slice[_] = value; }`
if let Some(higher::Range {
start: Some(start),
end: Some(end),
limits: RangeLimits::HalfOpen,
}) = higher::Range::hir(arg)
&& let ExprKind::Lit(Spanned {
node: LitKind::Int(Pu128(0), _),
..
}) = start.kind
&& let ExprKind::Block(..) = body.kind
// Check if the body is an assignment to a slice element.
&& let ExprKind::Assign(assignee, assignval, _) = peel_blocks_with_stmt(body).kind
&& let ExprKind::Index(slice, _, _) = assignee.kind
// Check if `len()` is used for the range end.
&& let ExprKind::MethodCall(path, recv,..) = end.kind
&& path.ident.name == sym::len
// Check if the slice which is being assigned to is the same as the one being iterated over.
&& let ExprKind::Path(Resolved(_, recv_path)) = recv.kind
&& let ExprKind::Path(Resolved(_, slice_path)) = slice.kind
&& recv_path.res == slice_path.res
&& !assignval.span.from_expansion()
// It is generally not equivalent to use the `fill` method if `assignval` can have side effects
&& switch_to_eager_eval(cx, assignval)
&& span_is_local(assignval.span)
// The `fill` method requires that the slice's element type implements the `Clone` trait.
&& let Some(clone_trait) = cx.tcx.lang_items().clone_trait()
&& implements_trait(cx, cx.typeck_results().expr_ty(slice), clone_trait, &[])
{
sugg(cx, body, expr, slice.span, assignval.span);
}
// `for _ in &mut slice { *_ = value; }`
else if let ExprKind::AddrOf(_, _, recv) = arg.kind
// Check if the body is an assignment to a slice element.
&& let ExprKind::Assign(assignee, assignval, _) = peel_blocks_with_stmt(body).kind
&& let ExprKind::Unary(UnOp::Deref, slice_iter) = assignee.kind
&& let ExprKind::Path(Resolved(_, recv_path)) = recv.kind
// Check if the slice which is being assigned to is the same as the one being iterated over.
&& let ExprKind::Path(Resolved(_, slice_path)) = slice_iter.kind
&& let Res::Local(local) = slice_path.res
&& local == pat.hir_id
&& !assignval.span.from_expansion()
&& switch_to_eager_eval(cx, assignval)
&& span_is_local(assignval.span)
// The `fill` method cannot be used if the slice's element type does not implement the `Clone` trait.
&& let Some(clone_trait) = cx.tcx.lang_items().clone_trait()
&& implements_trait(cx, cx.typeck_results().expr_ty(recv), clone_trait, &[])
{
sugg(cx, body, expr, recv_path.span, assignval.span);
}
}
fn sugg<'tcx>(
cx: &LateContext<'tcx>,
body: &'tcx Expr<'_>,
expr: &'tcx Expr<'_>,
slice_span: rustc_span::Span,
assignval_span: rustc_span::Span,
) {
let mut app = if span_contains_comment(cx.sess().source_map(), body.span) {
Applicability::MaybeIncorrect // Comments may be informational.
} else {
Applicability::MachineApplicable
};
span_lint_and_sugg(
cx,
MANUAL_SLICE_FILL,
expr.span,
"manually filling a slice",
"try",
format!(
"{}.fill({});",
snippet_with_applicability(cx, slice_span, "..", &mut app),
snippet_with_applicability(cx, assignval_span, "..", &mut app),
),
app,
);
}

View file

@ -8,6 +8,7 @@ mod iter_next_loop;
mod manual_find;
mod manual_flatten;
mod manual_memcpy;
mod manual_slice_fill;
mod manual_while_let_some;
mod missing_spin_loop;
mod mut_range_bound;
@ -714,6 +715,31 @@ declare_clippy_lint! {
"possibly unintended infinite loop"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for manually filling a slice with a value.
///
/// ### Why is this bad?
/// Using the `fill` method is more idiomatic and concise.
///
/// ### Example
/// ```no_run
/// let mut some_slice = [1, 2, 3, 4, 5];
/// for i in 0..some_slice.len() {
/// some_slice[i] = 0;
/// }
/// ```
/// Use instead:
/// ```no_run
/// let mut some_slice = [1, 2, 3, 4, 5];
/// some_slice.fill(0);
/// ```
#[clippy::version = "1.86.0"]
pub MANUAL_SLICE_FILL,
style,
"manually filling a slice with a value"
}
pub struct Loops {
msrv: Msrv,
enforce_iter_loop_reborrow: bool,
@ -750,6 +776,7 @@ impl_lint_pass!(Loops => [
MANUAL_WHILE_LET_SOME,
UNUSED_ENUMERATE_INDEX,
INFINITE_LOOP,
MANUAL_SLICE_FILL,
]);
impl<'tcx> LateLintPass<'tcx> for Loops {
@ -823,6 +850,7 @@ impl Loops {
) {
let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
if !is_manual_memcpy_triggered {
manual_slice_fill::check(cx, pat, arg, body, expr, &self.msrv);
needless_range_loop::check(cx, pat, arg, body, expr);
explicit_counter_loop::check(cx, pat, arg, body, expr, label);
}

View file

@ -181,13 +181,16 @@ fn build_suggestion(
ExprKind::Lit(Spanned {
node: LitKind::Int(_, LitIntType::Unsuffixed),
..
}) | ExprKind::Unary(UnOp::Neg, Expr {
kind: ExprKind::Lit(Spanned {
node: LitKind::Int(_, LitIntType::Unsuffixed),
}) | ExprKind::Unary(
UnOp::Neg,
Expr {
kind: ExprKind::Lit(Spanned {
node: LitKind::Int(_, LitIntType::Unsuffixed),
..
}),
..
}),
..
})
}
)
) {
format!("_{}", cx.typeck_results().expr_ty(rhs))
} else {

View file

@ -0,0 +1,225 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::impl_lint_pass;
use rustc_span::{Span, Symbol, sym};
declare_clippy_lint! {
/// ### What it does
/// This detects various manual reimplementations of `Option::as_slice`.
///
/// ### Why is this bad?
/// Those implementations are both more complex than calling `as_slice`
/// and unlike that incur a branch, pessimizing performance and leading
/// to more generated code.
///
/// ### Example
/// ```no_run
///# let opt = Some(1);
/// _ = opt.as_ref().map_or(&[][..], std::slice::from_ref);
/// _ = match opt.as_ref() {
/// Some(f) => std::slice::from_ref(f),
/// None => &[],
/// };
/// ```
/// Use instead:
/// ```no_run
///# let opt = Some(1);
/// _ = opt.as_slice();
/// _ = opt.as_slice();
/// ```
#[clippy::version = "1.85.0"]
pub MANUAL_OPTION_AS_SLICE,
complexity,
"manual `Option::as_slice`"
}
pub struct ManualOptionAsSlice {
msrv: msrvs::Msrv,
}
impl ManualOptionAsSlice {
pub fn new(conf: &Conf) -> Self {
Self {
msrv: conf.msrv.clone(),
}
}
}
impl_lint_pass!(ManualOptionAsSlice => [MANUAL_OPTION_AS_SLICE]);
impl LateLintPass<'_> for ManualOptionAsSlice {
extract_msrv_attr!(LateContext);
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
let span = expr.span;
if span.from_expansion()
|| !self.msrv.meets(if clippy_utils::is_in_const_context(cx) {
msrvs::CONST_OPTION_AS_SLICE
} else {
msrvs::OPTION_AS_SLICE
})
{
return;
}
match expr.kind {
ExprKind::Match(scrutinee, [arm1, arm2], _) => {
if is_none_arm(cx, arm2) && check_arms(cx, arm2, arm1)
|| is_none_arm(cx, arm1) && check_arms(cx, arm1, arm2)
{
check_as_ref(cx, scrutinee, span);
}
},
ExprKind::If(cond, then, Some(other)) => {
if let ExprKind::Let(let_expr) = cond.kind
&& let Some(binding) = extract_ident_from_some_pat(cx, let_expr.pat)
&& check_some_body(cx, binding, then)
&& is_empty_slice(cx, other.peel_blocks())
{
check_as_ref(cx, let_expr.init, span);
}
},
ExprKind::MethodCall(seg, callee, [], _) => {
if seg.ident.name.as_str() == "unwrap_or_default" {
check_map(cx, callee, span);
}
},
ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name.as_str() {
"unwrap_or" => {
if is_empty_slice(cx, or) {
check_map(cx, callee, span);
}
},
"unwrap_or_else" => {
if returns_empty_slice(cx, or) {
check_map(cx, callee, span);
}
},
_ => {},
},
ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name.as_str() {
"map_or" => {
if is_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) {
check_as_ref(cx, callee, span);
}
},
"map_or_else" => {
if returns_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) {
check_as_ref(cx, callee, span);
}
},
_ => {},
},
_ => {},
}
}
}
fn check_map(cx: &LateContext<'_>, map: &Expr<'_>, span: Span) {
if let ExprKind::MethodCall(seg, callee, [mapping], _) = map.kind
&& seg.ident.name == sym::map
&& is_slice_from_ref(cx, mapping)
{
check_as_ref(cx, callee, span);
}
}
fn check_as_ref(cx: &LateContext<'_>, expr: &Expr<'_>, span: Span) {
if let ExprKind::MethodCall(seg, callee, [], _) = expr.kind
&& seg.ident.name == sym::as_ref
&& let ty::Adt(adtdef, ..) = cx.typeck_results().expr_ty(callee).kind()
&& cx.tcx.is_diagnostic_item(sym::Option, adtdef.did())
{
if let Some(snippet) = clippy_utils::source::snippet_opt(cx, callee.span) {
span_lint_and_sugg(
cx,
MANUAL_OPTION_AS_SLICE,
span,
"use `Option::as_slice`",
"use",
format!("{snippet}.as_slice()"),
Applicability::MachineApplicable,
);
} else {
span_lint(cx, MANUAL_OPTION_AS_SLICE, span, "use `Option_as_slice`");
}
}
}
fn extract_ident_from_some_pat(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option<Symbol> {
if let PatKind::TupleStruct(QPath::Resolved(None, path), [binding], _) = pat.kind
&& let Res::Def(DefKind::Ctor(..), def_id) = path.res
&& let PatKind::Binding(_mode, _hir_id, ident, _inner_pat) = binding.kind
&& clippy_utils::is_lang_item_or_ctor(cx, def_id, LangItem::OptionSome)
{
Some(ident.name)
} else {
None
}
}
/// Returns true if `expr` is `std::slice::from_ref(<name>)`. Used in `if let`s.
fn check_some_body(cx: &LateContext<'_>, name: Symbol, expr: &Expr<'_>) -> bool {
if let ExprKind::Call(slice_from_ref, [arg]) = expr.peel_blocks().kind
&& is_slice_from_ref(cx, slice_from_ref)
&& let ExprKind::Path(QPath::Resolved(None, path)) = arg.kind
&& let [seg] = path.segments
{
seg.ident.name == name
} else {
false
}
}
fn check_arms(cx: &LateContext<'_>, none_arm: &Arm<'_>, some_arm: &Arm<'_>) -> bool {
if none_arm.guard.is_none()
&& some_arm.guard.is_none()
&& is_empty_slice(cx, none_arm.body)
&& let Some(name) = extract_ident_from_some_pat(cx, some_arm.pat)
{
check_some_body(cx, name, some_arm.body)
} else {
false
}
}
fn returns_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match expr.kind {
ExprKind::Path(_) => clippy_utils::is_path_diagnostic_item(cx, expr, sym::default_fn),
ExprKind::Closure(cl) => is_empty_slice(cx, cx.tcx.hir().body(cl.body).value),
_ => false,
}
}
/// Returns if expr returns an empty slice. If:
/// - An indexing operation to an empty array with a built-in range. `[][..]`
/// - An indexing operation with a zero-ended range. `expr[..0]`
/// - A reference to an empty array. `&[]`
/// - Or a call to `Default::default`.
fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let expr = peel_hir_expr_refs(expr.peel_blocks()).0;
match expr.kind {
ExprKind::Index(arr, range, _) => match arr.kind {
ExprKind::Array([]) => is_range_literal(range),
ExprKind::Array(_) => {
let Some(range) = clippy_utils::higher::Range::hir(range) else {
return false;
};
range.end.is_some_and(|e| clippy_utils::is_integer_const(cx, e, 0))
},
_ => false,
},
ExprKind::Array([]) => true,
ExprKind::Call(def, []) => clippy_utils::is_path_diagnostic_item(cx, def, sym::default_fn),
_ => false,
}
}
fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
clippy_utils::is_expr_path_def_path(cx, expr, &["core", "slice", "raw", "from_ref"])
}

View file

@ -9,8 +9,8 @@ use rustc_span::sym;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher::IfLetOrMatch;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::implements_trait;
use clippy_utils::{is_default_equivalent, is_in_const_context, peel_blocks, span_contains_comment};
use clippy_utils::ty::{expr_type_is_certain, implements_trait};
use clippy_utils::{is_default_equivalent, is_in_const_context, path_res, peel_blocks, span_contains_comment};
declare_clippy_lint! {
/// ### What it does
@ -158,6 +158,36 @@ fn handle<'tcx>(cx: &LateContext<'tcx>, if_let_or_match: IfLetOrMatch<'tcx>, exp
} else {
Applicability::MachineApplicable
};
// We now check if the condition is a None variant, in which case we need to specify the type
if path_res(cx, condition)
.opt_def_id()
.is_some_and(|id| Some(cx.tcx.parent(id)) == cx.tcx.lang_items().option_none_variant())
{
return span_lint_and_sugg(
cx,
MANUAL_UNWRAP_OR_DEFAULT,
expr.span,
format!("{expr_name} can be simplified with `.unwrap_or_default()`"),
"replace it with",
format!("{receiver}::<{expr_type}>.unwrap_or_default()"),
applicability,
);
}
// We check if the expression type is still uncertain, in which case we ask the user to specify it
if !expr_type_is_certain(cx, condition) {
return span_lint_and_sugg(
cx,
MANUAL_UNWRAP_OR_DEFAULT,
expr.span,
format!("{expr_name} can be simplified with `.unwrap_or_default()`"),
format!("ascribe the type {expr_type} and replace your expression with"),
format!("{receiver}.unwrap_or_default()"),
Applicability::Unspecified,
);
}
span_lint_and_sugg(
cx,
MANUAL_UNWRAP_OR_DEFAULT,

View file

@ -1,8 +1,8 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks};
use clippy_utils::{is_none_arm, is_res_lang_ctor, path_res, peel_blocks};
use rustc_errors::Applicability;
use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatExpr, PatExprKind, PatKind, QPath};
use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath};
use rustc_lint::LateContext;
use rustc_middle::ty;
@ -55,14 +55,6 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
}
}
// Checks if arm has the form `None => None`
fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
matches!(
arm.pat.kind,
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionNone)
)
}
// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<Mutability> {
if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind

View file

@ -95,6 +95,7 @@ mod readonly_write_lock;
mod redundant_as_str;
mod repeat_once;
mod result_map_or_else_none;
mod return_and_then;
mod search_is_some;
mod seek_from_current;
mod seek_to_start_instead_of_rewind;
@ -3517,7 +3518,7 @@ declare_clippy_lint! {
/// ```
#[clippy::version = "1.73.0"]
pub FORMAT_COLLECT,
perf,
pedantic,
"`format!`ing every element in a collection, then collecting the strings into a new `String`"
}
@ -4365,7 +4366,7 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
/// Checks for string slices immediantly followed by `as_bytes`.
/// Checks for string slices immediately followed by `as_bytes`.
///
/// ### Why is this bad?
/// It involves doing an unnecessary UTF-8 alignment check which is less efficient, and can cause a panic.
@ -4391,6 +4392,46 @@ declare_clippy_lint! {
"slicing a string and immediately calling as_bytes is less efficient and can lead to panics"
}
declare_clippy_lint! {
/// ### What it does
///
/// Detect functions that end with `Option::and_then` or `Result::and_then`, and suggest using a question mark (`?`) instead.
///
/// ### Why is this bad?
///
/// The `and_then` method is used to chain a computation that returns an `Option` or a `Result`.
/// This can be replaced with the `?` operator, which is more concise and idiomatic.
///
/// ### Example
///
/// ```no_run
/// fn test(opt: Option<i32>) -> Option<i32> {
/// opt.and_then(|n| {
/// if n > 1 {
/// Some(n + 1)
/// } else {
/// None
/// }
/// })
/// }
/// ```
/// Use instead:
/// ```no_run
/// fn test(opt: Option<i32>) -> Option<i32> {
/// let n = opt?;
/// if n > 1 {
/// Some(n + 1)
/// } else {
/// None
/// }
/// }
/// ```
#[clippy::version = "1.86.0"]
pub RETURN_AND_THEN,
restriction,
"using `Option::and_then` or `Result::and_then` to chain a computation that returns an `Option` or a `Result`"
}
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
@ -4560,6 +4601,7 @@ impl_lint_pass!(Methods => [
USELESS_NONZERO_NEW_UNCHECKED,
MANUAL_REPEAT_N,
SLICED_STRING_AS_BYTES,
RETURN_AND_THEN,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@ -4789,7 +4831,10 @@ impl Methods {
let biom_option_linted = bind_instead_of_map::check_and_then_some(cx, expr, recv, arg);
let biom_result_linted = bind_instead_of_map::check_and_then_ok(cx, expr, recv, arg);
if !biom_option_linted && !biom_result_linted {
unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
let ule_and_linted = unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
if !ule_and_linted {
return_and_then::check(cx, expr, recv, arg);
}
}
},
("any", [arg]) => {
@ -5003,7 +5048,9 @@ impl Methods {
get_first::check(cx, expr, recv, arg);
get_last_with_len::check(cx, expr, recv, arg);
},
("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
("get_or_insert_with", [arg]) => {
unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert");
},
("hash", [arg]) => {
unit_hash::check(cx, expr, recv, arg);
},
@ -5144,7 +5191,9 @@ impl Methods {
},
_ => iter_nth_zero::check(cx, expr, recv, n_arg),
},
("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
("ok_or_else", [arg]) => {
unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or");
},
("open", [_]) => {
open_options::check(cx, expr, recv);
},
@ -5437,9 +5486,12 @@ impl ShouldImplTraitCase {
fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
self.lint_explicit_lifetime
|| !impl_item.generics.params.iter().any(|p| {
matches!(p.kind, hir::GenericParamKind::Lifetime {
kind: hir::LifetimeParamKind::Explicit
})
matches!(
p.kind,
hir::GenericParamKind::Lifetime {
kind: hir::LifetimeParamKind::Explicit
}
)
})
}
}

View file

@ -1,5 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::LateContext;
use rustc_span::sym;
@ -10,13 +11,22 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'
// Checks if expression type is equal to sym::Option and if the expr is not a syntactic place
if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) {
if let Some(function_name) = source_of_temporary_value(recv) {
span_lint_and_note(
span_lint_and_then(
cx,
NEEDLESS_OPTION_TAKE,
expr.span,
"called `Option::take()` on a temporary value",
None,
format!("`{function_name}` creates a temporary value, so calling take() has no effect"),
|diag| {
diag.note(format!(
"`{function_name}` creates a temporary value, so calling take() has no effect"
));
diag.span_suggestion(
expr.span.with_lo(recv.span.hi()),
"remove",
"",
Applicability::MachineApplicable,
);
},
);
}
}

View file

@ -0,0 +1,67 @@
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, GenericArg, Ty};
use rustc_span::sym;
use std::ops::ControlFlow;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability};
use clippy_utils::ty::get_type_diagnostic_name;
use clippy_utils::visitors::for_each_unconsumed_temporary;
use clippy_utils::{is_expr_final_block_expr, peel_blocks};
use super::RETURN_AND_THEN;
/// lint if `and_then` is the last expression in a block, and
/// there are no references or temporaries in the receiver
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &hir::Expr<'_>,
recv: &'tcx hir::Expr<'tcx>,
arg: &'tcx hir::Expr<'_>,
) {
if !is_expr_final_block_expr(cx.tcx, expr) {
return;
}
let recv_type = cx.typeck_results().expr_ty(recv);
if !matches!(get_type_diagnostic_name(cx, recv_type), Some(sym::Option | sym::Result)) {
return;
}
let has_ref_type = matches!(recv_type.kind(), ty::Adt(_, args) if args
.first()
.and_then(|arg0: &GenericArg<'tcx>| GenericArg::as_type(*arg0))
.is_some_and(Ty::is_ref));
let has_temporaries = for_each_unconsumed_temporary(cx, recv, |_| ControlFlow::Break(())).is_break();
if has_ref_type && has_temporaries {
return;
}
let hir::ExprKind::Closure(&hir::Closure { body, fn_decl, .. }) = arg.kind else {
return;
};
let closure_arg = fn_decl.inputs[0];
let closure_expr = peel_blocks(cx.tcx.hir().body(body).value);
let mut applicability = Applicability::MachineApplicable;
let arg_snip = snippet_with_applicability(cx, closure_arg.span, "_", &mut applicability);
let recv_snip = snippet_with_applicability(cx, recv.span, "_", &mut applicability);
let body_snip = snippet_with_applicability(cx, closure_expr.span, "..", &mut applicability);
let inner = match body_snip.strip_prefix('{').and_then(|s| s.strip_suffix('}')) {
Some(s) => s.trim_start_matches('\n').trim_end(),
None => &body_snip,
};
let msg = "use the question mark operator instead of an `and_then` call";
let sugg = format!(
"let {} = {}?;\n{}",
arg_snip,
recv_snip,
reindent_multiline(inner.into(), false, indent_of(cx, expr.span))
);
span_lint_and_sugg(cx, RETURN_AND_THEN, expr.span, msg, "try", sugg, applicability);
}

View file

@ -123,32 +123,60 @@ pub(super) fn check(
if let hir::ExprKind::Lit(lit) = init.kind {
match lit.node {
ast::LitKind::Bool(false) => {
check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, Replacement {
method_name: "any",
has_args: true,
has_generic_return: false,
});
check_fold_with_op(
cx,
expr,
acc,
fold_span,
hir::BinOpKind::Or,
Replacement {
method_name: "any",
has_args: true,
has_generic_return: false,
},
);
},
ast::LitKind::Bool(true) => {
check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, Replacement {
method_name: "all",
has_args: true,
has_generic_return: false,
});
check_fold_with_op(
cx,
expr,
acc,
fold_span,
hir::BinOpKind::And,
Replacement {
method_name: "all",
has_args: true,
has_generic_return: false,
},
);
},
ast::LitKind::Int(Pu128(0), _) => {
check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, Replacement {
method_name: "sum",
has_args: false,
has_generic_return: needs_turbofish(cx, expr),
});
check_fold_with_op(
cx,
expr,
acc,
fold_span,
hir::BinOpKind::Add,
Replacement {
method_name: "sum",
has_args: false,
has_generic_return: needs_turbofish(cx, expr),
},
);
},
ast::LitKind::Int(Pu128(1), _) => {
check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, Replacement {
method_name: "product",
has_args: false,
has_generic_return: needs_turbofish(cx, expr),
});
check_fold_with_op(
cx,
expr,
acc,
fold_span,
hir::BinOpKind::Mul,
Replacement {
method_name: "product",
has_args: false,
has_generic_return: needs_turbofish(cx, expr),
},
);
},
_ => (),
}

View file

@ -18,7 +18,7 @@ pub(super) fn check<'tcx>(
recv: &'tcx hir::Expr<'_>,
arg: &'tcx hir::Expr<'_>,
simplify_using: &str,
) {
) -> bool {
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
let is_bool = cx.typeck_results().expr_ty(recv).is_bool();
@ -29,7 +29,7 @@ pub(super) fn check<'tcx>(
let body_expr = &body.value;
if usage::BindingUsageFinder::are_params_used(cx, body) || is_from_proc_macro(cx, expr) {
return;
return false;
}
if eager_or_lazy::switch_to_eager_eval(cx, body_expr) {
@ -71,8 +71,10 @@ pub(super) fn check<'tcx>(
applicability,
);
});
return true;
}
}
}
}
false
}

View file

@ -168,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
TOPLEVEL_REF_ARG,
arg.hir_id,
arg.pat.span,
"`ref` directly on a function argument is ignored. \
"`ref` directly on a function parameter does not prevent taking ownership of the passed argument. \
Consider using a reference type instead",
);
}

View file

@ -152,16 +152,19 @@ fn collect_unsafe_exprs<'tcx>(
ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => {
if matches!(
lhs.kind,
ExprKind::Path(QPath::Resolved(_, hir::Path {
res: Res::Def(
DefKind::Static {
mutability: Mutability::Mut,
..
},
_
),
..
}))
ExprKind::Path(QPath::Resolved(
_,
hir::Path {
res: Res::Def(
DefKind::Static {
mutability: Mutability::Mut,
..
},
_
),
..
}
))
) {
unsafe_ops.push(("modification of a mutable static occurs here", expr.span));
collect_unsafe_exprs(cx, rhs, unsafe_ops);

View file

@ -18,11 +18,14 @@ declare_clippy_lint! {
///
/// On the other hand, `Mutex`es are, in general, easier to
/// verify correctness. An atomic does not behave the same as
/// an equivalent mutex. See [this issue](https://github.com/rust-lang/rust-clippy/issues/4295)'s commentary for more details.
/// an equivalent mutex. See [this issue](https://github.com/rust-lang/rust-clippy/issues/4295)'s
/// commentary for more details.
///
/// ### Known problems
/// This lint cannot detect if the mutex is actually used
/// for waiting before a critical section.
/// * This lint cannot detect if the mutex is actually used
/// for waiting before a critical section.
/// * This lint has a false positive that warns without considering the case
/// where `Mutex` is used together with `Condvar`.
///
/// ### Example
/// ```no_run
@ -48,14 +51,23 @@ declare_clippy_lint! {
/// Checks for usage of `Mutex<X>` where `X` is an integral
/// type.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Using a mutex just to make access to a plain integer
/// sequential is
/// shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster.
///
/// On the other hand, `Mutex`es are, in general, easier to
/// verify correctness. An atomic does not behave the same as
/// an equivalent mutex. See [this issue](https://github.com/rust-lang/rust-clippy/issues/4295)'s
/// commentary for more details.
///
/// ### Known problems
/// This lint cannot detect if the mutex is actually used
/// for waiting before a critical section.
/// * This lint cannot detect if the mutex is actually used
/// for waiting before a critical section.
/// * This lint has a false positive that warns without considering the case
/// where `Mutex` is used together with `Condvar`.
/// * This lint suggest using `AtomicU64` instead of `Mutex<u64>`, but
/// `AtomicU64` is not available on some 32-bit platforms.
///
/// ### Example
/// ```no_run
@ -70,7 +82,7 @@ declare_clippy_lint! {
/// ```
#[clippy::version = "pre 1.29.0"]
pub MUTEX_INTEGER,
nursery,
restriction,
"using a mutex for an integer type"
}
@ -108,7 +120,7 @@ fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> {
UintTy::U32 => Some("AtomicU32"),
UintTy::U64 => Some("AtomicU64"),
UintTy::Usize => Some("AtomicUsize"),
// There's no `AtomicU128`.
// `AtomicU128` is unstable and only available on a few platforms: https://github.com/rust-lang/rust/issues/99069
UintTy::U128 => None,
}
},
@ -119,7 +131,7 @@ fn get_atomic_name(ty: Ty<'_>) -> Option<&'static str> {
IntTy::I32 => Some("AtomicI32"),
IntTy::I64 => Some("AtomicI64"),
IntTy::Isize => Some("AtomicIsize"),
// There's no `AtomicI128`.
// `AtomicU128` is unstable and only available on a few platforms: https://github.com/rust-lang/rust/issues/99069
IntTy::I128 => None,
}
},

View file

@ -181,9 +181,14 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
&& !is_copy(cx, ty)
&& ty.is_sized(cx.tcx, cx.typing_env())
&& !allowed_traits.iter().any(|&t| {
implements_trait_with_env_from_iter(cx.tcx, cx.typing_env(), ty, t, None, [None::<
ty::GenericArg<'tcx>,
>])
implements_trait_with_env_from_iter(
cx.tcx,
cx.typing_env(),
ty,
t,
None,
[None::<ty::GenericArg<'tcx>>],
)
})
&& !implements_borrow_trait
&& !all_borrowable_trait

View file

@ -265,9 +265,6 @@ declare_clippy_lint! {
/// `x.trailing_zeros() >= 4` is much clearer than `x & 15
/// == 0`
///
/// ### Known problems
/// llvm generates better code for `x & 15 == 0` on x86
///
/// ### Example
/// ```no_run
/// # let x = 1;

View file

@ -3,17 +3,14 @@ use clippy_utils::source::snippet_with_applicability;
use rustc_ast::ast::BinOpKind::{Add, BitAnd, BitOr, BitXor, Div, Mul, Rem, Shl, Shr, Sub};
use rustc_ast::ast::{BinOpKind, Expr, ExprKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_lint::{EarlyContext, EarlyLintPass, Lint};
use rustc_session::declare_lint_pass;
use rustc_span::source_map::Spanned;
declare_clippy_lint! {
/// ### What it does
/// Checks for operations where precedence may be unclear
/// and suggests to add parentheses. Currently it catches the following:
/// * mixed usage of arithmetic and bit shifting/combining operators without
/// parentheses
/// * mixed usage of bitmasking and bit shifting operators without parentheses
/// Checks for operations where precedence may be unclear and suggests to add parentheses.
/// It catches a mixed usage of arithmetic and bit shifting/combining operators without parentheses
///
/// ### Why is this bad?
/// Not everyone knows the precedence of those operators by
@ -21,15 +18,32 @@ declare_clippy_lint! {
/// code.
///
/// ### Example
/// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
/// * `0x2345 & 0xF000 >> 12` equals 5, while `(0x2345 & 0xF000) >> 12` equals 2
/// `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
#[clippy::version = "pre 1.29.0"]
pub PRECEDENCE,
complexity,
"operations where precedence may be unclear"
}
declare_lint_pass!(Precedence => [PRECEDENCE]);
declare_clippy_lint! {
/// ### What it does
/// Checks for bit shifting operations combined with bit masking/combining operators
/// and suggest using parentheses.
///
/// ### Why restrict this?
/// Not everyone knows the precedence of those operators by
/// heart, so expressions like these may trip others trying to reason about the
/// code.
///
/// ### Example
/// `0x2345 & 0xF000 >> 12` equals 5, while `(0x2345 & 0xF000) >> 12` equals 2
#[clippy::version = "1.86.0"]
pub PRECEDENCE_BITS,
restriction,
"operations mixing bit shifting with bit combining/masking"
}
declare_lint_pass!(Precedence => [PRECEDENCE, PRECEDENCE_BITS]);
impl EarlyLintPass for Precedence {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
@ -38,10 +52,10 @@ impl EarlyLintPass for Precedence {
}
if let ExprKind::Binary(Spanned { node: op, .. }, ref left, ref right) = expr.kind {
let span_sugg = |expr: &Expr, sugg, appl| {
let span_sugg = |lint: &'static Lint, expr: &Expr, sugg, appl| {
span_lint_and_sugg(
cx,
PRECEDENCE,
lint,
expr.span,
"operator precedence might not be obvious",
"consider parenthesizing your expression",
@ -57,37 +71,41 @@ impl EarlyLintPass for Precedence {
match (op, get_bin_opt(left), get_bin_opt(right)) {
(
BitAnd | BitOr | BitXor,
Some(Shl | Shr | Add | Div | Mul | Rem | Sub),
Some(Shl | Shr | Add | Div | Mul | Rem | Sub),
Some(left_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)),
Some(right_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)),
)
| (Shl | Shr, Some(Add | Div | Mul | Rem | Sub), Some(Add | Div | Mul | Rem | Sub)) => {
| (
Shl | Shr,
Some(left_op @ (Add | Div | Mul | Rem | Sub)),
Some(right_op @ (Add | Div | Mul | Rem | Sub)),
) => {
let sugg = format!(
"({}) {} ({})",
snippet_with_applicability(cx, left.span, "..", &mut applicability),
op.as_str(),
snippet_with_applicability(cx, right.span, "..", &mut applicability)
);
span_sugg(expr, sugg, applicability);
span_sugg(lint_for(&[op, left_op, right_op]), expr, sugg, applicability);
},
(BitAnd | BitOr | BitXor, Some(Shl | Shr | Add | Div | Mul | Rem | Sub), _)
| (Shl | Shr, Some(Add | Div | Mul | Rem | Sub), _) => {
(BitAnd | BitOr | BitXor, Some(side_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)), _)
| (Shl | Shr, Some(side_op @ (Add | Div | Mul | Rem | Sub)), _) => {
let sugg = format!(
"({}) {} {}",
snippet_with_applicability(cx, left.span, "..", &mut applicability),
op.as_str(),
snippet_with_applicability(cx, right.span, "..", &mut applicability)
);
span_sugg(expr, sugg, applicability);
span_sugg(lint_for(&[op, side_op]), expr, sugg, applicability);
},
(BitAnd | BitOr | BitXor, _, Some(Shl | Shr | Add | Div | Mul | Rem | Sub))
| (Shl | Shr, _, Some(Add | Div | Mul | Rem | Sub)) => {
(BitAnd | BitOr | BitXor, _, Some(side_op @ (Shl | Shr | Add | Div | Mul | Rem | Sub)))
| (Shl | Shr, _, Some(side_op @ (Add | Div | Mul | Rem | Sub))) => {
let sugg = format!(
"{} {} ({})",
snippet_with_applicability(cx, left.span, "..", &mut applicability),
op.as_str(),
snippet_with_applicability(cx, right.span, "..", &mut applicability)
);
span_sugg(expr, sugg, applicability);
span_sugg(lint_for(&[op, side_op]), expr, sugg, applicability);
},
_ => (),
}
@ -106,3 +124,11 @@ fn get_bin_opt(expr: &Expr) -> Option<BinOpKind> {
fn is_bit_op(op: BinOpKind) -> bool {
matches!(op, BitXor | BitAnd | BitOr | Shl | Shr)
}
fn lint_for(ops: &[BinOpKind]) -> &'static Lint {
if ops.iter().all(|op| is_bit_op(*op)) {
PRECEDENCE_BITS
} else {
PRECEDENCE
}
}

View file

@ -1,5 +1,6 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::source::SpanRangeExt;
use clippy_utils::sugg::Sugg;
use clippy_utils::visitors::contains_unsafe_block;
use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, std_or_core};
use hir::LifetimeName;
@ -250,15 +251,24 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Binary(ref op, l, r) = expr.kind {
if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) {
span_lint(
cx,
CMP_NULL,
expr.span,
"comparing with null is better expressed by the `.is_null()` method",
);
}
if let ExprKind::Binary(op, l, r) = expr.kind
&& (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne)
{
let non_null_path_snippet = match (is_null_path(cx, l), is_null_path(cx, r)) {
(true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_par(),
(false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_par(),
_ => return,
};
span_lint_and_sugg(
cx,
CMP_NULL,
expr.span,
"comparing with null is better expressed by the `.is_null()` method",
"try",
format!("{non_null_path_snippet}.is_null()"),
Applicability::MachineApplicable,
);
} else {
check_invalid_ptr_usage(cx, expr);
}

View file

@ -349,10 +349,14 @@ fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>,
local_use_locs: _,
local_consume_or_mutate_locs: clone_consume_or_mutate_locs,
},
)) = visit_local_usage(&[cloned, clone], mir, mir::Location {
block: bb,
statement_index: mir.basic_blocks[bb].statements.len(),
})
)) = visit_local_usage(
&[cloned, clone],
mir,
mir::Location {
block: bb,
statement_index: mir.basic_blocks[bb].statements.len(),
},
)
.map(|mut vec| (vec.remove(0), vec.remove(0)))
{
CloneUsage {

View file

@ -1,8 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, reindent_multiline, snippet};
use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind};
use rustc_ast::visit::{Visitor, walk_expr};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_session::declare_lint_pass;
use rustc_span::Span;
use std::borrow::Cow;
declare_clippy_lint! {
/// ### What it does
@ -75,13 +79,27 @@ impl EarlyLintPass for RedundantElse {
_ => break,
}
}
span_lint_and_help(
let mut app = Applicability::MachineApplicable;
if let ExprKind::Block(block, _) = &els.kind {
for stmt in &block.stmts {
// If the `else` block contains a local binding or a macro invocation, Clippy shouldn't auto-fix it
if matches!(&stmt.kind, StmtKind::Let(_) | StmtKind::MacCall(_)) {
app = Applicability::Unspecified;
break;
}
}
}
// FIXME: The indentation of the suggestion would be the same as the one of the macro invocation in this implementation, see https://github.com/rust-lang/rust-clippy/pull/13936#issuecomment-2569548202
span_lint_and_sugg(
cx,
REDUNDANT_ELSE,
els.span,
els.span.with_lo(then.span.hi()),
"redundant else block",
None,
"remove the `else` block and move the contents out",
make_sugg(cx, els.span, "..", Some(expr.span)).to_string(),
app,
);
}
}
@ -136,3 +154,23 @@ impl BreakVisitor {
self.check(stmt, Self::visit_stmt)
}
}
// Extract the inner contents of an `else` block str
// e.g. `{ foo(); bar(); }` -> `foo(); bar();`
fn extract_else_block(mut block: &str) -> String {
block = block.strip_prefix("{").unwrap_or(block);
block = block.strip_suffix("}").unwrap_or(block);
block.trim_end().to_string()
}
fn make_sugg<'a>(
cx: &EarlyContext<'_>,
els_span: Span,
default: &'a str,
indent_relative_to: Option<Span>,
) -> Cow<'a, str> {
let extracted = extract_else_block(&snippet(cx, els_span, default));
let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
reindent_multiline(extracted.into(), false, indent)
}

View file

@ -62,10 +62,13 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
&& let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
{
if !map.contains_key(res) {
map.insert(*res, ExistingName {
impl_methods: BTreeMap::new(),
trait_methods: BTreeMap::new(),
});
map.insert(
*res,
ExistingName {
impl_methods: BTreeMap::new(),
trait_methods: BTreeMap::new(),
},
);
}
let existing_name = map.get_mut(res).unwrap();

View file

@ -63,8 +63,7 @@ fn get_pointee_ty_and_count_expr<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> {
const METHODS: [&str; 11] = [
"write_bytes",
const METHODS: [&str; 10] = [
"copy_to",
"copy_from",
"copy_to_nonoverlapping",
@ -79,7 +78,7 @@ fn get_pointee_ty_and_count_expr<'tcx>(
if let ExprKind::Call(func, [.., count]) = expr.kind
// Find calls to ptr::{copy, copy_nonoverlapping}
// and ptr::{swap_nonoverlapping, write_bytes},
// and ptr::swap_nonoverlapping,
&& let ExprKind::Path(ref func_qpath) = func.kind
&& let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(
@ -88,7 +87,6 @@ fn get_pointee_ty_and_count_expr<'tcx>(
| sym::ptr_slice_from_raw_parts
| sym::ptr_slice_from_raw_parts_mut
| sym::ptr_swap_nonoverlapping
| sym::ptr_write_bytes
| sym::slice_from_raw_parts
| sym::slice_from_raw_parts_mut
))
@ -99,7 +97,7 @@ fn get_pointee_ty_and_count_expr<'tcx>(
return Some((pointee_ty, count));
}
if let ExprKind::MethodCall(method_path, ptr_self, [.., count], _) = expr.kind
// Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods
// Find calls to copy_{from,to}{,_nonoverlapping}
&& let method_ident = method_path.ident.as_str()
&& METHODS.iter().any(|m| *m == method_ident)
@ -121,6 +119,8 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount {
instead of a count of elements of `T`";
if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr)
// Using a number of bytes for a byte type isn't suspicious
&& pointee_ty != cx.tcx.types.u8
// Find calls to functions with an element count parameter and get
// the pointee type and count parameter expression

View file

@ -55,13 +55,11 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome {
if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind
&& let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id)
&& let Some(to_digits_def_id) = to_digits_call_res.opt_def_id()
&& match_def_path(cx, to_digits_def_id, &[
"core",
"char",
"methods",
"<impl char>",
"to_digit",
])
&& match_def_path(
cx,
to_digits_def_id,
&["core", "char", "methods", "<impl char>", "to_digit"],
)
{
Some((false, char_arg, radix_arg))
} else {

View file

@ -386,22 +386,30 @@ impl<'tcx> LateLintPass<'tcx> for Types {
let is_exported = cx.effective_visibilities.is_exported(def_id);
self.check_fn_decl(cx, decl, CheckTyContext {
is_in_trait_impl,
in_body: matches!(fn_kind, FnKind::Closure),
is_exported,
..CheckTyContext::default()
});
self.check_fn_decl(
cx,
decl,
CheckTyContext {
is_in_trait_impl,
in_body: matches!(fn_kind, FnKind::Closure),
is_exported,
..CheckTyContext::default()
},
);
}
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id);
match item.kind {
ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty(cx, ty, CheckTyContext {
is_exported,
..CheckTyContext::default()
}),
ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty(
cx,
ty,
CheckTyContext {
is_exported,
..CheckTyContext::default()
},
),
// functions, enums, structs, impls and traits are covered
_ => (),
}
@ -419,10 +427,14 @@ impl<'tcx> LateLintPass<'tcx> for Types {
false
};
self.check_ty(cx, ty, CheckTyContext {
is_in_trait_impl,
..CheckTyContext::default()
});
self.check_ty(
cx,
ty,
CheckTyContext {
is_in_trait_impl,
..CheckTyContext::default()
},
);
},
// Methods are covered by check_fn.
// Type aliases are ignored because oftentimes it's impossible to
@ -438,10 +450,14 @@ impl<'tcx> LateLintPass<'tcx> for Types {
let is_exported = cx.effective_visibilities.is_exported(field.def_id);
self.check_ty(cx, field.ty, CheckTyContext {
is_exported,
..CheckTyContext::default()
});
self.check_ty(
cx,
field.ty,
CheckTyContext {
is_exported,
..CheckTyContext::default()
},
);
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'tcx>) {
@ -469,10 +485,14 @@ impl<'tcx> LateLintPass<'tcx> for Types {
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) {
if let Some(ty) = local.ty {
self.check_ty(cx, ty, CheckTyContext {
in_body: true,
..CheckTyContext::default()
});
self.check_ty(
cx,
ty,
CheckTyContext {
in_body: true,
..CheckTyContext::default()
},
);
}
}
}

View file

@ -257,10 +257,13 @@ fn is_default_method_on_current_ty<'tcx>(tcx: TyCtxt<'tcx>, qpath: QPath<'tcx>,
}
if matches!(
ty.kind,
TyKind::Path(QPath::Resolved(_, hir::Path {
res: Res::SelfTyAlias { .. },
..
},))
TyKind::Path(QPath::Resolved(
_,
hir::Path {
res: Res::SelfTyAlias { .. },
..
},
))
) {
return true;
}

View file

@ -37,7 +37,7 @@ declare_clippy_lint! {
#[derive(Default)]
pub struct UnnecessarySemicolon {
last_statements: Vec<HirId>,
last_statements: Vec<(HirId, bool)>,
}
impl_lint_pass!(UnnecessarySemicolon => [UNNECESSARY_SEMICOLON]);
@ -45,27 +45,25 @@ impl_lint_pass!(UnnecessarySemicolon => [UNNECESSARY_SEMICOLON]);
impl UnnecessarySemicolon {
/// Enter or leave a block, remembering the last statement of the block.
fn handle_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>, enter: bool) {
// Up to edition 2021, removing the semicolon of the last statement of a block
// may result in the scrutinee temporary values to live longer than the block
// variables. To avoid this problem, we do not lint the last statement of an
// expressionless block.
if cx.tcx.sess.edition() <= Edition2021
&& block.expr.is_none()
// The last statement of an expressionless block deserves a special treatment.
if block.expr.is_none()
&& let Some(last_stmt) = block.stmts.last()
{
if enter {
self.last_statements.push(last_stmt.hir_id);
let block_ty = cx.typeck_results().node_type(block.hir_id);
self.last_statements.push((last_stmt.hir_id, block_ty.is_unit()));
} else {
self.last_statements.pop();
}
}
}
/// Checks if `stmt` is the last statement in an expressionless block for edition ≤ 2021.
fn is_last_in_block(&self, stmt: &Stmt<'_>) -> bool {
/// Checks if `stmt` is the last statement in an expressionless block. In this case,
/// return `Some` with a boolean which is `true` if the block type is `()`.
fn is_last_in_block(&self, stmt: &Stmt<'_>) -> Option<bool> {
self.last_statements
.last()
.is_some_and(|last_stmt_id| last_stmt_id == &stmt.hir_id)
.and_then(|&(stmt_id, is_unit)| (stmt_id == stmt.hir_id).then_some(is_unit))
}
}
@ -90,8 +88,22 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessarySemicolon {
)
&& cx.typeck_results().expr_ty(expr) == cx.tcx.types.unit
{
if self.is_last_in_block(stmt) && leaks_droppable_temporary_with_limited_lifetime(cx, expr) {
return;
if let Some(block_is_unit) = self.is_last_in_block(stmt) {
if cx.tcx.sess.edition() <= Edition2021 && leaks_droppable_temporary_with_limited_lifetime(cx, expr) {
// The expression contains temporaries with limited lifetimes in edition lower than 2024. Those may
// survive until after the end of the current scope instead of until the end of the statement, so do
// not lint this situation.
return;
}
if !block_is_unit {
// Although the expression returns `()`, the block doesn't. This may happen if the expression
// returns early in all code paths, such as a `return value` in the condition of an `if` statement,
// in which case the block type would be `!`. Do not lint in this case, as the statement would
// become the block expression; the block type would become `()` and this may not type correctly
// if the expected type for the block is not `()`.
return;
}
}
let semi_span = expr.span.shrink_to_hi().to(stmt.span.shrink_to_hi());

View file

@ -32,7 +32,7 @@ declare_clippy_lint! {
/// None => 0,
/// };
/// ```
#[clippy::version = "1.83.0"]
#[clippy::version = "1.86.0"]
pub UNNEEDED_STRUCT_PATTERN,
style,
"using struct pattern to match against unit variant"

View file

@ -522,7 +522,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
let replacement = match (format_string_is_raw, replace_raw) {
(false, false) => Some(replacement),
(false, true) => Some(replacement.replace('"', "\\\"").replace('\\', "\\\\")),
(false, true) => Some(replacement.replace('\\', "\\\\").replace('"', "\\\"")),
(true, false) => match conservative_unescape(&replacement) {
Ok(unescaped) => Some(unescaped),
Err(UnescapeErr::Lint) => None,

View file

@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain:
<!-- begin autogenerated nightly -->
```
nightly-2025-01-28
nightly-2025-02-06
```
<!-- end autogenerated nightly -->

View file

@ -662,12 +662,12 @@ pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
&& eq_ext(&l.ext, &r.ext)
}
#[expect(clippy::ref_option, reason = "This is the type how it is stored in the AST")]
pub fn eq_opt_fn_contract(l: &Option<P<FnContract>>, r: &Option<P<FnContract>>) -> bool {
match (l, r) {
(Some(l), Some(r)) => {
eq_expr_opt(l.requires.as_ref(), r.requires.as_ref())
&& eq_expr_opt(l.ensures.as_ref(), r.ensures.as_ref())
}
eq_expr_opt(l.requires.as_ref(), r.requires.as_ref()) && eq_expr_opt(l.ensures.as_ref(), r.ensures.as_ref())
},
(None, None) => true,
(Some(_), None) | (None, Some(_)) => false,
}

View file

@ -341,6 +341,15 @@ pub fn is_wild(pat: &Pat<'_>) -> bool {
matches!(pat.kind, PatKind::Wild)
}
// Checks if arm has the form `None => None`
pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
matches!(
arm.pat.kind,
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone)
)
}
/// Checks if the given `QPath` belongs to a type alias.
pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
match *qpath {

View file

@ -112,10 +112,14 @@ pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool {
/// Convenience wrapper around `visit_local_usage`.
pub fn used_exactly_once(mir: &Body<'_>, local: Local) -> Option<bool> {
visit_local_usage(&[local], mir, Location {
block: START_BLOCK,
statement_index: 0,
})
visit_local_usage(
&[local],
mir,
Location {
block: START_BLOCK,
statement_index: 0,
},
)
.map(|mut vec| {
let LocalUsage { local_use_locs, .. } = vec.remove(0);
let mut locations = local_use_locs

View file

@ -1,4 +1,5 @@
use rustc_ast::attr::AttributeExt;
use rustc_attr_parsing::{RustcVersion, parse_version};
use rustc_session::Session;
use rustc_span::{Symbol, sym};
@ -18,12 +19,14 @@ macro_rules! msrv_aliases {
// names may refer to stabilized feature flags or library items
msrv_aliases! {
1,84,0 { CONST_OPTION_AS_SLICE }
1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP }
1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP }
1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION }
1,80,0 { BOX_INTO_ITER, LAZY_CELL }
1,77,0 { C_STR_LITERALS }
1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }
1,75,0 { OPTION_AS_SLICE }
1,74,0 { REPR_RUST }
1,73,0 { MANUAL_DIV_CEIL }
1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
@ -40,7 +43,7 @@ msrv_aliases! {
1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
1,50,0 { BOOL_THEN, CLAMP }
1,50,0 { BOOL_THEN, CLAMP, SLICE_FILL }
1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN, SATURATING_SUB_CONST }
1,46,0 { CONST_IF_MATCH }
1,45,0 { STR_STRIP_PREFIX }

View file

@ -179,7 +179,10 @@ fn check_rvalue<'tcx>(
))
}
},
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks, _)
Rvalue::NullaryOp(
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks,
_,
)
| Rvalue::ShallowInitBox(_, _) => Ok(()),
Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(body, tcx);

View file

@ -727,12 +727,15 @@ pub fn str_literal_to_char_literal(
&snip[1..(snip.len() - 1)]
};
let hint = format!("'{}'", match ch {
"'" => "\\'",
r"\" => "\\\\",
"\\\"" => "\"", // no need to escape `"` in `'"'`
_ => ch,
});
let hint = format!(
"'{}'",
match ch {
"'" => "\\'",
r"\" => "\\\\",
"\\\"" => "\"", // no need to escape `"` in `'"'`
_ => ch,
}
);
Some(hint)
} else {

View file

@ -370,11 +370,9 @@ mod test {
assert_eq!(camel_case_split("AbcDef"), vec!["Abc", "Def"]);
assert_eq!(camel_case_split("Abc"), vec!["Abc"]);
assert_eq!(camel_case_split("abcDef"), vec!["abc", "Def"]);
assert_eq!(camel_case_split("\u{f6}\u{f6}AabABcd"), vec![
"\u{f6}\u{f6}",
"Aab",
"A",
"Bcd"
]);
assert_eq!(
camel_case_split("\u{f6}\u{f6}AabABcd"),
vec!["\u{f6}\u{f6}", "Aab", "A", "Bcd"]
);
}
}

View file

@ -2,6 +2,7 @@ use clap::{Parser, Subcommand, ValueEnum};
use std::num::NonZero;
use std::path::PathBuf;
#[allow(clippy::struct_excessive_bools)]
#[derive(Parser, Clone, Debug)]
#[command(args_conflicts_with_subcommands = true)]
pub(crate) struct LintcheckConfig {
@ -11,6 +12,9 @@ pub(crate) struct LintcheckConfig {
short = 'j',
value_name = "N",
default_value_t = 0,
default_value_if("perf", "true", Some("1")), // Limit jobs to 1 when benchmarking
conflicts_with("perf"),
required = false,
hide_default_value = true
)]
pub max_jobs: usize,
@ -46,6 +50,11 @@ pub(crate) struct LintcheckConfig {
/// Run clippy on the dependencies of crates specified in crates-toml
#[clap(long, conflicts_with("max_jobs"))]
pub recursive: bool,
/// Also produce a `perf.data` file, implies --jobs=1,
/// the `perf.data` file can be found at
/// `target/lintcheck/sources/<package>-<version>/perf.data`
#[clap(long)]
pub perf: bool,
#[command(subcommand)]
pub subcommand: Option<Commands>,
}

View file

@ -116,7 +116,25 @@ impl Crate {
clippy_args.extend(lint_levels_args.iter().map(String::as_str));
let mut cmd = Command::new("cargo");
let mut cmd;
if config.perf {
cmd = Command::new("perf");
cmd.args(&[
"record",
"-e",
"instructions", // Only count instructions
"-g", // Enable call-graph, useful for flamegraphs and produces richer reports
"--quiet", // Do not tamper with lintcheck's normal output
"-o",
"perf.data",
"--",
"cargo",
]);
} else {
cmd = Command::new("cargo");
}
cmd.arg(if config.fix { "fix" } else { "check" })
.arg("--quiet")
.current_dir(&self.path)
@ -234,12 +252,22 @@ fn normalize_diag(
}
/// Builds clippy inside the repo to make sure we have a clippy executable we can use.
fn build_clippy() -> String {
let output = Command::new("cargo")
.args(["run", "--bin=clippy-driver", "--", "--version"])
.stderr(Stdio::inherit())
.output()
.unwrap();
fn build_clippy(release_build: bool) -> String {
let mut build_cmd = Command::new("cargo");
build_cmd.args([
"run",
"--bin=clippy-driver",
if release_build { "-r" } else { "" },
"--",
"--version",
]);
if release_build {
build_cmd.env("CARGO_PROFILE_RELEASE_DEBUG", "true");
}
let output = build_cmd.stderr(Stdio::inherit()).output().unwrap();
if !output.status.success() {
eprintln!("Error: Failed to compile Clippy!");
std::process::exit(1);
@ -270,13 +298,18 @@ fn main() {
#[allow(clippy::too_many_lines)]
fn lintcheck(config: LintcheckConfig) {
let clippy_ver = build_clippy();
let clippy_driver_path = fs::canonicalize(format!("target/debug/clippy-driver{EXE_SUFFIX}")).unwrap();
let clippy_ver = build_clippy(config.perf);
let clippy_driver_path = fs::canonicalize(format!(
"target/{}/clippy-driver{EXE_SUFFIX}",
if config.perf { "release" } else { "debug" }
))
.unwrap();
// assert that clippy is found
assert!(
clippy_driver_path.is_file(),
"target/debug/clippy-driver binary not found! {}",
"target/{}/clippy-driver binary not found! {}",
if config.perf { "release" } else { "debug" },
clippy_driver_path.display()
);

View file

@ -1,6 +1,6 @@
[toolchain]
# begin autogenerated nightly
channel = "nightly-2025-01-28"
channel = "nightly-2025-02-06"
# end autogenerated nightly
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
profile = "minimal"

View file

@ -0,0 +1,32 @@
#![warn(clippy::cmp_null)]
#![allow(unused_mut)]
use std::ptr;
fn main() {
let x = 0;
let p: *const usize = &x;
if p.is_null() {
//~^ ERROR: comparing with null is better expressed by the `.is_null()` method
//~| NOTE: `-D clippy::cmp-null` implied by `-D warnings`
println!("This is surprising!");
}
if p.is_null() {
//~^ ERROR: comparing with null is better expressed by the `.is_null()` method
println!("This is surprising!");
}
let mut y = 0;
let mut m: *mut usize = &mut y;
if m.is_null() {
//~^ ERROR: comparing with null is better expressed by the `.is_null()` method
println!("This is surprising, too!");
}
if m.is_null() {
//~^ ERROR: comparing with null is better expressed by the `.is_null()` method
println!("This is surprising, too!");
}
let _ = (x as *const ()).is_null();
//~^ ERROR: comparing with null is better expressed by the `.is_null()` method
}

View file

@ -11,10 +11,22 @@ fn main() {
//~| NOTE: `-D clippy::cmp-null` implied by `-D warnings`
println!("This is surprising!");
}
if ptr::null() == p {
//~^ ERROR: comparing with null is better expressed by the `.is_null()` method
println!("This is surprising!");
}
let mut y = 0;
let mut m: *mut usize = &mut y;
if m == ptr::null_mut() {
//~^ ERROR: comparing with null is better expressed by the `.is_null()` method
println!("This is surprising, too!");
}
if ptr::null_mut() == m {
//~^ ERROR: comparing with null is better expressed by the `.is_null()` method
println!("This is surprising, too!");
}
let _ = x as *const () == ptr::null();
//~^ ERROR: comparing with null is better expressed by the `.is_null()` method
}

View file

@ -2,16 +2,34 @@ error: comparing with null is better expressed by the `.is_null()` method
--> tests/ui/cmp_null.rs:9:8
|
LL | if p == ptr::null() {
| ^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^ help: try: `p.is_null()`
|
= note: `-D clippy::cmp-null` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::cmp_null)]`
error: comparing with null is better expressed by the `.is_null()` method
--> tests/ui/cmp_null.rs:16:8
--> tests/ui/cmp_null.rs:14:8
|
LL | if ptr::null() == p {
| ^^^^^^^^^^^^^^^^ help: try: `p.is_null()`
error: comparing with null is better expressed by the `.is_null()` method
--> tests/ui/cmp_null.rs:21:8
|
LL | if m == ptr::null_mut() {
| ^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^ help: try: `m.is_null()`
error: aborting due to 2 previous errors
error: comparing with null is better expressed by the `.is_null()` method
--> tests/ui/cmp_null.rs:25:8
|
LL | if ptr::null_mut() == m {
| ^^^^^^^^^^^^^^^^^^^^ help: try: `m.is_null()`
error: comparing with null is better expressed by the `.is_null()` method
--> tests/ui/cmp_null.rs:30:13
|
LL | let _ = x as *const () == ptr::null();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(x as *const ()).is_null()`
error: aborting due to 5 previous errors

View file

@ -116,6 +116,11 @@ fn test_redundant_closures_containing_method_calls() {
t.iter().filter(|x| x.trait_foo_ref());
t.iter().map(|x| x.trait_foo_ref());
}
fn issue14096() {
let x = Some("42");
let _ = x.map(str::parse::<i16>);
}
}
struct Thunk<T>(Box<dyn FnMut() -> T>);

View file

@ -116,6 +116,11 @@ fn test_redundant_closures_containing_method_calls() {
t.iter().filter(|x| x.trait_foo_ref());
t.iter().map(|x| x.trait_foo_ref());
}
fn issue14096() {
let x = Some("42");
let _ = x.map(|x| x.parse::<i16>());
}
}
struct Thunk<T>(Box<dyn FnMut() -> T>);

View file

@ -71,142 +71,148 @@ LL | let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_as
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase`
error: redundant closure
--> tests/ui/eta.rs:169:22
--> tests/ui/eta.rs:122:23
|
LL | let _ = x.map(|x| x.parse::<i16>());
| ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `str::parse::<i16>`
error: redundant closure
--> tests/ui/eta.rs:174:22
|
LL | requires_fn_once(|| x());
| ^^^^^^ help: replace the closure with the function itself: `x`
error: redundant closure
--> tests/ui/eta.rs:176:27
--> tests/ui/eta.rs:181:27
|
LL | let a = Some(1u8).map(|a| foo_ptr(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr`
error: redundant closure
--> tests/ui/eta.rs:181:27
--> tests/ui/eta.rs:186:27
|
LL | let a = Some(1u8).map(|a| closure(a));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
error: redundant closure
--> tests/ui/eta.rs:213:28
--> tests/ui/eta.rs:218:28
|
LL | x.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
error: redundant closure
--> tests/ui/eta.rs:214:28
--> tests/ui/eta.rs:219:28
|
LL | y.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
error: redundant closure
--> tests/ui/eta.rs:215:28
--> tests/ui/eta.rs:220:28
|
LL | z.into_iter().for_each(|x| add_to_res(x));
| ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
error: redundant closure
--> tests/ui/eta.rs:222:21
--> tests/ui/eta.rs:227:21
|
LL | Some(1).map(|n| closure(n));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
error: redundant closure
--> tests/ui/eta.rs:226:21
--> tests/ui/eta.rs:231:21
|
LL | Some(1).map(|n| in_loop(n));
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop`
error: redundant closure
--> tests/ui/eta.rs:319:18
--> tests/ui/eta.rs:324:18
|
LL | takes_fn_mut(|| f());
| ^^^^^^ help: replace the closure with the function itself: `&mut f`
error: redundant closure
--> tests/ui/eta.rs:322:19
--> tests/ui/eta.rs:327:19
|
LL | takes_fn_once(|| f());
| ^^^^^^ help: replace the closure with the function itself: `&mut f`
error: redundant closure
--> tests/ui/eta.rs:326:26
--> tests/ui/eta.rs:331:26
|
LL | move || takes_fn_mut(|| f_used_once())
| ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once`
error: redundant closure
--> tests/ui/eta.rs:338:19
--> tests/ui/eta.rs:343:19
|
LL | array_opt.map(|a| a.as_slice());
| ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice`
error: redundant closure
--> tests/ui/eta.rs:341:19
--> tests/ui/eta.rs:346:19
|
LL | slice_opt.map(|s| s.len());
| ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len`
error: redundant closure
--> tests/ui/eta.rs:344:17
--> tests/ui/eta.rs:349:17
|
LL | ptr_opt.map(|p| p.is_null());
| ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null`
error: redundant closure
--> tests/ui/eta.rs:348:17
--> tests/ui/eta.rs:353:17
|
LL | dyn_opt.map(|d| d.method_on_dyn());
| ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<dyn TestTrait>::method_on_dyn`
error: redundant closure
--> tests/ui/eta.rs:408:19
--> tests/ui/eta.rs:413:19
|
LL | let _ = f(&0, |x, y| f2(x, y));
| ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2`
error: redundant closure
--> tests/ui/eta.rs:436:22
--> tests/ui/eta.rs:441:22
|
LL | test.map(|t| t.method())
| ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `Test::method`
error: redundant closure
--> tests/ui/eta.rs:440:22
--> tests/ui/eta.rs:445:22
|
LL | test.map(|t| t.method())
| ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `super::Outer::method`
error: redundant closure
--> tests/ui/eta.rs:453:18
--> tests/ui/eta.rs:458:18
|
LL | test.map(|t| t.method())
| ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `test_mod::Test::method`
error: redundant closure
--> tests/ui/eta.rs:460:30
--> tests/ui/eta.rs:465:30
|
LL | test.map(|t| t.method())
| ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `crate::issue_10854::d::Test::method`
error: redundant closure
--> tests/ui/eta.rs:479:38
--> tests/ui/eta.rs:484:38
|
LL | let x = Box::new(|| None.map(|x| f(x)));
| ^^^^^^^^ help: replace the closure with the function itself: `&f`
error: redundant closure
--> tests/ui/eta.rs:483:38
--> tests/ui/eta.rs:488:38
|
LL | let x = Box::new(|| None.map(|x| f(x)));
| ^^^^^^^^ help: replace the closure with the function itself: `f`
error: redundant closure
--> tests/ui/eta.rs:500:35
--> tests/ui/eta.rs:505:35
|
LL | let _field = bind.or_else(|| get_default()).unwrap();
| ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `get_default`
error: aborting due to 34 previous errors
error: aborting due to 35 previous errors

View file

@ -21,12 +21,15 @@ fn main() {
let _ = foo().map_err(|()| todo!());
//~^ ERROR: matching over `()` is more explicit
println!("{:?}", match foo() {
Ok(()) => {},
//~^ ERROR: matching over `()` is more explicit
Err(()) => {},
//~^ ERROR: matching over `()` is more explicit
});
println!(
"{:?}",
match foo() {
Ok(()) => {},
//~^ ERROR: matching over `()` is more explicit
Err(()) => {},
//~^ ERROR: matching over `()` is more explicit
}
);
}
// ignored_unit_patterns in derive macro should be ok

View file

@ -21,12 +21,15 @@ fn main() {
let _ = foo().map_err(|_| todo!());
//~^ ERROR: matching over `()` is more explicit
println!("{:?}", match foo() {
Ok(_) => {},
//~^ ERROR: matching over `()` is more explicit
Err(_) => {},
//~^ ERROR: matching over `()` is more explicit
});
println!(
"{:?}",
match foo() {
Ok(_) => {},
//~^ ERROR: matching over `()` is more explicit
Err(_) => {},
//~^ ERROR: matching over `()` is more explicit
}
);
}
// ignored_unit_patterns in derive macro should be ok

View file

@ -26,31 +26,31 @@ LL | let _ = foo().map_err(|_| todo!());
| ^ help: use `()` instead of `_`: `()`
error: matching over `()` is more explicit
--> tests/ui/ignored_unit_patterns.rs:25:12
--> tests/ui/ignored_unit_patterns.rs:27:16
|
LL | Ok(_) => {},
| ^ help: use `()` instead of `_`: `()`
LL | Ok(_) => {},
| ^ help: use `()` instead of `_`: `()`
error: matching over `()` is more explicit
--> tests/ui/ignored_unit_patterns.rs:27:13
--> tests/ui/ignored_unit_patterns.rs:29:17
|
LL | Err(_) => {},
| ^ help: use `()` instead of `_`: `()`
LL | Err(_) => {},
| ^ help: use `()` instead of `_`: `()`
error: matching over `()` is more explicit
--> tests/ui/ignored_unit_patterns.rs:38:9
--> tests/ui/ignored_unit_patterns.rs:41:9
|
LL | let _ = foo().unwrap();
| ^ help: use `()` instead of `_`: `()`
error: matching over `()` is more explicit
--> tests/ui/ignored_unit_patterns.rs:47:13
--> tests/ui/ignored_unit_patterns.rs:50:13
|
LL | (1, _) => unimplemented!(),
| ^ help: use `()` instead of `_`: `()`
error: matching over `()` is more explicit
--> tests/ui/ignored_unit_patterns.rs:54:13
--> tests/ui/ignored_unit_patterns.rs:57:13
|
LL | for (x, _) in v {
| ^ help: use `()` instead of `_`: `()`

View file

@ -1,5 +1,10 @@
#![warn(clippy::manual_memcpy)]
#![allow(clippy::assigning_clones, clippy::useless_vec, clippy::needless_range_loop)]
#![allow(
clippy::assigning_clones,
clippy::useless_vec,
clippy::needless_range_loop,
clippy::manual_slice_fill
)]
//@no-rustfix
const LOOP_OFFSET: usize = 5000;

View file

@ -1,5 +1,5 @@
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:9:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:14:5
|
LL | / for i in 0..src.len() {
LL | |
@ -12,7 +12,7 @@ LL | | }
= help: to override `-D warnings` add `#[allow(clippy::manual_memcpy)]`
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:16:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:21:5
|
LL | / for i in 0..src.len() {
LL | |
@ -21,7 +21,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].copy_from_slice(&src[..]);`
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:22:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:27:5
|
LL | / for i in 0..src.len() {
LL | |
@ -30,7 +30,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[10..(src.len() + 10)]);`
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:28:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:33:5
|
LL | / for i in 11..src.len() {
LL | |
@ -39,7 +39,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[11..src.len()].copy_from_slice(&src[(11 - 10)..(src.len() - 10)]);`
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:34:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:39:5
|
LL | / for i in 0..dst.len() {
LL | |
@ -48,7 +48,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..dst.len()]);`
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:48:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:53:5
|
LL | / for i in 10..256 {
LL | |
@ -64,7 +64,7 @@ LL + dst2[(10 + 500)..(256 + 500)].copy_from_slice(&src[10..256]);
|
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:61:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:66:5
|
LL | / for i in 10..LOOP_OFFSET {
LL | |
@ -73,7 +73,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].copy_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);`
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:75:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:80:5
|
LL | / for i in 0..src_vec.len() {
LL | |
@ -82,7 +82,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].copy_from_slice(&src_vec[..]);`
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:105:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:110:5
|
LL | / for i in from..from + src.len() {
LL | |
@ -91,7 +91,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].copy_from_slice(&src[..(from + src.len() - from)]);`
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:110:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:115:5
|
LL | / for i in from..from + 3 {
LL | |
@ -100,7 +100,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[from..(from + 3)].copy_from_slice(&src[..(from + 3 - from)]);`
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:116:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:121:5
|
LL | / for i in 0..5 {
LL | |
@ -109,7 +109,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src);`
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:122:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:127:5
|
LL | / for i in 0..0 {
LL | |
@ -118,7 +118,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[..0].copy_from_slice(&src[..0]);`
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:146:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:151:5
|
LL | / for i in 0..4 {
LL | |
@ -127,7 +127,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[..4]);`
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:152:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:157:5
|
LL | / for i in 0..5 {
LL | |
@ -136,7 +136,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst[..5].copy_from_slice(&src);`
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:158:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:163:5
|
LL | / for i in 0..5 {
LL | |
@ -145,7 +145,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src);`
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:205:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:210:5
|
LL | / for i in 0..5 {
LL | |
@ -154,7 +154,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[0]);`
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:211:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:216:5
|
LL | / for i in 0..5 {
LL | |
@ -163,7 +163,7 @@ LL | | }
| |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[0][1]);`
error: it looks like you're manually copying between slices
--> tests/ui/manual_memcpy/without_loop_counters.rs:219:5
--> tests/ui/manual_memcpy/without_loop_counters.rs:224:5
|
LL | / for i in 0..src.len() {
LL | |

View file

@ -0,0 +1,62 @@
#![warn(clippy::manual_option_as_slice)]
#![allow(clippy::redundant_closure, clippy::unwrap_or_default)]
fn check(x: Option<u32>) {
_ = x.as_slice();
_ = x.as_slice();
_ = x.as_slice();
//~^ manual_option_as_slice
_ = x.as_slice();
//~^ manual_option_as_slice
_ = x.as_slice();
//~^ manual_option_as_slice
_ = x.as_slice();
//~^ manual_option_as_slice
{
use std::slice::from_ref;
_ = x.as_slice();
//~^ manual_option_as_slice
}
// possible false positives
let y = x.as_ref();
_ = match y {
// as_ref outside
Some(f) => &[f][..],
None => &[][..],
};
_ = match x.as_ref() {
Some(f) => std::slice::from_ref(f),
None => &[0],
};
_ = match x.as_ref() {
Some(42) => &[23],
Some(f) => std::slice::from_ref(f),
None => &[],
};
let b = &[42];
_ = if let Some(_f) = x.as_ref() {
std::slice::from_ref(b)
} else {
&[]
};
_ = x.as_ref().map_or(&[42][..], std::slice::from_ref);
_ = x.as_ref().map_or_else(|| &[42][..1], std::slice::from_ref);
_ = x.as_ref().map(|f| std::slice::from_ref(f)).unwrap_or_default();
}
#[clippy::msrv = "1.74"]
fn check_msrv(x: Option<u32>) {
_ = x.as_ref().map_or(&[][..], std::slice::from_ref);
}
fn main() {
check(Some(1));
check_msrv(Some(175));
}

View file

@ -0,0 +1,71 @@
#![warn(clippy::manual_option_as_slice)]
#![allow(clippy::redundant_closure, clippy::unwrap_or_default)]
fn check(x: Option<u32>) {
_ = match x.as_ref() {
//~^ manual_option_as_slice
Some(f) => std::slice::from_ref(f),
None => &[],
};
_ = if let Some(f) = x.as_ref() {
//~^ manual_option_as_slice
std::slice::from_ref(f)
} else {
&[]
};
_ = x.as_ref().map_or(&[][..], std::slice::from_ref);
//~^ manual_option_as_slice
_ = x.as_ref().map_or_else(Default::default, std::slice::from_ref);
//~^ manual_option_as_slice
_ = x.as_ref().map(std::slice::from_ref).unwrap_or_default();
//~^ manual_option_as_slice
_ = x.as_ref().map_or_else(|| &[42][..0], std::slice::from_ref);
//~^ manual_option_as_slice
{
use std::slice::from_ref;
_ = x.as_ref().map_or_else(<&[_]>::default, from_ref);
//~^ manual_option_as_slice
}
// possible false positives
let y = x.as_ref();
_ = match y {
// as_ref outside
Some(f) => &[f][..],
None => &[][..],
};
_ = match x.as_ref() {
Some(f) => std::slice::from_ref(f),
None => &[0],
};
_ = match x.as_ref() {
Some(42) => &[23],
Some(f) => std::slice::from_ref(f),
None => &[],
};
let b = &[42];
_ = if let Some(_f) = x.as_ref() {
std::slice::from_ref(b)
} else {
&[]
};
_ = x.as_ref().map_or(&[42][..], std::slice::from_ref);
_ = x.as_ref().map_or_else(|| &[42][..1], std::slice::from_ref);
_ = x.as_ref().map(|f| std::slice::from_ref(f)).unwrap_or_default();
}
#[clippy::msrv = "1.74"]
fn check_msrv(x: Option<u32>) {
_ = x.as_ref().map_or(&[][..], std::slice::from_ref);
}
fn main() {
check(Some(1));
check_msrv(Some(175));
}

View file

@ -0,0 +1,58 @@
error: use `Option::as_slice`
--> tests/ui/manual_option_as_slice.rs:5:9
|
LL | _ = match x.as_ref() {
| _________^
LL | |
LL | | Some(f) => std::slice::from_ref(f),
LL | | None => &[],
LL | | };
| |_____^ help: use: `x.as_slice()`
|
= note: `-D clippy::manual-option-as-slice` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::manual_option_as_slice)]`
error: use `Option::as_slice`
--> tests/ui/manual_option_as_slice.rs:11:9
|
LL | _ = if let Some(f) = x.as_ref() {
| _________^
LL | |
LL | | std::slice::from_ref(f)
LL | | } else {
LL | | &[]
LL | | };
| |_____^ help: use: `x.as_slice()`
error: use `Option::as_slice`
--> tests/ui/manual_option_as_slice.rs:18:9
|
LL | _ = x.as_ref().map_or(&[][..], std::slice::from_ref);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()`
error: use `Option::as_slice`
--> tests/ui/manual_option_as_slice.rs:21:9
|
LL | _ = x.as_ref().map_or_else(Default::default, std::slice::from_ref);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()`
error: use `Option::as_slice`
--> tests/ui/manual_option_as_slice.rs:24:9
|
LL | _ = x.as_ref().map(std::slice::from_ref).unwrap_or_default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()`
error: use `Option::as_slice`
--> tests/ui/manual_option_as_slice.rs:27:9
|
LL | _ = x.as_ref().map_or_else(|| &[42][..0], std::slice::from_ref);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()`
error: use `Option::as_slice`
--> tests/ui/manual_option_as_slice.rs:32:13
|
LL | _ = x.as_ref().map_or_else(<&[_]>::default, from_ref);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.as_slice()`
error: aborting due to 7 previous errors

View file

@ -0,0 +1,101 @@
#![warn(clippy::manual_slice_fill)]
#![allow(clippy::needless_range_loop)]
macro_rules! assign_element {
($slice:ident, $index:expr) => {
$slice[$index] = 0;
};
}
macro_rules! assign_element_2 {
($i:expr) => {
$i = 0;
};
}
struct NoClone;
fn num() -> usize {
5
}
fn should_lint() {
let mut some_slice = [1, 2, 3, 4, 5];
some_slice.fill(0);
let x = 5;
some_slice.fill(x);
some_slice.fill(0);
// This should trigger `manual_slice_fill`, but the applicability is `MaybeIncorrect` since comments
// within the loop might be purely informational.
some_slice.fill(0);
}
fn should_not_lint() {
let mut some_slice = [1, 2, 3, 4, 5];
// Should not lint because we can't determine if the scope of the loop is intended to access all the
// elements of the slice.
for i in 0..5 {
some_slice[i] = 0;
}
// Should not lint, as using a function to assign values to elements might be
// intentional. For example, the contents of `num()` could be temporary and subject to change
// later.
for i in 0..some_slice.len() {
some_slice[i] = num();
}
// Should not lint because this loop isn't equivalent to `fill`.
for i in 0..some_slice.len() {
some_slice[i] = 0;
println!("foo");
}
// Should not lint because it may be intentional to use a macro to perform an operation equivalent
// to `fill`.
for i in 0..some_slice.len() {
assign_element!(some_slice, i);
}
let another_slice = [1, 2, 3];
// Should not lint because the range is not for `some_slice`.
for i in 0..another_slice.len() {
some_slice[i] = 0;
}
let mut vec: Vec<Option<NoClone>> = Vec::with_capacity(5);
// Should not lint because `NoClone` does not have `Clone` trait.
for i in 0..vec.len() {
vec[i] = None;
}
// Should not lint, as using a function to assign values to elements might be
// intentional. For example, the contents of `num()` could be temporary and subject to change
// later.
for i in &mut some_slice {
*i = num();
}
// Should not lint because this loop isn't equivalent to `fill`.
for i in &mut some_slice {
*i = 0;
println!("foo");
}
// Should not lint because it may be intentional to use a macro to perform an operation equivalent
// to `fill`.
for i in &mut some_slice {
assign_element_2!(*i);
}
let mut vec: Vec<Option<NoClone>> = Vec::with_capacity(5);
// Should not lint because `NoClone` does not have `Clone` trait.
for i in &mut vec {
*i = None;
}
}

View file

@ -0,0 +1,110 @@
#![warn(clippy::manual_slice_fill)]
#![allow(clippy::needless_range_loop)]
macro_rules! assign_element {
($slice:ident, $index:expr) => {
$slice[$index] = 0;
};
}
macro_rules! assign_element_2 {
($i:expr) => {
$i = 0;
};
}
struct NoClone;
fn num() -> usize {
5
}
fn should_lint() {
let mut some_slice = [1, 2, 3, 4, 5];
for i in 0..some_slice.len() {
some_slice[i] = 0;
}
let x = 5;
for i in 0..some_slice.len() {
some_slice[i] = x;
}
for i in &mut some_slice {
*i = 0;
}
// This should trigger `manual_slice_fill`, but the applicability is `MaybeIncorrect` since comments
// within the loop might be purely informational.
for i in 0..some_slice.len() {
some_slice[i] = 0;
// foo
}
}
fn should_not_lint() {
let mut some_slice = [1, 2, 3, 4, 5];
// Should not lint because we can't determine if the scope of the loop is intended to access all the
// elements of the slice.
for i in 0..5 {
some_slice[i] = 0;
}
// Should not lint, as using a function to assign values to elements might be
// intentional. For example, the contents of `num()` could be temporary and subject to change
// later.
for i in 0..some_slice.len() {
some_slice[i] = num();
}
// Should not lint because this loop isn't equivalent to `fill`.
for i in 0..some_slice.len() {
some_slice[i] = 0;
println!("foo");
}
// Should not lint because it may be intentional to use a macro to perform an operation equivalent
// to `fill`.
for i in 0..some_slice.len() {
assign_element!(some_slice, i);
}
let another_slice = [1, 2, 3];
// Should not lint because the range is not for `some_slice`.
for i in 0..another_slice.len() {
some_slice[i] = 0;
}
let mut vec: Vec<Option<NoClone>> = Vec::with_capacity(5);
// Should not lint because `NoClone` does not have `Clone` trait.
for i in 0..vec.len() {
vec[i] = None;
}
// Should not lint, as using a function to assign values to elements might be
// intentional. For example, the contents of `num()` could be temporary and subject to change
// later.
for i in &mut some_slice {
*i = num();
}
// Should not lint because this loop isn't equivalent to `fill`.
for i in &mut some_slice {
*i = 0;
println!("foo");
}
// Should not lint because it may be intentional to use a macro to perform an operation equivalent
// to `fill`.
for i in &mut some_slice {
assign_element_2!(*i);
}
let mut vec: Vec<Option<NoClone>> = Vec::with_capacity(5);
// Should not lint because `NoClone` does not have `Clone` trait.
for i in &mut vec {
*i = None;
}
}

View file

@ -0,0 +1,38 @@
error: manually filling a slice
--> tests/ui/manual_slice_fill.rs:25:5
|
LL | / for i in 0..some_slice.len() {
LL | | some_slice[i] = 0;
LL | | }
| |_____^ help: try: `some_slice.fill(0);`
|
= note: `-D clippy::manual-slice-fill` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::manual_slice_fill)]`
error: manually filling a slice
--> tests/ui/manual_slice_fill.rs:30:5
|
LL | / for i in 0..some_slice.len() {
LL | | some_slice[i] = x;
LL | | }
| |_____^ help: try: `some_slice.fill(x);`
error: manually filling a slice
--> tests/ui/manual_slice_fill.rs:34:5
|
LL | / for i in &mut some_slice {
LL | | *i = 0;
LL | | }
| |_____^ help: try: `some_slice.fill(0);`
error: manually filling a slice
--> tests/ui/manual_slice_fill.rs:40:5
|
LL | / for i in 0..some_slice.len() {
LL | | some_slice[i] = 0;
LL | | // foo
LL | | }
| |_____^ help: try: `some_slice.fill(0);`
error: aborting due to 4 previous errors

View file

@ -0,0 +1,15 @@
//@no-rustfix
fn issue_12670() {
// no auto: type not found
#[allow(clippy::match_result_ok)]
let _ = if let Some(x) = "1".parse().ok() {
x
} else {
i32::default()
};
let _ = if let Some(x) = None { x } else { i32::default() };
// auto fix with unwrap_or_default
let a: Option<i32> = None;
let _ = if let Some(x) = a { x } else { i32::default() };
let _ = if let Some(x) = Some(99) { x } else { i32::default() };
}

View file

@ -0,0 +1,34 @@
error: if let can be simplified with `.unwrap_or_default()`
--> tests/ui/manual_unwrap_or_default_unfixable.rs:5:13
|
LL | let _ = if let Some(x) = "1".parse().ok() {
| _____________^
LL | | x
LL | | } else {
LL | | i32::default()
LL | | };
| |_____^ help: ascribe the type i32 and replace your expression with: `"1".parse().ok().unwrap_or_default()`
|
= note: `-D clippy::manual-unwrap-or-default` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::manual_unwrap_or_default)]`
error: if let can be simplified with `.unwrap_or_default()`
--> tests/ui/manual_unwrap_or_default_unfixable.rs:10:13
|
LL | let _ = if let Some(x) = None { x } else { i32::default() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `None::<i32>.unwrap_or_default()`
error: if let can be simplified with `.unwrap_or_default()`
--> tests/ui/manual_unwrap_or_default_unfixable.rs:13:13
|
LL | let _ = if let Some(x) = a { x } else { i32::default() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.unwrap_or_default()`
error: if let can be simplified with `.unwrap_or_default()`
--> tests/ui/manual_unwrap_or_default_unfixable.rs:14:13
|
LL | let _ = if let Some(x) = Some(99) { x } else { i32::default() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(99).unwrap_or_default()`
error: aborting due to 4 previous errors

View file

@ -0,0 +1,58 @@
struct MyStruct;
impl MyStruct {
pub fn get_option() -> Option<Self> {
todo!()
}
}
fn return_option() -> Option<i32> {
todo!()
}
fn main() {
println!("Testing non erroneous option_take_on_temporary");
let mut option = Some(1);
let _ = Box::new(move || option.take().unwrap());
println!("Testing non erroneous option_take_on_temporary");
let x = Some(3);
x.as_ref();
let x = Some(3);
x.as_ref();
//~^ ERROR: called `Option::take()` on a temporary value
println!("Testing non erroneous option_take_on_temporary");
let mut x = Some(3);
let y = x.as_mut();
let mut x = Some(3);
let y = x.as_mut();
//~^ ERROR: called `Option::take()` on a temporary value
let y = x.replace(289);
//~^ ERROR: called `Option::take()` on a temporary value
let y = Some(3).as_mut();
//~^ ERROR: called `Option::take()` on a temporary value
let y = Option::as_mut(&mut x);
//~^ ERROR: called `Option::take()` on a temporary value
let x = return_option();
let x = return_option();
//~^ ERROR: called `Option::take()` on a temporary value
let x = MyStruct::get_option();
let x = MyStruct::get_option();
//~^ ERROR: called `Option::take()` on a temporary value
let mut my_vec = vec![1, 2, 3];
my_vec.push(4);
let y = my_vec.first();
let y = my_vec.first();
//~^ ERROR: called `Option::take()` on a temporary value
let y = my_vec.first();
//~^ ERROR: called `Option::take()` on a temporary value
}

View file

@ -2,7 +2,9 @@ error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:23:5
|
LL | x.as_ref().take();
| ^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^-------
| |
| help: remove
|
= note: `as_ref` creates a temporary value, so calling take() has no effect
= note: `-D clippy::needless-option-take` implied by `-D warnings`
@ -12,7 +14,9 @@ error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:31:13
|
LL | let y = x.as_mut().take();
| ^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^-------
| |
| help: remove
|
= note: `as_mut` creates a temporary value, so calling take() has no effect
@ -20,7 +24,9 @@ error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:33:13
|
LL | let y = x.replace(289).take();
| ^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^-------
| |
| help: remove
|
= note: `replace` creates a temporary value, so calling take() has no effect
@ -28,7 +34,9 @@ error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:36:13
|
LL | let y = Some(3).as_mut().take();
| ^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^-------
| |
| help: remove
|
= note: `as_mut` creates a temporary value, so calling take() has no effect
@ -36,7 +44,9 @@ error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:39:13
|
LL | let y = Option::as_mut(&mut x).take();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^-------
| |
| help: remove
|
= note: `as_mut` creates a temporary value, so calling take() has no effect
@ -44,7 +54,9 @@ error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:43:13
|
LL | let x = return_option().take();
| ^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^-------
| |
| help: remove
|
= note: `return_option` creates a temporary value, so calling take() has no effect
@ -52,7 +64,9 @@ error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:47:13
|
LL | let x = MyStruct::get_option().take();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^-------
| |
| help: remove
|
= note: `get_option` creates a temporary value, so calling take() has no effect
@ -60,7 +74,9 @@ error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:53:13
|
LL | let y = my_vec.first().take();
| ^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^-------
| |
| help: remove
|
= note: `first` creates a temporary value, so calling take() has no effect
@ -68,7 +84,9 @@ error: called `Option::take()` on a temporary value
--> tests/ui/needless_option_take.rs:56:13
|
LL | let y = my_vec.first().take();
| ^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^-------
| |
| help: remove
|
= note: `first` creates a temporary value, so calling take() has no effect

View file

@ -2,7 +2,8 @@
#![allow(
clippy::uninlined_format_args,
clippy::unnecessary_literal_unwrap,
clippy::useless_vec
clippy::useless_vec,
clippy::manual_slice_fill
)]
//@no-rustfix
static STATIC: [usize; 4] = [0, 1, 8, 16];

View file

@ -1,5 +1,5 @@
error: the loop variable `i` is only used to index `vec`
--> tests/ui/needless_range_loop.rs:15:14
--> tests/ui/needless_range_loop.rs:16:14
|
LL | for i in 0..vec.len() {
| ^^^^^^^^^^^^
@ -12,7 +12,7 @@ LL | for <item> in &vec {
| ~~~~~~ ~~~~
error: the loop variable `i` is only used to index `vec`
--> tests/ui/needless_range_loop.rs:26:14
--> tests/ui/needless_range_loop.rs:27:14
|
LL | for i in 0..vec.len() {
| ^^^^^^^^^^^^
@ -23,7 +23,7 @@ LL | for <item> in &vec {
| ~~~~~~ ~~~~
error: the loop variable `j` is only used to index `STATIC`
--> tests/ui/needless_range_loop.rs:32:14
--> tests/ui/needless_range_loop.rs:33:14
|
LL | for j in 0..4 {
| ^^^^
@ -34,7 +34,7 @@ LL | for <item> in &STATIC {
| ~~~~~~ ~~~~~~~
error: the loop variable `j` is only used to index `CONST`
--> tests/ui/needless_range_loop.rs:37:14
--> tests/ui/needless_range_loop.rs:38:14
|
LL | for j in 0..4 {
| ^^^^
@ -45,7 +45,7 @@ LL | for <item> in &CONST {
| ~~~~~~ ~~~~~~
error: the loop variable `i` is used to index `vec`
--> tests/ui/needless_range_loop.rs:42:14
--> tests/ui/needless_range_loop.rs:43:14
|
LL | for i in 0..vec.len() {
| ^^^^^^^^^^^^
@ -56,7 +56,7 @@ LL | for (i, <item>) in vec.iter().enumerate() {
| ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is only used to index `vec2`
--> tests/ui/needless_range_loop.rs:51:14
--> tests/ui/needless_range_loop.rs:52:14
|
LL | for i in 0..vec.len() {
| ^^^^^^^^^^^^
@ -67,7 +67,7 @@ LL | for <item> in vec2.iter().take(vec.len()) {
| ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is only used to index `vec`
--> tests/ui/needless_range_loop.rs:56:14
--> tests/ui/needless_range_loop.rs:57:14
|
LL | for i in 5..vec.len() {
| ^^^^^^^^^^^^
@ -78,7 +78,7 @@ LL | for <item> in vec.iter().skip(5) {
| ~~~~~~ ~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is only used to index `vec`
--> tests/ui/needless_range_loop.rs:61:14
--> tests/ui/needless_range_loop.rs:62:14
|
LL | for i in 0..MAX_LEN {
| ^^^^^^^^^^
@ -89,7 +89,7 @@ LL | for <item> in vec.iter().take(MAX_LEN) {
| ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is only used to index `vec`
--> tests/ui/needless_range_loop.rs:66:14
--> tests/ui/needless_range_loop.rs:67:14
|
LL | for i in 0..=MAX_LEN {
| ^^^^^^^^^^^
@ -100,7 +100,7 @@ LL | for <item> in vec.iter().take(MAX_LEN + 1) {
| ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is only used to index `vec`
--> tests/ui/needless_range_loop.rs:71:14
--> tests/ui/needless_range_loop.rs:72:14
|
LL | for i in 5..10 {
| ^^^^^
@ -111,7 +111,7 @@ LL | for <item> in vec.iter().take(10).skip(5) {
| ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is only used to index `vec`
--> tests/ui/needless_range_loop.rs:76:14
--> tests/ui/needless_range_loop.rs:77:14
|
LL | for i in 5..=10 {
| ^^^^^^
@ -122,7 +122,7 @@ LL | for <item> in vec.iter().take(10 + 1).skip(5) {
| ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is used to index `vec`
--> tests/ui/needless_range_loop.rs:81:14
--> tests/ui/needless_range_loop.rs:82:14
|
LL | for i in 5..vec.len() {
| ^^^^^^^^^^^^
@ -133,7 +133,7 @@ LL | for (i, <item>) in vec.iter().enumerate().skip(5) {
| ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is used to index `vec`
--> tests/ui/needless_range_loop.rs:86:14
--> tests/ui/needless_range_loop.rs:87:14
|
LL | for i in 5..10 {
| ^^^^^
@ -144,7 +144,7 @@ LL | for (i, <item>) in vec.iter().enumerate().take(10).skip(5) {
| ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: the loop variable `i` is used to index `vec`
--> tests/ui/needless_range_loop.rs:92:14
--> tests/ui/needless_range_loop.rs:93:14
|
LL | for i in 0..vec.len() {
| ^^^^^^^^^^^^

View file

@ -20,10 +20,10 @@ fn main() {
1 ^ (1 - 1);
3 | (2 - 1);
3 & (5 - 2);
0x0F00 & (0x00F0 << 4);
0x0F00 & (0xF000 >> 4);
(0x0F00 << 1) ^ 3;
(0x0F00 << 1) | 2;
0x0F00 & 0x00F0 << 4;
0x0F00 & 0xF000 >> 4;
0x0F00 << 1 ^ 3;
0x0F00 << 1 | 2;
let b = 3;
trip!(b * 8);

View file

@ -43,29 +43,5 @@ error: operator precedence might not be obvious
LL | 3 & 5 - 2;
| ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)`
error: operator precedence might not be obvious
--> tests/ui/precedence.rs:23:5
|
LL | 0x0F00 & 0x00F0 << 4;
| ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0x00F0 << 4)`
error: operator precedence might not be obvious
--> tests/ui/precedence.rs:24:5
|
LL | 0x0F00 & 0xF000 >> 4;
| ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0xF000 >> 4)`
error: operator precedence might not be obvious
--> tests/ui/precedence.rs:25:5
|
LL | 0x0F00 << 1 ^ 3;
| ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) ^ 3`
error: operator precedence might not be obvious
--> tests/ui/precedence.rs:26:5
|
LL | 0x0F00 << 1 | 2;
| ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) | 2`
error: aborting due to 11 previous errors
error: aborting due to 7 previous errors

View file

@ -0,0 +1,35 @@
#![warn(clippy::precedence_bits)]
#![allow(
unused_must_use,
clippy::no_effect,
clippy::unnecessary_operation,
clippy::precedence
)]
#![allow(clippy::identity_op)]
#![allow(clippy::eq_op)]
macro_rules! trip {
($a:expr) => {
match $a & 0b1111_1111u8 {
0 => println!("a is zero ({})", $a),
_ => println!("a is {}", $a),
}
};
}
fn main() {
1 << 2 + 3;
1 + 2 << 3;
4 >> 1 + 1;
1 + 3 >> 2;
1 ^ 1 - 1;
3 | 2 - 1;
3 & 5 - 2;
0x0F00 & (0x00F0 << 4);
0x0F00 & (0xF000 >> 4);
(0x0F00 << 1) ^ 3;
(0x0F00 << 1) | 2;
let b = 3;
trip!(b * 8);
}

View file

@ -0,0 +1,35 @@
#![warn(clippy::precedence_bits)]
#![allow(
unused_must_use,
clippy::no_effect,
clippy::unnecessary_operation,
clippy::precedence
)]
#![allow(clippy::identity_op)]
#![allow(clippy::eq_op)]
macro_rules! trip {
($a:expr) => {
match $a & 0b1111_1111u8 {
0 => println!("a is zero ({})", $a),
_ => println!("a is {}", $a),
}
};
}
fn main() {
1 << 2 + 3;
1 + 2 << 3;
4 >> 1 + 1;
1 + 3 >> 2;
1 ^ 1 - 1;
3 | 2 - 1;
3 & 5 - 2;
0x0F00 & 0x00F0 << 4;
0x0F00 & 0xF000 >> 4;
0x0F00 << 1 ^ 3;
0x0F00 << 1 | 2;
let b = 3;
trip!(b * 8);
}

View file

@ -0,0 +1,29 @@
error: operator precedence might not be obvious
--> tests/ui/precedence_bits.rs:28:5
|
LL | 0x0F00 & 0x00F0 << 4;
| ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0x00F0 << 4)`
|
= note: `-D clippy::precedence-bits` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::precedence_bits)]`
error: operator precedence might not be obvious
--> tests/ui/precedence_bits.rs:29:5
|
LL | 0x0F00 & 0xF000 >> 4;
| ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0xF000 >> 4)`
error: operator precedence might not be obvious
--> tests/ui/precedence_bits.rs:30:5
|
LL | 0x0F00 << 1 ^ 3;
| ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) ^ 3`
error: operator precedence might not be obvious
--> tests/ui/precedence_bits.rs:31:5
|
LL | 0x0F00 << 1 | 2;
| ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) | 2`
error: aborting due to 4 previous errors

View file

@ -66,3 +66,17 @@ fn main() {
println!("mixed: {{hello}} {world}");
}
fn issue_13959() {
println!("\"");
println!(
"
foo
\\
\\\\
\"
\\\"
bar
"
);
}

View file

@ -66,3 +66,18 @@ fn main() {
println!("mixed: {} {world}", "{hello}");
}
fn issue_13959() {
println!("{}", r#"""#);
println!(
"{}",
r#"
foo
\
\\
"
\"
bar
"#
);
}

View file

@ -192,5 +192,41 @@ LL - println!("mixed: {} {world}", "{hello}");
LL + println!("mixed: {{hello}} {world}");
|
error: aborting due to 16 previous errors
error: literal with an empty format string
--> tests/ui/print_literal.rs:71:20
|
LL | println!("{}", r#"""#);
| ^^^^^^
|
help: try
|
LL - println!("{}", r#"""#);
LL + println!("\"");
|
error: literal with an empty format string
--> tests/ui/print_literal.rs:74:9
|
LL | / r#"
LL | | foo
LL | | \
LL | | \\
... |
LL | | bar
LL | | "#
| |__^
|
help: try
|
LL ~ "
LL + foo
LL + \\
LL + \\\\
LL + \"
LL + \\\"
LL + bar
LL ~ "
|
error: aborting due to 18 previous errors

View file

@ -0,0 +1,154 @@
#![warn(clippy::redundant_else)]
#![allow(clippy::needless_return, clippy::if_same_then_else, clippy::needless_late_init)]
fn main() {
loop {
// break
if foo() {
println!("Love your neighbor;");
break;
}
//~^ ERROR: redundant else block
println!("yet don't pull down your hedge.");
// continue
if foo() {
println!("He that lies down with Dogs,");
continue;
}
//~^ ERROR: redundant else block
println!("shall rise up with fleas.");
// match block
if foo() {
match foo() {
1 => break,
_ => return,
}
}
//~^ ERROR: redundant else block
println!("You may delay, but time will not.");
}
// else if
if foo() {
return;
} else if foo() {
return;
}
//~^ ERROR: redundant else block
println!("A fat kitchen makes a lean will.");
// let binding outside of block
let _ = {
if foo() {
return;
}
//~^ ERROR: redundant else block
1
};
// else if with let binding outside of block
let _ = {
if foo() {
return;
} else if foo() {
return;
}
//~^ ERROR: redundant else block
2
};
// inside if let
let _ = if let Some(1) = foo() {
let _ = 1;
if foo() {
return;
}
//~^ ERROR: redundant else block
1
} else {
1
};
//
// non-lint cases
//
// sanity check
if foo() {
let _ = 1;
} else {
println!("Who is wise? He that learns from every one.");
}
// else if without else
if foo() {
return;
} else if foo() {
foo()
};
// nested if return
if foo() {
if foo() {
return;
}
} else {
foo()
};
// match with non-breaking branch
if foo() {
match foo() {
1 => foo(),
_ => return,
}
} else {
println!("Three may keep a secret, if two of them are dead.");
}
// let binding
let _ = if foo() {
return;
} else {
1
};
// assign
let mut a;
a = if foo() {
return;
} else {
1
};
// assign-op
a += if foo() {
return;
} else {
1
};
// if return else if else
if foo() {
return;
} else if foo() {
1
} else {
2
};
// if else if return else
if foo() {
1
} else if foo() {
return;
} else {
2
};
// else if with let binding
let _ = if foo() {
return;
} else if foo() {
return;
} else {
2
};
// inside function call
Box::new(if foo() {
return;
} else {
1
});
}
fn foo<T>() -> T {
unimplemented!("I'm not Santa Claus")
}

View file

@ -1,88 +1,123 @@
error: redundant else block
--> tests/ui/redundant_else.rs:10:16
--> tests/ui/redundant_else.rs:10:10
|
LL | } else {
| ________________^
| __________^
LL | |
LL | | println!("yet don't pull down your hedge.");
LL | | }
| |_________^
|
= help: remove the `else` block and move the contents out
= note: `-D clippy::redundant-else` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::redundant_else)]`
help: remove the `else` block and move the contents out
|
LL ~ }
LL +
LL + println!("yet don't pull down your hedge.");
|
error: redundant else block
--> tests/ui/redundant_else.rs:18:16
--> tests/ui/redundant_else.rs:18:10
|
LL | } else {
| ________________^
| __________^
LL | |
LL | | println!("shall rise up with fleas.");
LL | | }
| |_________^
|
= help: remove the `else` block and move the contents out
help: remove the `else` block and move the contents out
|
LL ~ }
LL +
LL + println!("shall rise up with fleas.");
|
error: redundant else block
--> tests/ui/redundant_else.rs:28:16
--> tests/ui/redundant_else.rs:28:10
|
LL | } else {
| ________________^
| __________^
LL | |
LL | | println!("You may delay, but time will not.");
LL | | }
| |_________^
|
= help: remove the `else` block and move the contents out
help: remove the `else` block and move the contents out
|
LL ~ }
LL +
LL + println!("You may delay, but time will not.");
|
error: redundant else block
--> tests/ui/redundant_else.rs:38:12
--> tests/ui/redundant_else.rs:38:6
|
LL | } else {
| ____________^
| ______^
LL | |
LL | | println!("A fat kitchen makes a lean will.");
LL | | }
| |_____^
|
= help: remove the `else` block and move the contents out
help: remove the `else` block and move the contents out
|
LL ~ }
LL +
LL + println!("A fat kitchen makes a lean will.");
|
error: redundant else block
--> tests/ui/redundant_else.rs:46:16
--> tests/ui/redundant_else.rs:46:10
|
LL | } else {
| ________________^
| __________^
LL | |
LL | | 1
LL | | }
| |_________^
|
= help: remove the `else` block and move the contents out
help: remove the `else` block and move the contents out
|
LL ~ }
LL +
LL + 1
|
error: redundant else block
--> tests/ui/redundant_else.rs:57:16
--> tests/ui/redundant_else.rs:57:10
|
LL | } else {
| ________________^
| __________^
LL | |
LL | | 2
LL | | }
| |_________^
|
= help: remove the `else` block and move the contents out
help: remove the `else` block and move the contents out
|
LL ~ }
LL +
LL + 2
|
error: redundant else block
--> tests/ui/redundant_else.rs:67:16
--> tests/ui/redundant_else.rs:67:10
|
LL | } else {
| ________________^
| __________^
LL | |
LL | | 1
LL | | }
| |_________^
|
= help: remove the `else` block and move the contents out
help: remove the `else` block and move the contents out
|
LL ~ }
LL +
LL + 1
|
error: aborting due to 7 previous errors

View file

@ -0,0 +1,67 @@
#![warn(clippy::return_and_then)]
fn main() {
fn test_opt_block(opt: Option<i32>) -> Option<i32> {
let n = opt?;
let mut ret = n + 1;
ret += n;
if n > 1 { Some(ret) } else { None }
}
fn test_opt_func(opt: Option<i32>) -> Option<i32> {
let n = opt?;
test_opt_block(Some(n))
}
fn test_call_chain() -> Option<i32> {
let n = gen_option(1)?;
test_opt_block(Some(n))
}
fn test_res_block(opt: Result<i32, i32>) -> Result<i32, i32> {
let n = opt?;
if n > 1 { Ok(n + 1) } else { Err(n) }
}
fn test_res_func(opt: Result<i32, i32>) -> Result<i32, i32> {
let n = opt?;
test_res_block(Ok(n))
}
fn test_ref_only() -> Option<i32> {
// ref: empty string
let x = Some("")?;
if x.len() > 2 { Some(3) } else { None }
}
fn test_tmp_only() -> Option<i32> {
// unused temporary: vec![1, 2, 4]
let x = Some(match (vec![1, 2, 3], vec![1, 2, 4]) {
(a, _) if a.len() > 1 => a,
(_, b) => b,
})?;
if x.len() > 2 { Some(3) } else { None }
}
// should not lint
fn test_tmp_ref() -> Option<String> {
String::from("<BOOM>")
.strip_prefix("<")
.and_then(|s| s.strip_suffix(">").map(String::from))
}
// should not lint
fn test_unconsumed_tmp() -> Option<i32> {
[1, 2, 3]
.iter()
.map(|x| x + 1)
.collect::<Vec<_>>() // temporary Vec created here
.as_slice() // creates temporary slice
.first() // creates temporary reference
.and_then(|x| test_opt_block(Some(*x)))
}
}
fn gen_option(n: i32) -> Option<i32> {
Some(n)
}

View file

@ -0,0 +1,63 @@
#![warn(clippy::return_and_then)]
fn main() {
fn test_opt_block(opt: Option<i32>) -> Option<i32> {
opt.and_then(|n| {
let mut ret = n + 1;
ret += n;
if n > 1 { Some(ret) } else { None }
})
}
fn test_opt_func(opt: Option<i32>) -> Option<i32> {
opt.and_then(|n| test_opt_block(Some(n)))
}
fn test_call_chain() -> Option<i32> {
gen_option(1).and_then(|n| test_opt_block(Some(n)))
}
fn test_res_block(opt: Result<i32, i32>) -> Result<i32, i32> {
opt.and_then(|n| if n > 1 { Ok(n + 1) } else { Err(n) })
}
fn test_res_func(opt: Result<i32, i32>) -> Result<i32, i32> {
opt.and_then(|n| test_res_block(Ok(n)))
}
fn test_ref_only() -> Option<i32> {
// ref: empty string
Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None })
}
fn test_tmp_only() -> Option<i32> {
// unused temporary: vec![1, 2, 4]
Some(match (vec![1, 2, 3], vec![1, 2, 4]) {
(a, _) if a.len() > 1 => a,
(_, b) => b,
})
.and_then(|x| if x.len() > 2 { Some(3) } else { None })
}
// should not lint
fn test_tmp_ref() -> Option<String> {
String::from("<BOOM>")
.strip_prefix("<")
.and_then(|s| s.strip_suffix(">").map(String::from))
}
// should not lint
fn test_unconsumed_tmp() -> Option<i32> {
[1, 2, 3]
.iter()
.map(|x| x + 1)
.collect::<Vec<_>>() // temporary Vec created here
.as_slice() // creates temporary slice
.first() // creates temporary reference
.and_then(|x| test_opt_block(Some(*x)))
}
}
fn gen_option(n: i32) -> Option<i32> {
Some(n)
}

View file

@ -0,0 +1,101 @@
error: use the question mark operator instead of an `and_then` call
--> tests/ui/return_and_then.rs:5:9
|
LL | / opt.and_then(|n| {
LL | | let mut ret = n + 1;
LL | | ret += n;
LL | | if n > 1 { Some(ret) } else { None }
LL | | })
| |__________^
|
= note: `-D clippy::return-and-then` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::return_and_then)]`
help: try
|
LL ~ let n = opt?;
LL + let mut ret = n + 1;
LL + ret += n;
LL + if n > 1 { Some(ret) } else { None }
|
error: use the question mark operator instead of an `and_then` call
--> tests/ui/return_and_then.rs:13:9
|
LL | opt.and_then(|n| test_opt_block(Some(n)))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL ~ let n = opt?;
LL + test_opt_block(Some(n))
|
error: use the question mark operator instead of an `and_then` call
--> tests/ui/return_and_then.rs:17:9
|
LL | gen_option(1).and_then(|n| test_opt_block(Some(n)))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL ~ let n = gen_option(1)?;
LL + test_opt_block(Some(n))
|
error: use the question mark operator instead of an `and_then` call
--> tests/ui/return_and_then.rs:21:9
|
LL | opt.and_then(|n| if n > 1 { Ok(n + 1) } else { Err(n) })
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL ~ let n = opt?;
LL + if n > 1 { Ok(n + 1) } else { Err(n) }
|
error: use the question mark operator instead of an `and_then` call
--> tests/ui/return_and_then.rs:25:9
|
LL | opt.and_then(|n| test_res_block(Ok(n)))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL ~ let n = opt?;
LL + test_res_block(Ok(n))
|
error: use the question mark operator instead of an `and_then` call
--> tests/ui/return_and_then.rs:30:9
|
LL | Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None })
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL ~ let x = Some("")?;
LL + if x.len() > 2 { Some(3) } else { None }
|
error: use the question mark operator instead of an `and_then` call
--> tests/ui/return_and_then.rs:35:9
|
LL | / Some(match (vec![1, 2, 3], vec![1, 2, 4]) {
LL | | (a, _) if a.len() > 1 => a,
LL | | (_, b) => b,
LL | | })
LL | | .and_then(|x| if x.len() > 2 { Some(3) } else { None })
| |_______________________________________________________________^
|
help: try
|
LL ~ let x = Some(match (vec![1, 2, 3], vec![1, 2, 4]) {
LL + (a, _) if a.len() > 1 => a,
LL + (_, b) => b,
LL + })?;
LL + if x.len() > 2 { Some(3) } else { None }
|
error: aborting due to 7 previous errors

View file

@ -8,11 +8,11 @@ fn main() {
const SIZE: usize = 128;
const HALF_SIZE: usize = SIZE / 2;
const DOUBLE_SIZE: usize = SIZE * 2;
let mut x = [2u8; SIZE];
let mut y = [2u8; SIZE];
let mut x = [2u16; SIZE];
let mut y = [2u16; SIZE];
// Count expression involving multiplication of size_of (Should trigger the lint)
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u16>() * SIZE) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
// Count expression involving nested multiplications of size_of (Should trigger the lint)
@ -20,22 +20,19 @@ fn main() {
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
// Count expression involving divisions of size_of (Should trigger the lint)
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::<u8>() / 2) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::<u16>() / 2) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
// Count expression involving divisions by size_of (Should not trigger the lint)
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / size_of::<u8>()) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / size_of::<u16>()) };
// Count expression involving divisions by multiple size_of (Should not trigger the lint)
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 * size_of::<u8>())) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 * size_of::<u16>())) };
// Count expression involving recursive divisions by size_of (Should trigger the lint)
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::<u8>())) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::<u16>())) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
// No size_of calls (Should not trigger the lint)
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) };
// Different types for pointee and size_of (Should not trigger the lint)
unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::<u16>() / 2 * SIZE) };
}

View file

@ -1,8 +1,8 @@
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/expressions.rs:15:62
|
LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^
LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u16>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
= note: `-D clippy::size-of-in-element-count` implied by `-D warnings`
@ -19,16 +19,16 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * si
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/expressions.rs:23:47
|
LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::<u8>() / 2) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::<u16>() / 2) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/expressions.rs:33:47
|
LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::<u8>())) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::<u16>())) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type

View file

@ -11,57 +11,52 @@ fn main() {
const SIZE: usize = 128;
const HALF_SIZE: usize = SIZE / 2;
const DOUBLE_SIZE: usize = SIZE * 2;
let mut x = [2u8; SIZE];
let mut y = [2u8; SIZE];
let mut x = [2u16; SIZE];
let mut y = [2u16; SIZE];
// Count is size_of (Should trigger the lint)
unsafe { copy_nonoverlapping::<u8>(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>()) };
unsafe { copy_nonoverlapping::<u16>(x.as_ptr(), y.as_mut_ptr(), size_of::<u16>()) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::<u8>()) };
unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::<u16>()) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::<u8>()) };
unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::<u16>()) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::<u8>()) };
unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::<u16>()) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::<u8>()) };
unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::<u16>()) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>()) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::<u16>()) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::<u8>() * SIZE) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::<u8>() * SIZE) };
unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::<u16>() * SIZE) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::<u8>() * SIZE) };
slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::<u16>() * SIZE);
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
slice_from_raw_parts(y.as_ptr(), size_of::<u16>() * SIZE);
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::<u8>() * SIZE);
unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::<u16>() * SIZE) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
slice_from_raw_parts(y.as_ptr(), size_of::<u8>() * SIZE);
unsafe { from_raw_parts(y.as_ptr(), size_of::<u16>() * SIZE) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::<u8>() * SIZE) };
unsafe { y.as_mut_ptr().sub(size_of::<u16>()) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
unsafe { from_raw_parts(y.as_ptr(), size_of::<u8>() * SIZE) };
y.as_ptr().wrapping_sub(size_of::<u16>());
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
unsafe { y.as_mut_ptr().sub(size_of::<u8>()) };
unsafe { y.as_ptr().add(size_of::<u16>()) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
y.as_ptr().wrapping_sub(size_of::<u8>());
y.as_mut_ptr().wrapping_add(size_of::<u16>());
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
unsafe { y.as_ptr().add(size_of::<u8>()) };
unsafe { y.as_ptr().offset(size_of::<u16>() as isize) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
y.as_mut_ptr().wrapping_add(size_of::<u8>());
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
unsafe { y.as_ptr().offset(size_of::<u8>() as isize) };
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
y.as_mut_ptr().wrapping_offset(size_of::<u8>() as isize);
y.as_mut_ptr().wrapping_offset(size_of::<u16>() as isize);
//~^ ERROR: found a count of bytes instead of a count of elements of `T`
}

View file

@ -1,8 +1,8 @@
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:18:68
--> tests/ui/size_of_in_element_count/functions.rs:18:69
|
LL | unsafe { copy_nonoverlapping::<u8>(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>()) };
| ^^^^^^^^^^^^^^^
LL | unsafe { copy_nonoverlapping::<u16>(x.as_ptr(), y.as_mut_ptr(), size_of::<u16>()) };
| ^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
= note: `-D clippy::size-of-in-element-count` implied by `-D warnings`
@ -19,40 +19,40 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:23:49
|
LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::<u8>()) };
| ^^^^^^^^^^^^^^^
LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::<u16>()) };
| ^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:25:64
|
LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::<u8>()) };
| ^^^^^^^^^^^^^^^
LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::<u16>()) };
| ^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:27:51
|
LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::<u8>()) };
| ^^^^^^^^^^^^^^^
LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::<u16>()) };
| ^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:29:66
|
LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::<u8>()) };
| ^^^^^^^^^^^^^^^
LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::<u16>()) };
| ^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:32:47
|
LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>()) };
| ^^^^^^^^^^^^^^^
LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::<u16>()) };
| ^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
@ -65,108 +65,92 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) };
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:37:46
--> tests/ui/size_of_in_element_count/functions.rs:37:66
|
LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::<u8>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^
LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::<u16>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:39:47
--> tests/ui/size_of_in_element_count/functions.rs:40:46
|
LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::<u8>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^
LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::<u16>() * SIZE);
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:42:66
--> tests/ui/size_of_in_element_count/functions.rs:42:38
|
LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::<u8>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^
LL | slice_from_raw_parts(y.as_ptr(), size_of::<u16>() * SIZE);
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:45:46
--> tests/ui/size_of_in_element_count/functions.rs:45:49
|
LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::<u8>() * SIZE);
| ^^^^^^^^^^^^^^^^^^^^^^
LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::<u16>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:47:38
--> tests/ui/size_of_in_element_count/functions.rs:47:41
|
LL | slice_from_raw_parts(y.as_ptr(), size_of::<u8>() * SIZE);
| ^^^^^^^^^^^^^^^^^^^^^^
LL | unsafe { from_raw_parts(y.as_ptr(), size_of::<u16>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:50:49
--> tests/ui/size_of_in_element_count/functions.rs:50:33
|
LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::<u8>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^
LL | unsafe { y.as_mut_ptr().sub(size_of::<u16>()) };
| ^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:52:41
--> tests/ui/size_of_in_element_count/functions.rs:52:29
|
LL | unsafe { from_raw_parts(y.as_ptr(), size_of::<u8>() * SIZE) };
| ^^^^^^^^^^^^^^^^^^^^^^
LL | y.as_ptr().wrapping_sub(size_of::<u16>());
| ^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:55:33
--> tests/ui/size_of_in_element_count/functions.rs:54:29
|
LL | unsafe { y.as_mut_ptr().sub(size_of::<u8>()) };
| ^^^^^^^^^^^^^^^
LL | unsafe { y.as_ptr().add(size_of::<u16>()) };
| ^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:57:29
--> tests/ui/size_of_in_element_count/functions.rs:56:33
|
LL | y.as_ptr().wrapping_sub(size_of::<u8>());
| ^^^^^^^^^^^^^^^
LL | y.as_mut_ptr().wrapping_add(size_of::<u16>());
| ^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:59:29
--> tests/ui/size_of_in_element_count/functions.rs:58:32
|
LL | unsafe { y.as_ptr().add(size_of::<u8>()) };
| ^^^^^^^^^^^^^^^
LL | unsafe { y.as_ptr().offset(size_of::<u16>() as isize) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:61:33
--> tests/ui/size_of_in_element_count/functions.rs:60:36
|
LL | y.as_mut_ptr().wrapping_add(size_of::<u8>());
| ^^^^^^^^^^^^^^^
LL | y.as_mut_ptr().wrapping_offset(size_of::<u16>() as isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:63:32
|
LL | unsafe { y.as_ptr().offset(size_of::<u8>() as isize) };
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: found a count of bytes instead of a count of elements of `T`
--> tests/ui/size_of_in_element_count/functions.rs:65:36
|
LL | y.as_mut_ptr().wrapping_offset(size_of::<u8>() as isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type
error: aborting due to 21 previous errors
error: aborting due to 19 previous errors

View file

@ -1,4 +1,4 @@
error: `ref` directly on a function argument is ignored. Consider using a reference type instead
error: `ref` directly on a function parameter does not prevent taking ownership of the passed argument. Consider using a reference type instead
--> tests/ui/toplevel_ref_arg_non_rustfix.rs:9:15
|
LL | fn the_answer(ref mut x: u8) {
@ -7,7 +7,7 @@ LL | fn the_answer(ref mut x: u8) {
= note: `-D clippy::toplevel-ref-arg` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::toplevel_ref_arg)]`
error: `ref` directly on a function argument is ignored. Consider using a reference type instead
error: `ref` directly on a function parameter does not prevent taking ownership of the passed argument. Consider using a reference type instead
--> tests/ui/toplevel_ref_arg_non_rustfix.rs:20:24
|
LL | fn fun_example(ref _x: usize) {}

View file

@ -55,3 +55,9 @@ fn no_borrow_issue(a: u32, b: u32) {
None => {},
}
}
fn issue14100() -> bool {
// Removing the `;` would make the block type be `()` instead of `!`, and this could no longer be
// cast into the `bool` function return type.
if return true {};
}

View file

@ -55,3 +55,9 @@ fn no_borrow_issue(a: u32, b: u32) {
None => {},
}
}
fn issue14100() -> bool {
// Removing the `;` would make the block type be `()` instead of `!`, and this could no longer be
// cast into the `bool` function return type.
if return true {};
}

View file

@ -55,3 +55,9 @@ fn no_borrow_issue(a: u32, b: u32) {
None => {},
};
}
fn issue14100() -> bool {
// Removing the `;` would make the block type be `()` instead of `!`, and this could no longer be
// cast into the `bool` function return type.
if return true {};
}

View file

@ -62,3 +62,19 @@ fn main() {
writeln!(v, "hello {0} {1}, world {2}", 2, 3, 4);
//~^ ERROR: literal with an empty format string
}
fn issue_13959() {
let mut v = Vec::new();
writeln!(v, "\"");
writeln!(
v,
"
foo
\\
\\\\
\"
\\\"
bar
"
);
}

Some files were not shown because too many files have changed in this diff Show more