Merge commit 'e9b70454e4' into clippy-subtree-update
This commit is contained in:
parent
2a6b3b61f0
commit
9b0611ff7a
202 changed files with 4541 additions and 1646 deletions
|
|
@ -23,3 +23,10 @@ split-debuginfo = "unpacked"
|
|||
rustflags = ["--remap-path-prefix", "=clippy_dev"]
|
||||
[profile.dev.package.lintcheck]
|
||||
rustflags = ["--remap-path-prefix", "=lintcheck"]
|
||||
|
||||
# quine-mc_cluskey makes up a significant part of the runtime in dogfood
|
||||
# due to the number of conditions in the clippy_lints crate
|
||||
# and enabling optimizations for that specific dependency helps a bit
|
||||
# without increasing total build times.
|
||||
[profile.dev.package.quine-mc_cluskey]
|
||||
opt-level = 3
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ Current stable, released 2025-08-07
|
|||
* Removed superseded lints: `transmute_float_to_int`, `transmute_int_to_char`,
|
||||
`transmute_int_to_float`, `transmute_num_to_bytes` (now in rustc)
|
||||
[#14703](https://github.com/rust-lang/rust-clippy/pull/14703)
|
||||
* Move [`uninlined_format_args`] to `pedantic` (from `style`, now allow-by-default)
|
||||
[#15287](https://github.com/rust-lang/rust-clippy/pull/15287)
|
||||
|
||||
### Enhancements
|
||||
|
||||
|
|
@ -74,6 +76,9 @@ Current stable, released 2025-08-07
|
|||
[#14719](https://github.com/rust-lang/rust-clippy/pull/14719)
|
||||
* [`unnecessary_to_owned`] fixed FP when map key is a reference
|
||||
[#14834](https://github.com/rust-lang/rust-clippy/pull/14834)
|
||||
* [`swap_with_temporary`]: fix false positive leading to different semantics
|
||||
being suggested, and use the right number of dereferences in suggestion
|
||||
[#15172](https://github.com/rust-lang/rust-clippy/pull/15172)
|
||||
|
||||
### ICE Fixes
|
||||
|
||||
|
|
@ -6707,6 +6712,7 @@ Released 2018-09-13
|
|||
[`check-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-inconsistent-struct-field-initializers
|
||||
[`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items
|
||||
[`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold
|
||||
[`const-literal-digits-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#const-literal-digits-threshold
|
||||
[`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros
|
||||
[`disallowed-methods`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-methods
|
||||
[`disallowed-names`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-names
|
||||
|
|
|
|||
|
|
@ -485,6 +485,16 @@ The maximum cognitive complexity a function can have
|
|||
* [`cognitive_complexity`](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity)
|
||||
|
||||
|
||||
## `const-literal-digits-threshold`
|
||||
The minimum digits a const float literal must have to supress the `excessive_precicion` lint
|
||||
|
||||
**Default Value:** `30`
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
* [`excessive_precision`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision)
|
||||
|
||||
|
||||
## `disallowed-macros`
|
||||
The list of disallowed macros, written as fully qualified paths.
|
||||
|
||||
|
|
@ -555,7 +565,7 @@ default configuration of Clippy. By default, any configuration will replace the
|
|||
* `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
|
||||
* `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
|
||||
|
||||
**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`
|
||||
**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`
|
||||
|
||||
---
|
||||
**Affected lints:**
|
||||
|
|
@ -873,7 +883,6 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
|
|||
* [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow)
|
||||
* [`non_std_lazy_statics`](https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics)
|
||||
* [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref)
|
||||
* [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or)
|
||||
* [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr)
|
||||
* [`question_mark`](https://rust-lang.github.io/rust-clippy/master/index.html#question_mark)
|
||||
* [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names)
|
||||
|
|
@ -881,7 +890,6 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
|
|||
* [`repeat_vec_with_capacity`](https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity)
|
||||
* [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push)
|
||||
* [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current)
|
||||
* [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind)
|
||||
* [`to_digit_is_some`](https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some)
|
||||
* [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref)
|
||||
* [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions)
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ lint-commented-code = true
|
|||
|
||||
[[disallowed-methods]]
|
||||
path = "rustc_lint::context::LintContext::lint"
|
||||
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
|
||||
reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead"
|
||||
|
||||
[[disallowed-methods]]
|
||||
path = "rustc_lint::context::LintContext::span_lint"
|
||||
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
|
||||
reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead"
|
||||
|
||||
[[disallowed-methods]]
|
||||
path = "rustc_middle::ty::context::TyCtxt::node_span_lint"
|
||||
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead"
|
||||
reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead"
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
|
|||
"GPLv2", "GPLv3",
|
||||
"GitHub", "GitLab",
|
||||
"IPv4", "IPv6",
|
||||
"InfiniBand", "RoCE",
|
||||
"ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript",
|
||||
"PowerPC", "WebAssembly",
|
||||
"NaN", "NaNs",
|
||||
|
|
@ -569,6 +570,9 @@ define_Conf! {
|
|||
/// The maximum cognitive complexity a function can have
|
||||
#[lints(cognitive_complexity)]
|
||||
cognitive_complexity_threshold: u64 = 25,
|
||||
/// The minimum digits a const float literal must have to supress the `excessive_precicion` lint
|
||||
#[lints(excessive_precision)]
|
||||
const_literal_digits_threshold: usize = 30,
|
||||
/// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY.
|
||||
///
|
||||
/// Use the Cognitive Complexity lint instead.
|
||||
|
|
@ -775,7 +779,6 @@ define_Conf! {
|
|||
needless_borrow,
|
||||
non_std_lazy_statics,
|
||||
option_as_ref_deref,
|
||||
option_map_unwrap_or,
|
||||
ptr_as_ptr,
|
||||
question_mark,
|
||||
redundant_field_names,
|
||||
|
|
@ -783,7 +786,6 @@ define_Conf! {
|
|||
repeat_vec_with_capacity,
|
||||
same_item_push,
|
||||
seek_from_current,
|
||||
seek_rewind,
|
||||
to_digit_is_some,
|
||||
transmute_ptr_to_ref,
|
||||
tuple_array_conversions,
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ url = "2.2"
|
|||
[dev-dependencies]
|
||||
walkdir = "2.3"
|
||||
|
||||
[lints.rust.unexpected_cfgs]
|
||||
level = "warn"
|
||||
check-cfg = ['cfg(bootstrap)']
|
||||
|
||||
[package.metadata.rust-analyzer]
|
||||
# This crate uses #[feature(rustc_private)]
|
||||
rustc_private = true
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_no
|
|||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item};
|
||||
use clippy_utils::usage::local_used_after_expr;
|
||||
use clippy_utils::{is_expr_final_block_expr, path_res, sym};
|
||||
use clippy_utils::{path_res, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_hir::{Expr, ExprKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
|
@ -77,17 +77,20 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
|
|||
_ => return,
|
||||
};
|
||||
span_lint_and_then(cx, ASSERTIONS_ON_RESULT_STATES, macro_call.span, message, |diag| {
|
||||
let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" };
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
diag.span_suggestion(
|
||||
macro_call.span,
|
||||
"replace with",
|
||||
format!(
|
||||
"{}.{replacement}(){semicolon}",
|
||||
snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0
|
||||
),
|
||||
app,
|
||||
);
|
||||
let recv = snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0;
|
||||
|
||||
// `assert!` doesn't return anything, but `Result::unwrap(_err)` does, so we might need to add a
|
||||
// semicolon to the suggestion to avoid leaking the type
|
||||
let sugg = match cx.tcx.parent_hir_node(e.hir_id) {
|
||||
// trailing expr of a block
|
||||
Node::Block(..) => format!("{recv}.{replacement}();"),
|
||||
// already has a trailing semicolon
|
||||
Node::Stmt(..) => format!("{recv}.{replacement}()"),
|
||||
// this is the last-resort option, because it's rather verbose
|
||||
_ => format!("{{ {recv}.{replacement}(); }}"),
|
||||
};
|
||||
diag.span_suggestion(macro_call.span, "replace with", sugg, app);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::is_expr_async_block;
|
||||
use clippy_utils::source::walk_span_to_context;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, QPath};
|
||||
use rustc_hir::{
|
||||
Block, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, QPath,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
|
|
@ -87,31 +91,37 @@ impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
|
|||
let expr_ty = typeck_results.expr_ty(body_expr);
|
||||
|
||||
if implements_trait(cx, expr_ty, future_trait_def_id, &[]) {
|
||||
let return_expr_span = match &body_expr.kind {
|
||||
// XXXkhuey there has to be a better way.
|
||||
ExprKind::Block(block, _) => block.expr.map(|e| e.span),
|
||||
ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span),
|
||||
_ => None,
|
||||
let (return_expr, return_expr_span) = match &body_expr.kind {
|
||||
ExprKind::Block(Block { expr: Some(e), .. }, _) => (*e, e.span),
|
||||
ExprKind::Path(QPath::Resolved(_, path)) => (body_expr, path.span),
|
||||
_ => return,
|
||||
};
|
||||
if let Some(return_expr_span) = return_expr_span {
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
ASYNC_YIELDS_ASYNC,
|
||||
body_expr.hir_id,
|
||||
return_expr_span,
|
||||
"an async construct yields a type which is itself awaitable",
|
||||
|db| {
|
||||
db.span_label(body_expr.span, "outer async construct");
|
||||
db.span_label(return_expr_span, "awaitable value not awaited");
|
||||
db.span_suggestion(
|
||||
return_expr_span,
|
||||
"consider awaiting this value",
|
||||
format!("{}.await", snippet(cx, return_expr_span, "..")),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
let return_expr_span = walk_span_to_context(return_expr_span, expr.span.ctxt()).unwrap_or(return_expr_span);
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
let mut return_expr_snip =
|
||||
Sugg::hir_with_context(cx, return_expr, expr.span.ctxt(), "..", &mut applicability);
|
||||
if !is_expr_async_block(return_expr) {
|
||||
return_expr_snip = return_expr_snip.maybe_paren();
|
||||
}
|
||||
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
ASYNC_YIELDS_ASYNC,
|
||||
body_expr.hir_id,
|
||||
return_expr_span,
|
||||
"an async construct yields a type which is itself awaitable",
|
||||
|db| {
|
||||
db.span_label(body_expr.span, "outer async construct");
|
||||
db.span_label(return_expr_span, "awaitable value not awaited");
|
||||
db.span_suggestion(
|
||||
return_expr_span,
|
||||
"consider awaiting this value",
|
||||
format!("{return_expr_snip}.await"),
|
||||
applicability,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
179
clippy_lints/src/bool_comparison.rs
Normal file
179
clippy_lints/src/bool_comparison.rs
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{is_expn_of, peel_blocks, sym};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for expressions of the form `x == true`,
|
||||
/// `x != true` and order comparisons such as `x < true` (or vice versa) and
|
||||
/// suggest using the variable directly.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Unnecessary code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// if x == true {}
|
||||
/// if y == false {}
|
||||
/// ```
|
||||
/// use `x` directly:
|
||||
/// ```rust,ignore
|
||||
/// if x {}
|
||||
/// if !y {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub BOOL_COMPARISON,
|
||||
complexity,
|
||||
"comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BoolComparison {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if e.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Binary(Spanned { node, .. }, left_side, right_side) = e.kind
|
||||
&& is_expn_of(left_side.span, sym::cfg).is_none()
|
||||
&& is_expn_of(right_side.span, sym::cfg).is_none()
|
||||
&& cx.typeck_results().expr_ty(left_side).is_bool()
|
||||
&& cx.typeck_results().expr_ty(right_side).is_bool()
|
||||
{
|
||||
let ignore_case = None::<(fn(_) -> _, &str)>;
|
||||
let ignore_no_literal = None::<(fn(_, _) -> _, &str)>;
|
||||
match node {
|
||||
BinOpKind::Eq => {
|
||||
let true_case = Some((|h| h, "equality checks against true are unnecessary"));
|
||||
let false_case = Some((
|
||||
|h: Sugg<'tcx>| !h,
|
||||
"equality checks against false can be replaced by a negation",
|
||||
));
|
||||
check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal);
|
||||
},
|
||||
BinOpKind::Ne => {
|
||||
let true_case = Some((
|
||||
|h: Sugg<'tcx>| !h,
|
||||
"inequality checks against true can be replaced by a negation",
|
||||
));
|
||||
let false_case = Some((|h| h, "inequality checks against false are unnecessary"));
|
||||
check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal);
|
||||
},
|
||||
BinOpKind::Lt => check_comparison(
|
||||
cx,
|
||||
e,
|
||||
ignore_case,
|
||||
Some((|h| h, "greater than checks against false are unnecessary")),
|
||||
Some((
|
||||
|h: Sugg<'tcx>| !h,
|
||||
"less than comparison against true can be replaced by a negation",
|
||||
)),
|
||||
ignore_case,
|
||||
Some((
|
||||
|l: Sugg<'tcx>, r: Sugg<'tcx>| (!l).bit_and(&r),
|
||||
"order comparisons between booleans can be simplified",
|
||||
)),
|
||||
),
|
||||
BinOpKind::Gt => check_comparison(
|
||||
cx,
|
||||
e,
|
||||
Some((
|
||||
|h: Sugg<'tcx>| !h,
|
||||
"less than comparison against true can be replaced by a negation",
|
||||
)),
|
||||
ignore_case,
|
||||
ignore_case,
|
||||
Some((|h| h, "greater than checks against false are unnecessary")),
|
||||
Some((
|
||||
|l: Sugg<'tcx>, r: Sugg<'tcx>| l.bit_and(&(!r)),
|
||||
"order comparisons between booleans can be simplified",
|
||||
)),
|
||||
),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_comparison<'a, 'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>,
|
||||
left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>,
|
||||
right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>,
|
||||
right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>,
|
||||
no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &'static str)>,
|
||||
) {
|
||||
if let ExprKind::Binary(_, left_side, right_side) = e.kind {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
// Eliminate parentheses in `e` by using the lo pos of lhs and hi pos of rhs,
|
||||
// calling `source_callsite` make sure macros are handled correctly, see issue #9907
|
||||
let binop_span = left_side.span.source_callsite().to(right_side.span.source_callsite());
|
||||
|
||||
match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
|
||||
(Some(true), None) => left_true.map_or((), |(h, m)| {
|
||||
suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h);
|
||||
}),
|
||||
(None, Some(true)) => right_true.map_or((), |(h, m)| {
|
||||
suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h);
|
||||
}),
|
||||
(Some(false), None) => left_false.map_or((), |(h, m)| {
|
||||
suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h);
|
||||
}),
|
||||
(None, Some(false)) => right_false.map_or((), |(h, m)| {
|
||||
suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h);
|
||||
}),
|
||||
(None, None) => no_literal.map_or((), |(h, m)| {
|
||||
let left_side = Sugg::hir_with_context(cx, left_side, binop_span.ctxt(), "..", &mut applicability);
|
||||
let right_side = Sugg::hir_with_context(cx, right_side, binop_span.ctxt(), "..", &mut applicability);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BOOL_COMPARISON,
|
||||
binop_span,
|
||||
m,
|
||||
"try",
|
||||
h(left_side, right_side).into_string(),
|
||||
applicability,
|
||||
);
|
||||
}),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_bool_comparison<'a, 'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
span: Span,
|
||||
expr: &Expr<'_>,
|
||||
mut app: Applicability,
|
||||
message: &'static str,
|
||||
conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
|
||||
) {
|
||||
let hint = Sugg::hir_with_context(cx, expr, span.ctxt(), "..", &mut app);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BOOL_COMPARISON,
|
||||
span,
|
||||
message,
|
||||
"try",
|
||||
conv_hint(hint).into_string(),
|
||||
app,
|
||||
);
|
||||
}
|
||||
|
||||
fn fetch_bool_expr(expr: &Expr<'_>) -> Option<bool> {
|
||||
if let ExprKind::Lit(lit_ptr) = peel_blocks(expr).kind
|
||||
&& let LitKind::Bool(value) = lit_ptr.node
|
||||
{
|
||||
return Some(value);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
@ -8,17 +8,12 @@ use rustc_middle::ty::{self, Ty};
|
|||
|
||||
use super::CAST_PTR_ALIGNMENT;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
|
||||
if is_hir_ty_cfg_dependant(cx, cast_to) {
|
||||
return;
|
||||
}
|
||||
let (cast_from, cast_to) = (
|
||||
cx.typeck_results().expr_ty(cast_expr),
|
||||
cx.typeck_results().expr_ty(expr),
|
||||
);
|
||||
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
|
||||
} else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) {
|
||||
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
|
||||
}
|
||||
|
||||
pub(super) fn check_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind
|
||||
&& method_path.ident.name == sym::cast
|
||||
&& let Some(generic_args) = method_path.args
|
||||
&& let [GenericArg::Type(cast_to)] = generic_args.args
|
||||
|
|
@ -74,14 +69,13 @@ fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
|||
ExprKind::Call(func, [arg, ..]) if arg.hir_id == e.hir_id => {
|
||||
if let ExprKind::Path(path) = &func.kind
|
||||
&& let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
|
||||
&& let Some(name) = cx.tcx.get_diagnostic_name(def_id)
|
||||
&& matches!(
|
||||
cx.tcx.get_diagnostic_name(def_id),
|
||||
Some(
|
||||
sym::ptr_write_unaligned
|
||||
| sym::ptr_read_unaligned
|
||||
| sym::intrinsics_unaligned_volatile_load
|
||||
| sym::intrinsics_unaligned_volatile_store
|
||||
)
|
||||
name,
|
||||
sym::ptr_write_unaligned
|
||||
| sym::ptr_read_unaligned
|
||||
| sym::intrinsics_unaligned_volatile_load
|
||||
| sym::intrinsics_unaligned_volatile_store
|
||||
)
|
||||
{
|
||||
true
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::{get_parent_expr, is_no_std_crate};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::sym;
|
||||
|
||||
|
|
@ -42,13 +44,52 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
|
|||
let mut applicability = Applicability::MachineApplicable;
|
||||
let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0;
|
||||
let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0;
|
||||
let krate = if is_no_std_crate(cx) { "core" } else { "std" };
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CAST_SLICE_FROM_RAW_PARTS,
|
||||
span,
|
||||
format!("casting the result of `{func}` to {cast_to}"),
|
||||
"replace with",
|
||||
format!("core::ptr::slice_{func}({ptr}, {len})"),
|
||||
format!("{krate}::ptr::slice_{func}({ptr}, {len})"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks for implicit cast from slice reference to raw slice pointer.
|
||||
pub(super) fn check_implicit_cast(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::Call(fun, [ptr_arg, len_arg]) = expr.peel_blocks().kind
|
||||
&& let ExprKind::Path(ref qpath) = fun.kind
|
||||
&& let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id()
|
||||
&& let Some(rpk) = raw_parts_kind(cx, fun_def_id)
|
||||
&& !matches!(get_parent_expr(cx, expr).map(|e| e.kind), Some(ExprKind::Cast(..)))
|
||||
&& let [deref, borrow] = cx.typeck_results().expr_adjustments(expr)
|
||||
&& matches!(deref.kind, Adjust::Deref(..))
|
||||
&& let Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::RawPtr(..)),
|
||||
target,
|
||||
} = borrow
|
||||
&& let ty::RawPtr(pointee_ty, _) = target.kind()
|
||||
&& pointee_ty.is_slice()
|
||||
&& !expr.span.from_expansion()
|
||||
{
|
||||
let func = match rpk {
|
||||
RawPartsKind::Immutable => "from_raw_parts",
|
||||
RawPartsKind::Mutable => "from_raw_parts_mut",
|
||||
};
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let ctxt = expr.span.ctxt();
|
||||
let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0;
|
||||
let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0;
|
||||
let krate = if is_no_std_crate(cx) { "core" } else { "std" };
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CAST_SLICE_FROM_RAW_PARTS,
|
||||
expr.span,
|
||||
format!("implicitly casting the result of `{func}` to `{target}`"),
|
||||
"replace_with",
|
||||
format!("{krate}::ptr::slice_{func}({ptr}, {len})"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -873,7 +873,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
}
|
||||
char_lit_as_u8::check(cx, expr, cast_from_expr, cast_to);
|
||||
cast_slice_from_raw_parts::check(cx, expr, cast_from_expr, cast_to, self.msrv);
|
||||
cast_ptr_alignment::check(cx, expr, cast_from, cast_to);
|
||||
ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv);
|
||||
ptr_as_ptr::check(cx, expr, cast_from_expr, cast_from, cast_to_hir, cast_to, self.msrv);
|
||||
as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to);
|
||||
fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to);
|
||||
confusing_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to);
|
||||
|
|
@ -911,8 +913,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
if self.msrv.meets(cx, msrvs::RAW_REF_OP) {
|
||||
borrow_as_ptr::check_implicit_cast(cx, expr);
|
||||
}
|
||||
cast_ptr_alignment::check(cx, expr);
|
||||
ptr_as_ptr::check(cx, expr, self.msrv);
|
||||
if self.msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) {
|
||||
cast_slice_from_raw_parts::check_implicit_cast(cx, expr);
|
||||
}
|
||||
cast_ptr_alignment::check_cast_method(cx, expr);
|
||||
cast_slice_different_sizes::check(cx, expr, self.msrv);
|
||||
ptr_cast_constness::check_null_ptr_cast_method(cx, expr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ use clippy_utils::msrvs::{self, Msrv};
|
|||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Mutability, QPath, TyKind};
|
||||
use rustc_hir::{self as hir, Expr, ExprKind, QPath, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
use super::PTR_AS_PTR;
|
||||
|
|
@ -26,13 +26,18 @@ impl OmitFollowedCastReason<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) {
|
||||
if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind
|
||||
&& let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr))
|
||||
&& let ty::RawPtr(_, from_mutbl) = cast_from.kind()
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &Expr<'tcx>,
|
||||
cast_from_expr: &Expr<'_>,
|
||||
cast_from: Ty<'_>,
|
||||
cast_to_hir: &hir::Ty<'_>,
|
||||
cast_to: Ty<'tcx>,
|
||||
msrv: Msrv,
|
||||
) {
|
||||
if let ty::RawPtr(_, from_mutbl) = cast_from.kind()
|
||||
&& let ty::RawPtr(to_pointee_ty, to_mutbl) = cast_to.kind()
|
||||
&& matches!((from_mutbl, to_mutbl),
|
||||
(Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut))
|
||||
&& from_mutbl == to_mutbl
|
||||
// The `U` in `pointer::cast` have to be `Sized`
|
||||
// as explained here: https://github.com/rust-lang/rust/issues/60602.
|
||||
&& to_pointee_ty.is_sized(cx.tcx, cx.typing_env())
|
||||
|
|
@ -40,7 +45,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv)
|
|||
&& !is_from_proc_macro(cx, expr)
|
||||
{
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let turbofish = match &cast_to_hir_ty.kind {
|
||||
let turbofish = match &cast_to_hir.kind {
|
||||
TyKind::Infer(()) => String::new(),
|
||||
TyKind::Ptr(mut_ty) => {
|
||||
if matches!(mut_ty.ty.kind, TyKind::Infer(())) {
|
||||
|
|
@ -58,16 +63,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv)
|
|||
// following `cast` does not compile because it fails to infer what type is expected
|
||||
// as type argument to `std::ptr::ptr_null` or `std::ptr::ptr_null_mut`, so
|
||||
// we omit following `cast`:
|
||||
let omit_cast = if let ExprKind::Call(func, []) = cast_expr.kind
|
||||
let omit_cast = if let ExprKind::Call(func, []) = cast_from_expr.kind
|
||||
&& let ExprKind::Path(ref qpath @ QPath::Resolved(None, path)) = func.kind
|
||||
&& let Some(method_defid) = path.res.opt_def_id()
|
||||
{
|
||||
if cx.tcx.is_diagnostic_item(sym::ptr_null, method_defid) {
|
||||
OmitFollowedCastReason::Null(qpath)
|
||||
} else if cx.tcx.is_diagnostic_item(sym::ptr_null_mut, method_defid) {
|
||||
OmitFollowedCastReason::NullMut(qpath)
|
||||
} else {
|
||||
OmitFollowedCastReason::None
|
||||
match cx.tcx.get_diagnostic_name(method_defid) {
|
||||
Some(sym::ptr_null) => OmitFollowedCastReason::Null(qpath),
|
||||
Some(sym::ptr_null_mut) => OmitFollowedCastReason::NullMut(qpath),
|
||||
_ => OmitFollowedCastReason::None,
|
||||
}
|
||||
} else {
|
||||
OmitFollowedCastReason::None
|
||||
|
|
@ -78,7 +81,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv)
|
|||
let method = snippet_with_applicability(cx, qpath_span_without_turbofish(method), "..", &mut app);
|
||||
("try call directly", format!("{method}{turbofish}()"))
|
||||
} else {
|
||||
let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app);
|
||||
let cast_expr_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "_", &mut app);
|
||||
|
||||
(
|
||||
"try `pointer::cast`, a safer alternative",
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{std_or_core, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Mutability, QPath};
|
||||
use rustc_hir::{self as hir, Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
|
||||
|
||||
|
|
@ -12,26 +13,23 @@ use super::PTR_CAST_CONSTNESS;
|
|||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
cast_expr: &Expr<'_>,
|
||||
cast_from_expr: &Expr<'_>,
|
||||
cast_from: Ty<'tcx>,
|
||||
cast_to: Ty<'tcx>,
|
||||
msrv: Msrv,
|
||||
) {
|
||||
if let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind()
|
||||
&& let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind()
|
||||
&& matches!(
|
||||
(from_mutbl, to_mutbl),
|
||||
(Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not)
|
||||
)
|
||||
&& from_mutbl != to_mutbl
|
||||
&& from_ty == to_ty
|
||||
&& !from_ty.has_erased_regions()
|
||||
{
|
||||
if let ExprKind::Call(func, []) = cast_expr.kind
|
||||
if let ExprKind::Call(func, []) = cast_from_expr.kind
|
||||
&& let ExprKind::Path(QPath::Resolved(None, path)) = func.kind
|
||||
&& let Some(defid) = path.res.opt_def_id()
|
||||
&& let Some(prefix) = std_or_core(cx)
|
||||
&& let mut app = Applicability::MachineApplicable
|
||||
&& let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app))
|
||||
&& let sugg = snippet_with_applicability(cx, cast_from_expr.span, "_", &mut app)
|
||||
&& let Some((_, after_lt)) = sugg.split_once("::<")
|
||||
&& let Some((source, target, target_func)) = match cx.tcx.get_diagnostic_name(defid) {
|
||||
Some(sym::ptr_null) => Some(("const", "mutable", "null_mut")),
|
||||
|
|
@ -53,11 +51,17 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
if msrv.meets(cx, msrvs::POINTER_CAST_CONSTNESS) {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let sugg = Sugg::hir_with_context(cx, cast_expr, expr.span.ctxt(), "_", &mut app);
|
||||
let constness = match *to_mutbl {
|
||||
Mutability::Not => "const",
|
||||
Mutability::Mut => "mut",
|
||||
let sugg = if let ExprKind::Cast(nested_from, nested_hir_ty) = cast_from_expr.kind
|
||||
&& let hir::TyKind::Ptr(ptr_ty) = nested_hir_ty.kind
|
||||
&& let hir::TyKind::Infer(()) = ptr_ty.ty.kind
|
||||
{
|
||||
// `(foo as *const _).cast_mut()` fails method name resolution
|
||||
// avoid this by `as`-ing the full type
|
||||
Sugg::hir_with_context(cx, nested_from, expr.span.ctxt(), "_", &mut app).as_ty(cast_from)
|
||||
} else {
|
||||
Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "_", &mut app)
|
||||
};
|
||||
let constness = to_mutbl.ptr_str();
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
@ -73,8 +77,8 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
|
||||
pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::MethodCall(method, cast_expr, [], _) = expr.kind
|
||||
&& let ExprKind::Call(func, []) = cast_expr.kind
|
||||
if let ExprKind::MethodCall(method, cast_from_expr, [], _) = expr.kind
|
||||
&& let ExprKind::Call(func, []) = cast_from_expr.kind
|
||||
&& let ExprKind::Path(QPath::Resolved(None, path)) = func.kind
|
||||
&& let Some(defid) = path.res.opt_def_id()
|
||||
&& let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.name) {
|
||||
|
|
@ -84,7 +88,7 @@ pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>)
|
|||
}
|
||||
&& let Some(prefix) = std_or_core(cx)
|
||||
&& let mut app = Applicability::MachineApplicable
|
||||
&& let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app))
|
||||
&& let sugg = snippet_with_applicability(cx, cast_from_expr.span, "_", &mut app)
|
||||
&& let Some((_, after_lt)) = sugg.split_once("::<")
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Known problems
|
||||
/// The true Cognitive Complexity of a method is not something we can
|
||||
/// calculate using modern technology. This lint has been left in the
|
||||
/// `nursery` so as to not mislead users into using this lint as a
|
||||
/// calculate using modern technology. This lint has been left in
|
||||
/// `restriction` so as to not mislead users into using this lint as a
|
||||
/// measurement tool.
|
||||
///
|
||||
/// For more detailed information, see [rust-clippy#3793](https://github.com/rust-lang/rust-clippy/issues/3793)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
|||
crate::await_holding_invalid::AWAIT_HOLDING_REFCELL_REF_INFO,
|
||||
crate::blocks_in_conditions::BLOCKS_IN_CONDITIONS_INFO,
|
||||
crate::bool_assert_comparison::BOOL_ASSERT_COMPARISON_INFO,
|
||||
crate::bool_comparison::BOOL_COMPARISON_INFO,
|
||||
crate::bool_to_int_with_if::BOOL_TO_INT_WITH_IF_INFO,
|
||||
crate::booleans::NONMINIMAL_BOOL_INFO,
|
||||
crate::booleans::OVERLY_COMPLEX_BOOL_EXPR_INFO,
|
||||
|
|
@ -538,7 +539,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
|||
crate::mutex_atomic::MUTEX_ATOMIC_INFO,
|
||||
crate::mutex_atomic::MUTEX_INTEGER_INFO,
|
||||
crate::needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE_INFO,
|
||||
crate::needless_bool::BOOL_COMPARISON_INFO,
|
||||
crate::needless_bool::NEEDLESS_BOOL_INFO,
|
||||
crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO,
|
||||
crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use rustc_hir::{
|
|||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::adjustment::{Adjust, PointerCoercion};
|
||||
use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty, TypeckResults};
|
||||
use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty, TypeckResults, VariantDef};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
|
|
@ -85,6 +85,13 @@ fn contains_trait_object(ty: Ty<'_>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn determine_derive_macro(cx: &LateContext<'_>, is_const: bool) -> Option<&'static str> {
|
||||
(!is_const)
|
||||
.then_some("derive")
|
||||
.or_else(|| cx.tcx.features().enabled(sym::derive_const).then_some("derive_const"))
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
fn check_struct<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
item: &'tcx Item<'_>,
|
||||
|
|
@ -93,6 +100,7 @@ fn check_struct<'tcx>(
|
|||
adt_def: AdtDef<'_>,
|
||||
ty_args: GenericArgsRef<'_>,
|
||||
typeck_results: &'tcx TypeckResults<'tcx>,
|
||||
is_const: bool,
|
||||
) {
|
||||
if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind
|
||||
&& let Some(PathSegment { args, .. }) = p.segments.last()
|
||||
|
|
@ -125,14 +133,18 @@ fn check_struct<'tcx>(
|
|||
ExprKind::Tup(fields) => fields.iter().all(is_default_without_adjusts),
|
||||
ExprKind::Call(callee, args) if is_path_self(callee) => args.iter().all(is_default_without_adjusts),
|
||||
ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_without_adjusts(ef.expr)),
|
||||
_ => false,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if should_emit {
|
||||
if should_emit && let Some(derive_snippet) = determine_derive_macro(cx, is_const) {
|
||||
let struct_span = cx.tcx.def_span(adt_def.did());
|
||||
let indent_enum = indent_of(cx, struct_span).unwrap_or(0);
|
||||
let suggestions = vec![
|
||||
(item.span, String::new()), // Remove the manual implementation
|
||||
(struct_span.shrink_to_lo(), "#[derive(Default)]\n".to_string()), // Add the derive attribute
|
||||
(
|
||||
struct_span.shrink_to_lo(),
|
||||
format!("#[{derive_snippet}(Default)]\n{}", " ".repeat(indent_enum)),
|
||||
), // Add the derive attribute
|
||||
];
|
||||
|
||||
span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| {
|
||||
|
|
@ -145,11 +157,41 @@ fn check_struct<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>) {
|
||||
if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind
|
||||
&& let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res
|
||||
&& let variant_id = cx.tcx.parent(id)
|
||||
&& let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id)
|
||||
fn extract_enum_variant<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
func_expr: &'tcx Expr<'tcx>,
|
||||
adt_def: AdtDef<'tcx>,
|
||||
) -> Option<&'tcx VariantDef> {
|
||||
match &peel_blocks(func_expr).kind {
|
||||
ExprKind::Path(QPath::Resolved(None, p))
|
||||
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res
|
||||
&& let variant_id = cx.tcx.parent(id)
|
||||
&& let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id) =>
|
||||
{
|
||||
Some(variant_def)
|
||||
},
|
||||
ExprKind::Path(QPath::TypeRelative(ty, segment))
|
||||
if let TyKind::Path(QPath::Resolved(None, p)) = &ty.kind
|
||||
&& let Res::SelfTyAlias {
|
||||
is_trait_impl: true, ..
|
||||
} = p.res
|
||||
&& let variant_ident = segment.ident
|
||||
&& let Some(variant_def) = adt_def.variants().iter().find(|v| v.ident(cx.tcx) == variant_ident) =>
|
||||
{
|
||||
Some(variant_def)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_enum<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
item: &'tcx Item<'tcx>,
|
||||
func_expr: &'tcx Expr<'tcx>,
|
||||
adt_def: AdtDef<'tcx>,
|
||||
is_const: bool,
|
||||
) {
|
||||
if let Some(variant_def) = extract_enum_variant(cx, func_expr, adt_def)
|
||||
&& variant_def.fields.is_empty()
|
||||
&& !variant_def.is_field_list_non_exhaustive()
|
||||
{
|
||||
|
|
@ -158,11 +200,15 @@ fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Ex
|
|||
let variant_span = cx.tcx.def_span(variant_def.def_id);
|
||||
let indent_variant = indent_of(cx, variant_span).unwrap_or(0);
|
||||
|
||||
let Some(derive_snippet) = determine_derive_macro(cx, is_const) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let suggestions = vec![
|
||||
(item.span, String::new()), // Remove the manual implementation
|
||||
(
|
||||
enum_span.shrink_to_lo(),
|
||||
format!("#[derive(Default)]\n{}", " ".repeat(indent_enum)),
|
||||
format!("#[{derive_snippet}(Default)]\n{}", " ".repeat(indent_enum)),
|
||||
), // Add the derive attribute
|
||||
(
|
||||
variant_span.shrink_to_lo(),
|
||||
|
|
@ -201,10 +247,20 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
|
|||
&& !attrs.iter().any(|attr| attr.doc_str().is_some())
|
||||
&& cx.tcx.hir_attrs(impl_item_hir).is_empty()
|
||||
{
|
||||
let is_const = of_trait.constness == hir::Constness::Const;
|
||||
if adt_def.is_struct() {
|
||||
check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b));
|
||||
check_struct(
|
||||
cx,
|
||||
item,
|
||||
self_ty,
|
||||
func_expr,
|
||||
adt_def,
|
||||
args,
|
||||
cx.tcx.typeck_body(*b),
|
||||
is_const,
|
||||
);
|
||||
} else if adt_def.is_enum() && self.msrv.meets(cx, msrvs::DEFAULT_ENUM_ATTRIBUTE) {
|
||||
check_enum(cx, item, func_expr, adt_def);
|
||||
check_enum(cx, item, func_expr, adt_def, is_const);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
|
||||
use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::ty::is_copy;
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
|
|
@ -194,7 +194,17 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
|
|||
if let Some(sugg) = sugg {
|
||||
span_lint_and_sugg(cx, MAP_ENTRY, expr.span, lint_msg, "try", sugg, app);
|
||||
} else {
|
||||
span_lint(cx, MAP_ENTRY, expr.span, lint_msg);
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
MAP_ENTRY,
|
||||
expr.span,
|
||||
lint_msg,
|
||||
None,
|
||||
format!(
|
||||
"consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.{}.html#entry-api",
|
||||
map_ty.name()
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -254,35 +264,28 @@ fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Optio
|
|||
_ => None,
|
||||
});
|
||||
|
||||
match expr.kind {
|
||||
ExprKind::MethodCall(
|
||||
_,
|
||||
if let ExprKind::MethodCall(_, map, [arg], _) = expr.kind
|
||||
&& let Expr {
|
||||
kind: ExprKind::AddrOf(_, _, key),
|
||||
span: key_span,
|
||||
..
|
||||
} = arg
|
||||
&& key_span.eq_ctxt(expr.span)
|
||||
{
|
||||
let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
|
||||
let expr = ContainsExpr {
|
||||
negated,
|
||||
map,
|
||||
[
|
||||
Expr {
|
||||
kind: ExprKind::AddrOf(_, _, key),
|
||||
span: key_span,
|
||||
..
|
||||
},
|
||||
],
|
||||
_,
|
||||
) if key_span.eq_ctxt(expr.span) => {
|
||||
let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
|
||||
let expr = ContainsExpr {
|
||||
negated,
|
||||
map,
|
||||
key,
|
||||
call_ctxt: expr.span.ctxt(),
|
||||
};
|
||||
if cx.tcx.is_diagnostic_item(sym::btreemap_contains_key, id) {
|
||||
Some((MapType::BTree, expr))
|
||||
} else if cx.tcx.is_diagnostic_item(sym::hashmap_contains_key, id) {
|
||||
Some((MapType::Hash, expr))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
key,
|
||||
call_ctxt: expr.span.ctxt(),
|
||||
};
|
||||
match cx.tcx.get_diagnostic_name(id) {
|
||||
Some(sym::btreemap_contains_key) => Some((MapType::BTree, expr)),
|
||||
Some(sym::hashmap_contains_key) => Some((MapType::Hash, expr)),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -311,7 +314,9 @@ struct InsertExpr<'tcx> {
|
|||
fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
|
||||
if let ExprKind::MethodCall(_, map, [key, value], _) = expr.kind {
|
||||
let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
|
||||
if cx.tcx.is_diagnostic_item(sym::btreemap_insert, id) || cx.tcx.is_diagnostic_item(sym::hashmap_insert, id) {
|
||||
if let Some(insert) = cx.tcx.get_diagnostic_name(id)
|
||||
&& matches!(insert, sym::btreemap_insert | sym::hashmap_insert)
|
||||
{
|
||||
Some(InsertExpr { map, key, value })
|
||||
} else {
|
||||
None
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use rustc_hir::attrs::AttributeKind;
|
|||
use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind, find_attr};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_middle::ty::{
|
||||
self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults,
|
||||
};
|
||||
|
|
@ -148,10 +149,9 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
|
|||
{
|
||||
return;
|
||||
}
|
||||
let callee_ty_adjusted = typeck
|
||||
.expr_adjustments(callee)
|
||||
.last()
|
||||
.map_or(callee_ty, |a| a.target.peel_refs());
|
||||
|
||||
let callee_ty_adjustments = typeck.expr_adjustments(callee);
|
||||
let callee_ty_adjusted = callee_ty_adjustments.last().map_or(callee_ty, |a| a.target);
|
||||
|
||||
let sig = match callee_ty_adjusted.kind() {
|
||||
ty::FnDef(def, _) => {
|
||||
|
|
@ -230,7 +230,20 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
|
|||
},
|
||||
_ => (),
|
||||
}
|
||||
} else if let n_refs =
|
||||
callee_ty_adjustments
|
||||
.iter()
|
||||
.rev()
|
||||
.fold(0, |acc, adjustment| match adjustment.kind {
|
||||
Adjust::Deref(Some(_)) => acc + 1,
|
||||
Adjust::Deref(_) if acc > 0 => acc + 1,
|
||||
_ => acc,
|
||||
})
|
||||
&& n_refs > 0
|
||||
{
|
||||
snippet = format!("{}{snippet}", "*".repeat(n_refs));
|
||||
}
|
||||
|
||||
let replace_with = match callee_ty_adjusted.kind() {
|
||||
ty::FnDef(def, _) => cx.tcx.def_descr(*def),
|
||||
_ => "function",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::numeric_literal;
|
||||
use clippy_utils::{ExprUseNode, expr_use_ctxt, numeric_literal};
|
||||
use rustc_ast::ast::{LitFloatType, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, FloatTy};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use std::fmt;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -13,6 +14,8 @@ declare_clippy_lint! {
|
|||
/// Checks for float literals with a precision greater
|
||||
/// than that supported by the underlying type.
|
||||
///
|
||||
/// The lint is suppressed for literals with over `const_literal_digits_threshold` digits.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Rust will truncate the literal silently.
|
||||
///
|
||||
|
|
@ -58,7 +61,21 @@ declare_clippy_lint! {
|
|||
"lossy whole number float literals"
|
||||
}
|
||||
|
||||
declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]);
|
||||
pub struct FloatLiteral {
|
||||
const_literal_digits_threshold: usize,
|
||||
}
|
||||
|
||||
impl_lint_pass!(FloatLiteral => [
|
||||
EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL
|
||||
]);
|
||||
|
||||
impl FloatLiteral {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
const_literal_digits_threshold: conf.const_literal_digits_threshold,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
|
|
@ -126,13 +143,25 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
|
|||
},
|
||||
);
|
||||
}
|
||||
} else if digits > max as usize && count_digits(&float_str) < count_digits(sym_str) {
|
||||
} else if digits > max as usize && count_digits(&float_str) < digits {
|
||||
if digits >= self.const_literal_digits_threshold
|
||||
&& matches!(expr_use_ctxt(cx, expr).use_node(cx), ExprUseNode::ConstStatic(_))
|
||||
{
|
||||
// If a big enough number of digits is specified and it's a constant
|
||||
// we assume the user is definining a constant, and excessive precision is ok
|
||||
return;
|
||||
}
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
EXCESSIVE_PRECISION,
|
||||
expr.span,
|
||||
"float has excessive precision",
|
||||
|diag| {
|
||||
if digits >= self.const_literal_digits_threshold
|
||||
&& let Some(let_stmt) = maybe_let_stmt(cx, expr)
|
||||
{
|
||||
diag.span_note(let_stmt.span, "consider making it a `const` item");
|
||||
}
|
||||
diag.span_suggestion_verbose(
|
||||
expr.span,
|
||||
"consider changing the type or truncating it to",
|
||||
|
|
@ -196,3 +225,11 @@ impl FloatFormat {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_let_stmt<'a>(cx: &LateContext<'a>, expr: &hir::Expr<'_>) -> Option<&'a hir::LetStmt<'a>> {
|
||||
let parent = cx.tcx.parent_hir_node(expr.hir_id);
|
||||
match parent {
|
||||
hir::Node::LetStmt(let_stmt) => Some(let_stmt),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -535,7 +535,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
|
|||
def_id: LocalDefId,
|
||||
) {
|
||||
let hir_id = cx.tcx.local_def_id_to_hir_id(def_id);
|
||||
too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold);
|
||||
too_many_arguments::check_fn(cx, kind, decl, hir_id, def_id, self.too_many_arguments_threshold);
|
||||
too_many_lines::check_fn(cx, kind, body, span, def_id, self.too_many_lines_threshold);
|
||||
not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, def_id);
|
||||
misnamed_getters::check_fn(cx, kind, decl, body, span);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use rustc_abi::ExternAbi;
|
||||
use rustc_hir::{self as hir, intravisit};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::Span;
|
||||
|
||||
|
|
@ -10,39 +12,18 @@ use super::TOO_MANY_ARGUMENTS;
|
|||
|
||||
pub(super) fn check_fn(
|
||||
cx: &LateContext<'_>,
|
||||
kind: intravisit::FnKind<'_>,
|
||||
kind: FnKind<'_>,
|
||||
decl: &hir::FnDecl<'_>,
|
||||
span: Span,
|
||||
hir_id: hir::HirId,
|
||||
def_id: LocalDefId,
|
||||
too_many_arguments_threshold: u64,
|
||||
) {
|
||||
// don't warn for implementations, it's not their fault
|
||||
if !is_trait_impl_item(cx, hir_id) {
|
||||
if !is_trait_impl_item(cx, hir_id)
|
||||
// don't lint extern functions decls, it's not their fault either
|
||||
match kind {
|
||||
intravisit::FnKind::Method(
|
||||
_,
|
||||
&hir::FnSig {
|
||||
header: hir::FnHeader {
|
||||
abi: ExternAbi::Rust, ..
|
||||
},
|
||||
..
|
||||
},
|
||||
)
|
||||
| intravisit::FnKind::ItemFn(
|
||||
_,
|
||||
_,
|
||||
hir::FnHeader {
|
||||
abi: ExternAbi::Rust, ..
|
||||
},
|
||||
) => check_arg_number(
|
||||
cx,
|
||||
decl,
|
||||
span.with_hi(decl.output.span().hi()),
|
||||
too_many_arguments_threshold,
|
||||
),
|
||||
_ => {},
|
||||
}
|
||||
&& kind.header().is_some_and(|header| header.abi == ExternAbi::Rust)
|
||||
{
|
||||
check_arg_number(cx, decl, cx.tcx.def_span(def_id), too_many_arguments_threshold);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,16 +2,16 @@ use clippy_config::Conf;
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{
|
||||
contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, is_res_lang_ctor,
|
||||
path_res, peel_blocks,
|
||||
path_res, peel_blocks, sym,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -71,21 +71,21 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
|||
&& let ExprKind::Block(then_block, _) = then.kind
|
||||
&& let Some(then_expr) = then_block.expr
|
||||
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
|
||||
&& let ctxt = expr.span.ctxt()
|
||||
&& then_expr.span.ctxt() == ctxt
|
||||
&& !expr.span.from_expansion()
|
||||
&& !then_expr.span.from_expansion()
|
||||
&& is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome)
|
||||
&& is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone)
|
||||
&& !is_else_clause(cx.tcx, expr)
|
||||
&& !is_in_const_context(cx)
|
||||
&& !expr.span.in_external_macro(cx.sess().source_map())
|
||||
&& self.msrv.meets(cx, msrvs::BOOL_THEN)
|
||||
&& !contains_return(then_block.stmts)
|
||||
{
|
||||
let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) {
|
||||
"then_some"
|
||||
sym::then_some
|
||||
} else {
|
||||
"then"
|
||||
sym::then
|
||||
};
|
||||
let ctxt = expr.span.ctxt();
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
|
|
@ -98,16 +98,18 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
|||
}
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app)
|
||||
let cond_snip = Sugg::hir_with_context(cx, cond, ctxt, "[condition]", &mut app)
|
||||
.maybe_paren()
|
||||
.to_string();
|
||||
let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0;
|
||||
let method_body = if let Some(first_stmt) = then_block.stmts.first() {
|
||||
let (block_snippet, _) =
|
||||
snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app);
|
||||
let closure = if method_name == "then" { "|| " } else { "" };
|
||||
format!("{closure} {{ {block_snippet}; {arg_snip} }}")
|
||||
} else if method_name == "then" {
|
||||
let method_body = if let Some(first_stmt) = then_block.stmts.first()
|
||||
&& let Some(first_stmt_span) = walk_span_to_context(first_stmt.span, ctxt)
|
||||
{
|
||||
let block_snippet =
|
||||
snippet_with_applicability(cx, first_stmt_span.until(then_expr.span), "..", &mut app);
|
||||
let closure = if method_name == sym::then { "|| " } else { "" };
|
||||
format!("{closure} {{ {} {arg_snip} }}", block_snippet.trim_end())
|
||||
} else if method_name == sym::then {
|
||||
(std::borrow::Cow::Borrowed("|| ") + arg_snip).into_owned()
|
||||
} else {
|
||||
arg_snip.into_owned()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
|||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty;
|
||||
use clippy_utils::{is_path_diagnostic_item, ty};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -107,8 +107,7 @@ impl LateLintPass<'_> for InstantSubtraction {
|
|||
|
||||
fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {
|
||||
if let ExprKind::Call(fn_expr, []) = expr_block.kind
|
||||
&& let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr)
|
||||
&& cx.tcx.is_diagnostic_item(sym::instant_now, fn_id)
|
||||
&& is_path_diagnostic_item(cx, fn_expr, sym::instant_now)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@ impl LateLintPass<'_> for LargeIncludeFile {
|
|||
}
|
||||
&& len as u64 > self.max_file_size
|
||||
&& let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||
&& (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id))
|
||||
&& let Some(macro_name) = cx.tcx.get_diagnostic_name(macro_call.def_id)
|
||||
&& matches!(macro_name, sym::include_bytes_macro | sym::include_str_macro)
|
||||
{
|
||||
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
|
||||
span_lint_and_then(
|
||||
|
|
|
|||
|
|
@ -355,12 +355,15 @@ fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option<Le
|
|||
return Some(LenOutput::Integral);
|
||||
}
|
||||
|
||||
if let Res::Def(_, def_id) = res {
|
||||
if cx.tcx.is_diagnostic_item(sym::Option, def_id) && is_first_generic_integral(segment) {
|
||||
return Some(LenOutput::Option(def_id));
|
||||
} else if cx.tcx.is_diagnostic_item(sym::Result, def_id) && is_first_generic_integral(segment) {
|
||||
return Some(LenOutput::Result(def_id));
|
||||
if let Res::Def(_, def_id) = res
|
||||
&& let Some(res) = match cx.tcx.get_diagnostic_name(def_id) {
|
||||
Some(sym::Option) => Some(LenOutput::Option(def_id)),
|
||||
Some(sym::Result) => Some(LenOutput::Result(def_id)),
|
||||
_ => None,
|
||||
}
|
||||
&& is_first_generic_integral(segment)
|
||||
{
|
||||
return Some(res);
|
||||
}
|
||||
|
||||
return None;
|
||||
|
|
@ -368,11 +371,10 @@ fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option<Le
|
|||
|
||||
match *sig.output().kind() {
|
||||
ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral),
|
||||
ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) => {
|
||||
subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did()))
|
||||
},
|
||||
ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => {
|
||||
subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did()))
|
||||
ty::Adt(adt, subs) if subs.type_at(0).is_integral() => match cx.tcx.get_diagnostic_name(adt.did()) {
|
||||
Some(sym::Option) => Some(LenOutput::Option(adt.did())),
|
||||
Some(sym::Result) => Some(LenOutput::Result(adt.did())),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ mod attrs;
|
|||
mod await_holding_invalid;
|
||||
mod blocks_in_conditions;
|
||||
mod bool_assert_comparison;
|
||||
mod bool_comparison;
|
||||
mod bool_to_int_with_if;
|
||||
mod booleans;
|
||||
mod borrow_deref_ref;
|
||||
|
|
@ -474,10 +475,10 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
|
|||
store.register_late_pass(move |_| Box::new(types::Types::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(booleans::NonminimalBool::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant));
|
||||
store.register_late_pass(|_| Box::new(float_literal::FloatLiteral));
|
||||
store.register_late_pass(move |_| Box::new(float_literal::FloatLiteral::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(ptr::Ptr));
|
||||
store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool));
|
||||
store.register_late_pass(|_| Box::new(needless_bool::BoolComparison));
|
||||
store.register_late_pass(|_| Box::new(bool_comparison::BoolComparison));
|
||||
store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach));
|
||||
store.register_late_pass(|_| Box::new(misc::LintPass));
|
||||
store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction));
|
||||
|
|
@ -655,7 +656,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
|
|||
store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(conf)));
|
||||
store.register_late_pass(|_| Box::<macro_use::MacroUseImports>::default());
|
||||
store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch));
|
||||
store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult));
|
||||
store.register_late_pass(|_| Box::<unwrap_in_result::UnwrapInResult>::default());
|
||||
store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
|
||||
store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync));
|
||||
let attrs = attr_storage.clone();
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ use hir::intravisit::{Visitor, walk_expr};
|
|||
use rustc_ast::Label;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{
|
||||
self as hir, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, Expr, ExprKind, FnRetTy, FnSig, Node, TyKind,
|
||||
self as hir, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, FnRetTy,
|
||||
FnSig, Node, TyKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_span::sym;
|
||||
|
|
@ -73,7 +74,11 @@ fn is_inside_unawaited_async_block(cx: &LateContext<'_>, expr: &Expr<'_>) -> boo
|
|||
if let Node::Expr(Expr {
|
||||
kind:
|
||||
ExprKind::Closure(Closure {
|
||||
kind: ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)),
|
||||
kind:
|
||||
ClosureKind::Coroutine(CoroutineKind::Desugared(
|
||||
CoroutineDesugaring::Async,
|
||||
CoroutineSource::Block | CoroutineSource::Closure,
|
||||
)),
|
||||
..
|
||||
}),
|
||||
..
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use super::MISSING_SPIN_LOOP;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::std_or_core;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Block, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::sym;
|
||||
|
||||
fn unpack_cond<'tcx>(cond: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
|
||||
|
|
@ -39,8 +39,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &'
|
|||
) = body.kind
|
||||
&& let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind
|
||||
&& [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name)
|
||||
&& let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind()
|
||||
&& cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did())
|
||||
&& let callee_ty = cx.typeck_results().expr_ty(callee)
|
||||
&& is_type_diagnostic_item(cx, callee_ty, sym::AtomicBool)
|
||||
&& let Some(std_or_core) = std_or_core(cx)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::has_iter_method;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, sugg};
|
||||
use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, peel_hir_expr_while, sugg};
|
||||
use rustc_ast::ast;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -253,12 +253,38 @@ struct VarVisitor<'a, 'tcx> {
|
|||
|
||||
impl<'tcx> VarVisitor<'_, 'tcx> {
|
||||
fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
|
||||
let index_used_directly = matches!(idx.kind, ExprKind::Path(_));
|
||||
let mut used_cnt = 0;
|
||||
// It is `true` if all indices are direct
|
||||
let mut index_used_directly = true;
|
||||
|
||||
// Handle initial index
|
||||
if is_local_used(self.cx, idx, self.var) {
|
||||
used_cnt += 1;
|
||||
index_used_directly &= matches!(idx.kind, ExprKind::Path(_));
|
||||
}
|
||||
// Handle nested indices
|
||||
let seqexpr = peel_hir_expr_while(seqexpr, |e| {
|
||||
if let ExprKind::Index(e, idx, _) = e.kind {
|
||||
if is_local_used(self.cx, idx, self.var) {
|
||||
used_cnt += 1;
|
||||
index_used_directly &= matches!(idx.kind, ExprKind::Path(_));
|
||||
}
|
||||
Some(e)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
match used_cnt {
|
||||
0 => return true,
|
||||
n if n > 1 => self.nonindex = true, // Optimize code like `a[i][i]`
|
||||
_ => {},
|
||||
}
|
||||
|
||||
if let ExprKind::Path(ref seqpath) = seqexpr.kind
|
||||
// the indexed container is referenced by a name
|
||||
&& let QPath::Resolved(None, seqvar) = *seqpath
|
||||
&& seqvar.segments.len() == 1
|
||||
&& is_local_used(self.cx, idx, self.var)
|
||||
{
|
||||
if self.prefer_mutable {
|
||||
self.indexed_mut.insert(seqvar.segments[0].ident.name);
|
||||
|
|
@ -312,7 +338,6 @@ impl<'tcx> VarVisitor<'_, 'tcx> {
|
|||
impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind
|
||||
// a range index op
|
||||
&& let Some(trait_id) = self
|
||||
.cx
|
||||
.typeck_results()
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use super::UNUSED_ENUMERATE_INDEX;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{pat_is_wild, sugg};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::{Expr, ExprKind, Pat, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::sym;
|
||||
|
||||
/// Checks for the `UNUSED_ENUMERATE_INDEX` lint.
|
||||
|
|
@ -17,8 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_
|
|||
&& let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind
|
||||
&& let ty = cx.typeck_results().expr_ty(arg)
|
||||
&& pat_is_wild(cx, &index.kind, body)
|
||||
&& let ty::Adt(base, _) = *ty.kind()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Enumerate, base.did())
|
||||
&& is_type_diagnostic_item(cx, ty, sym::Enumerate)
|
||||
&& let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id)
|
||||
&& cx.tcx.is_diagnostic_item(sym::enumerate_method, call_id)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -97,11 +97,12 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
|
|||
return;
|
||||
}
|
||||
|
||||
if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro) {
|
||||
if let ExprKind::Match(recv, [arm, ..], _) = expr.kind {
|
||||
let range = check_pat(&arm.pat.kind);
|
||||
check_is_ascii(cx, macro_call.span, recv, &range, None);
|
||||
}
|
||||
let (arg, span, range) = if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro)
|
||||
&& let ExprKind::Match(recv, [arm, ..], _) = expr.kind
|
||||
{
|
||||
let recv = peel_ref_operators(cx, recv);
|
||||
let range = check_pat(&arm.pat.kind);
|
||||
(recv, macro_call.span, range)
|
||||
} else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind
|
||||
&& path.ident.name == sym::contains
|
||||
&& let Some(higher::Range {
|
||||
|
|
@ -112,10 +113,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
|
|||
&& !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_))
|
||||
{
|
||||
let arg = peel_ref_operators(cx, arg);
|
||||
let ty_sugg = get_ty_sugg(cx, arg);
|
||||
let range = check_expr_range(start, end);
|
||||
check_is_ascii(cx, expr.span, arg, &range, ty_sugg);
|
||||
}
|
||||
(arg, expr.span, range)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let ty_sugg = get_ty_sugg(cx, arg);
|
||||
check_is_ascii(cx, span, arg, &range, ty_sugg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -146,9 +151,8 @@ fn check_is_ascii(
|
|||
CharRange::HexDigit => "is_ascii_hexdigit",
|
||||
CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => return,
|
||||
};
|
||||
let default_snip = "..";
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_paren();
|
||||
let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), "_", &mut app).maybe_paren();
|
||||
let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))];
|
||||
if let Some((ty_span, ty)) = ty_sugg {
|
||||
suggestion.push((ty_span, format!("{recv}: {ty}")));
|
||||
|
|
@ -182,7 +186,7 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange {
|
|||
CharRange::Otherwise
|
||||
}
|
||||
},
|
||||
PatKind::Range(Some(start), Some(end), kind) if *kind == RangeEnd::Included => check_range(start, end),
|
||||
PatKind::Range(Some(start), Some(end), RangeEnd::Included) => check_range(start, end),
|
||||
_ => CharRange::Otherwise,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,8 +123,8 @@ fn check_iter(
|
|||
) {
|
||||
if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
|
||||
&& let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
|
||||
&& (cx.tcx.is_diagnostic_item(sym::iter_copied, copied_def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::iter_cloned, copied_def_id))
|
||||
&& let Some(copied_name) = cx.tcx.get_diagnostic_name(copied_def_id)
|
||||
&& matches!(copied_name, sym::iter_copied | sym::iter_cloned)
|
||||
&& let hir::ExprKind::MethodCall(_, iter_expr, [_], _) = &filter_expr.kind
|
||||
&& let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(filter_expr.hir_id)
|
||||
&& cx.tcx.is_diagnostic_item(sym::iter_filter, filter_def_id)
|
||||
|
|
@ -243,9 +243,9 @@ fn make_sugg(
|
|||
}
|
||||
|
||||
fn match_acceptable_sym(cx: &LateContext<'_>, collect_def_id: DefId) -> bool {
|
||||
ACCEPTABLE_METHODS
|
||||
.iter()
|
||||
.any(|&method| cx.tcx.is_diagnostic_item(method, collect_def_id))
|
||||
cx.tcx
|
||||
.get_diagnostic_name(collect_def_id)
|
||||
.is_some_and(|collect_name| ACCEPTABLE_METHODS.contains(&collect_name))
|
||||
}
|
||||
|
||||
fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Msrv) -> bool {
|
||||
|
|
|
|||
|
|
@ -75,12 +75,10 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
|
|||
&& let ExprKind::Path(target_path) = &target_arg.kind
|
||||
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id)
|
||||
{
|
||||
let strip_kind = if cx.tcx.is_diagnostic_item(sym::str_starts_with, method_def_id) {
|
||||
StripKind::Prefix
|
||||
} else if cx.tcx.is_diagnostic_item(sym::str_ends_with, method_def_id) {
|
||||
StripKind::Suffix
|
||||
} else {
|
||||
return;
|
||||
let strip_kind = match cx.tcx.get_diagnostic_name(method_def_id) {
|
||||
Some(sym::str_starts_with) => StripKind::Prefix,
|
||||
Some(sym::str_ends_with) => StripKind::Suffix,
|
||||
_ => return,
|
||||
};
|
||||
let target_res = cx.qpath_res(target_path, target_arg.hir_id);
|
||||
if target_res == Res::Err {
|
||||
|
|
|
|||
|
|
@ -116,8 +116,10 @@ fn is_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
|||
/// The expression inside a closure may or may not have surrounding braces and
|
||||
/// semicolons, which causes problems when generating a suggestion. Given an
|
||||
/// expression that evaluates to '()' or '!', recursively remove useless braces
|
||||
/// and semi-colons until is suitable for including in the suggestion template
|
||||
fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Span> {
|
||||
/// and semi-colons until is suitable for including in the suggestion template.
|
||||
/// The `bool` is `true` when the resulting `span` needs to be enclosed in an
|
||||
/// `unsafe` block.
|
||||
fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<(Span, bool)> {
|
||||
if !is_unit_expression(cx, expr) {
|
||||
return None;
|
||||
}
|
||||
|
|
@ -125,22 +127,24 @@ fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<
|
|||
match expr.kind {
|
||||
hir::ExprKind::Call(_, _) | hir::ExprKind::MethodCall(..) => {
|
||||
// Calls can't be reduced any more
|
||||
Some(expr.span)
|
||||
Some((expr.span, false))
|
||||
},
|
||||
hir::ExprKind::Block(block, _) => {
|
||||
let is_unsafe = matches!(block.rules, hir::BlockCheckMode::UnsafeBlock(_));
|
||||
match (block.stmts, block.expr.as_ref()) {
|
||||
([], Some(inner_expr)) => {
|
||||
// If block only contains an expression,
|
||||
// reduce `{ X }` to `X`
|
||||
reduce_unit_expression(cx, inner_expr)
|
||||
.map(|(span, inner_is_unsafe)| (span, inner_is_unsafe || is_unsafe))
|
||||
},
|
||||
([inner_stmt], None) => {
|
||||
// If block only contains statements,
|
||||
// reduce `{ X; }` to `X` or `X;`
|
||||
match inner_stmt.kind {
|
||||
hir::StmtKind::Let(local) => Some(local.span),
|
||||
hir::StmtKind::Expr(e) => Some(e.span),
|
||||
hir::StmtKind::Semi(..) => Some(inner_stmt.span),
|
||||
hir::StmtKind::Let(local) => Some((local.span, is_unsafe)),
|
||||
hir::StmtKind::Expr(e) => Some((e.span, is_unsafe)),
|
||||
hir::StmtKind::Semi(..) => Some((inner_stmt.span, is_unsafe)),
|
||||
hir::StmtKind::Item(..) => None,
|
||||
}
|
||||
},
|
||||
|
|
@ -228,10 +232,11 @@ fn lint_map_unit_fn(
|
|||
let msg = suggestion_msg("closure", map_type);
|
||||
|
||||
span_lint_and_then(cx, lint, expr.span, msg, |diag| {
|
||||
if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) {
|
||||
if let Some((reduced_expr_span, is_unsafe)) = reduce_unit_expression(cx, closure_expr) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let (prefix_is_unsafe, suffix_is_unsafe) = if is_unsafe { ("unsafe { ", " }") } else { ("", "") };
|
||||
let suggestion = format!(
|
||||
"if let {0}({1}) = {2} {{ {3} }}",
|
||||
"if let {0}({1}) = {2} {{ {prefix_is_unsafe}{3}{suffix_is_unsafe} }}",
|
||||
variant,
|
||||
snippet_with_applicability(cx, binding.pat.span, "_", &mut applicability),
|
||||
snippet_with_applicability(cx, var_arg.span, "_", &mut applicability),
|
||||
|
|
|
|||
|
|
@ -4,20 +4,22 @@ use clippy_utils::msrvs::Msrv;
|
|||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{
|
||||
SpanlessEq, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators,
|
||||
SpanlessEq, get_ref_operators, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt,
|
||||
peel_ref_operators,
|
||||
};
|
||||
use rustc_ast::BorrowKind;
|
||||
use rustc_errors::MultiSpan;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
use rustc_hir::{Arm, Expr, HirId, Pat, PatExpr, PatExprKind, PatKind};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatExpr, PatExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::Span;
|
||||
|
||||
use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or};
|
||||
|
||||
pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], msrv: Msrv) {
|
||||
pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], msrv: Msrv) {
|
||||
if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
|
||||
for arm in arms {
|
||||
check_arm(cx, true, arm.pat, arm.body, arm.guard, Some(els_arm.body), msrv);
|
||||
check_arm(cx, true, arm.pat, expr, arm.body, arm.guard, Some(els_arm.body), msrv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,15 +29,18 @@ pub(super) fn check_if_let<'tcx>(
|
|||
pat: &'tcx Pat<'_>,
|
||||
body: &'tcx Expr<'_>,
|
||||
else_expr: Option<&'tcx Expr<'_>>,
|
||||
let_expr: &'tcx Expr<'_>,
|
||||
msrv: Msrv,
|
||||
) {
|
||||
check_arm(cx, false, pat, body, None, else_expr, msrv);
|
||||
check_arm(cx, false, pat, let_expr, body, None, else_expr, msrv);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn check_arm<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
outer_is_match: bool,
|
||||
outer_pat: &'tcx Pat<'tcx>,
|
||||
outer_cond: &'tcx Expr<'tcx>,
|
||||
outer_then_body: &'tcx Expr<'tcx>,
|
||||
outer_guard: Option<&'tcx Expr<'tcx>>,
|
||||
outer_else_body: Option<&'tcx Expr<'tcx>>,
|
||||
|
|
@ -82,6 +87,9 @@ fn check_arm<'tcx>(
|
|||
},
|
||||
IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| is_local_used(cx, arm, binding_id)),
|
||||
}
|
||||
// Check if the inner expression contains any borrows/dereferences
|
||||
&& let ref_types = get_ref_operators(cx, inner_scrutinee)
|
||||
&& let Some(method) = build_ref_method_chain(ref_types)
|
||||
{
|
||||
let msg = format!(
|
||||
"this `{}` can be collapsed into the outer `{}`",
|
||||
|
|
@ -103,6 +111,10 @@ fn check_arm<'tcx>(
|
|||
let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]);
|
||||
help_span.push_span_label(binding_span, "replace this binding");
|
||||
help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}"));
|
||||
if !method.is_empty() {
|
||||
let outer_cond_msg = format!("use: `{}{}`", snippet(cx, outer_cond.span, ".."), method);
|
||||
help_span.push_span_label(outer_cond.span, outer_cond_msg);
|
||||
}
|
||||
diag.span_help(
|
||||
help_span,
|
||||
"the outer pattern can be modified to include the inner pattern",
|
||||
|
|
@ -148,3 +160,30 @@ fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: Hi
|
|||
});
|
||||
(span, is_innermost_parent_pat_struct)
|
||||
}
|
||||
|
||||
/// Builds a chain of reference-manipulation method calls (e.g., `.as_ref()`, `.as_mut()`,
|
||||
/// `.copied()`) based on reference operators
|
||||
fn build_ref_method_chain(expr: Vec<&Expr<'_>>) -> Option<String> {
|
||||
let mut req_method_calls = String::new();
|
||||
|
||||
for ref_operator in expr {
|
||||
match ref_operator.kind {
|
||||
ExprKind::AddrOf(BorrowKind::Raw, _, _) => {
|
||||
return None;
|
||||
},
|
||||
ExprKind::AddrOf(_, m, _) if m.is_mut() => {
|
||||
req_method_calls.push_str(".as_mut()");
|
||||
},
|
||||
ExprKind::AddrOf(_, _, _) => {
|
||||
req_method_calls.push_str(".as_ref()");
|
||||
},
|
||||
// Deref operator is the only operator that this function should have received
|
||||
ExprKind::Unary(_, _) => {
|
||||
req_method_calls.push_str(".copied()");
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
Some(req_method_calls)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1073,7 +1073,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
|||
significant_drop_in_scrutinee::check_match(cx, expr, ex, arms, source);
|
||||
}
|
||||
|
||||
collapsible_match::check_match(cx, arms, self.msrv);
|
||||
collapsible_match::check_match(cx, ex, arms, self.msrv);
|
||||
if !from_expansion {
|
||||
// These don't depend on a relationship between multiple arms
|
||||
match_wild_err_arm::check(cx, ex, arms);
|
||||
|
|
@ -1137,7 +1137,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
|||
match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr);
|
||||
}
|
||||
} else if let Some(if_let) = higher::IfLet::hir(cx, expr) {
|
||||
collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else, self.msrv);
|
||||
collapsible_match::check_if_let(
|
||||
cx,
|
||||
if_let.let_pat,
|
||||
if_let.if_then,
|
||||
if_let.if_else,
|
||||
if_let.let_expr,
|
||||
self.msrv,
|
||||
);
|
||||
significant_drop_in_scrutinee::check_if_let(cx, expr, if_let.let_expr, if_let.if_then, if_let.if_else);
|
||||
if !from_expansion {
|
||||
if let Some(else_expr) = if_let.if_else {
|
||||
|
|
|
|||
|
|
@ -226,11 +226,12 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)]
|
||||
enum SigDropHolder {
|
||||
/// No values with significant drop present in this expression.
|
||||
///
|
||||
/// Expressions that we've emitted lints do not count.
|
||||
#[default]
|
||||
None,
|
||||
/// Some field in this expression references to values with significant drop.
|
||||
///
|
||||
|
|
@ -244,12 +245,6 @@ enum SigDropHolder {
|
|||
Moved,
|
||||
}
|
||||
|
||||
impl Default for SigDropHolder {
|
||||
fn default() -> Self {
|
||||
Self::None
|
||||
}
|
||||
}
|
||||
|
||||
struct SigDropHelper<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
parent_expr: Option<&'tcx Expr<'tcx>>,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::ty::option_arg_ty;
|
||||
use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::ResultErr;
|
||||
|
|
@ -28,25 +28,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
|
|||
&& is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr)
|
||||
&& let Some(return_ty) = find_return_type(cx, &expr.kind)
|
||||
{
|
||||
let prefix;
|
||||
let suffix;
|
||||
let err_ty;
|
||||
|
||||
if let Some(ty) = result_error_type(cx, return_ty) {
|
||||
prefix = "Err(";
|
||||
suffix = ")";
|
||||
err_ty = ty;
|
||||
let (prefix, suffix, err_ty) = if let Some(ty) = result_error_type(cx, return_ty) {
|
||||
("Err(", ")", ty)
|
||||
} else if let Some(ty) = poll_result_error_type(cx, return_ty) {
|
||||
prefix = "Poll::Ready(Err(";
|
||||
suffix = "))";
|
||||
err_ty = ty;
|
||||
("Poll::Ready(Err(", "))", ty)
|
||||
} else if let Some(ty) = poll_option_result_error_type(cx, return_ty) {
|
||||
prefix = "Poll::Ready(Some(Err(";
|
||||
suffix = ")))";
|
||||
err_ty = ty;
|
||||
("Poll::Ready(Some(Err(", ")))", ty)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
|
|
@ -88,8 +78,8 @@ fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> O
|
|||
|
||||
/// Extracts the error type from Result<T, E>.
|
||||
fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
if let ty::Adt(_, subst) = ty.kind()
|
||||
&& is_type_diagnostic_item(cx, ty, sym::Result)
|
||||
if let ty::Adt(def, subst) = ty.kind()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Result, def.did())
|
||||
{
|
||||
Some(subst.type_at(1))
|
||||
} else {
|
||||
|
|
@ -101,11 +91,9 @@ fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
|
|||
fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
if let ty::Adt(def, subst) = ty.kind()
|
||||
&& cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did())
|
||||
&& let ready_ty = subst.type_at(0)
|
||||
&& let ty::Adt(ready_def, ready_subst) = ready_ty.kind()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Result, ready_def.did())
|
||||
{
|
||||
Some(ready_subst.type_at(1))
|
||||
let ready_ty = subst.type_at(0);
|
||||
result_error_type(cx, ready_ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -116,13 +104,9 @@ fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) ->
|
|||
if let ty::Adt(def, subst) = ty.kind()
|
||||
&& cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did())
|
||||
&& let ready_ty = subst.type_at(0)
|
||||
&& let ty::Adt(ready_def, ready_subst) = ready_ty.kind()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Option, ready_def.did())
|
||||
&& let some_ty = ready_subst.type_at(0)
|
||||
&& let ty::Adt(some_def, some_subst) = some_ty.kind()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Result, some_def.did())
|
||||
&& let Some(some_ty) = option_arg_ty(cx, ready_ty)
|
||||
{
|
||||
Some(some_subst.type_at(1))
|
||||
result_error_type(cx, some_ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -215,7 +215,8 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'
|
|||
&& let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
|
||||
&& let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id()
|
||||
{
|
||||
if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) {
|
||||
let repl_name = cx.tcx.get_diagnostic_name(repl_def_id);
|
||||
if repl_name == Some(sym::mem_uninitialized) {
|
||||
let Some(top_crate) = std_or_core(cx) else { return };
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
|
|
@ -230,9 +231,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'
|
|||
),
|
||||
applicability,
|
||||
);
|
||||
} else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id)
|
||||
&& !cx.typeck_results().expr_ty(src).is_primitive()
|
||||
{
|
||||
} else if repl_name == Some(sym::mem_zeroed) && !cx.typeck_results().expr_ty(src).is_primitive() {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
MEM_REPLACE_WITH_UNINIT,
|
||||
|
|
|
|||
|
|
@ -24,9 +24,10 @@ pub(super) fn check(
|
|||
&& let Some(name) = cx.tcx.get_diagnostic_name(adt.did())
|
||||
{
|
||||
let caller_type = match name {
|
||||
sym::Rc => "Rc",
|
||||
sym::Arc => "Arc",
|
||||
sym::RcWeak | sym::ArcWeak => "Weak",
|
||||
sym::Rc => "std::rc::Rc",
|
||||
sym::Arc => "std::sync::Arc",
|
||||
sym::RcWeak => "std::rc::Weak",
|
||||
sym::ArcWeak => "std::sync::Weak",
|
||||
_ => return,
|
||||
};
|
||||
span_lint_and_then(
|
||||
|
|
|
|||
|
|
@ -233,18 +233,16 @@ impl<'tcx> OffendingFilterExpr<'tcx> {
|
|||
// the latter only calls `effect` once
|
||||
let side_effect_expr_span = receiver.can_have_side_effects().then_some(receiver.span);
|
||||
|
||||
if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name == sym::is_some {
|
||||
Some(Self::IsSome {
|
||||
match (cx.tcx.get_diagnostic_name(recv_ty.did()), path.ident.name) {
|
||||
(Some(sym::Option), sym::is_some) => Some(Self::IsSome {
|
||||
receiver,
|
||||
side_effect_expr_span,
|
||||
})
|
||||
} else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name == sym::is_ok {
|
||||
Some(Self::IsOk {
|
||||
}),
|
||||
(Some(sym::Result), sym::is_ok) => Some(Self::IsOk {
|
||||
receiver,
|
||||
side_effect_expr_span,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
} else if matching_root_macro_call(cx, expr.span, sym::matches_macro).is_some()
|
||||
// we know for a fact that the wildcard pattern is the second arm
|
||||
|
|
|
|||
|
|
@ -24,32 +24,29 @@ fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) ->
|
|||
let ty::Adt(adt, substs) = cx.typeck_results().expr_ty(iter).kind() else {
|
||||
return None;
|
||||
};
|
||||
let did = adt.did();
|
||||
|
||||
if cx.tcx.is_diagnostic_item(sym::ArrayIntoIter, did) {
|
||||
// For array::IntoIter<T, const N: usize>, the length is the second generic
|
||||
// parameter.
|
||||
substs.const_at(1).try_to_target_usize(cx.tcx).map(u128::from)
|
||||
} else if cx.tcx.is_diagnostic_item(sym::SliceIter, did)
|
||||
&& let ExprKind::MethodCall(_, recv, ..) = iter.kind
|
||||
{
|
||||
if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() {
|
||||
// For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..)
|
||||
len.try_to_target_usize(cx.tcx).map(u128::from)
|
||||
} else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) {
|
||||
match args {
|
||||
VecArgs::Vec(vec) => vec.len().try_into().ok(),
|
||||
VecArgs::Repeat(_, len) => expr_as_u128(cx, len),
|
||||
match cx.tcx.get_diagnostic_name(adt.did()) {
|
||||
Some(sym::ArrayIntoIter) => {
|
||||
// For array::IntoIter<T, const N: usize>, the length is the second generic
|
||||
// parameter.
|
||||
substs.const_at(1).try_to_target_usize(cx.tcx).map(u128::from)
|
||||
},
|
||||
Some(sym::SliceIter) if let ExprKind::MethodCall(_, recv, ..) = iter.kind => {
|
||||
if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() {
|
||||
// For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..)
|
||||
len.try_to_target_usize(cx.tcx).map(u128::from)
|
||||
} else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) {
|
||||
match args {
|
||||
VecArgs::Vec(vec) => vec.len().try_into().ok(),
|
||||
VecArgs::Repeat(_, len) => expr_as_u128(cx, len),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if cx.tcx.is_diagnostic_item(sym::IterEmpty, did) {
|
||||
Some(0)
|
||||
} else if cx.tcx.is_diagnostic_item(sym::IterOnce, did) {
|
||||
Some(1)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
Some(sym::IterEmpty) => Some(0),
|
||||
Some(sym::IterOnce) => Some(1),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,10 +50,10 @@ fn try_get_caller_ty_name_and_method_name(
|
|||
}
|
||||
} else {
|
||||
if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(caller_expr).kind() {
|
||||
if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) {
|
||||
return Some(("Option", "and_then"));
|
||||
} else if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) {
|
||||
return Some(("Result", "and_then"));
|
||||
match cx.tcx.get_diagnostic_name(adt.did()) {
|
||||
Some(sym::Option) => return Some(("Option", "and_then")),
|
||||
Some(sym::Result) => return Some(("Result", "and_then")),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
None
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_expr_untyped_identity_function, is_trait_method, path_to_local};
|
||||
use rustc_ast::BindingMode;
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_expr_untyped_identity_function, is_mutable, is_trait_method, path_to_local_with_projections};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{self as hir, Node, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_hir::{self as hir, ExprKind, Node, PatKind};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use super::MAP_IDENTITY;
|
||||
|
||||
const MSG: &str = "unnecessary map of the identity function";
|
||||
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
|
|
@ -23,26 +25,70 @@ pub(super) fn check(
|
|||
|| is_type_diagnostic_item(cx, caller_ty, sym::Result)
|
||||
|| is_type_diagnostic_item(cx, caller_ty, sym::Option))
|
||||
&& is_expr_untyped_identity_function(cx, map_arg)
|
||||
&& let Some(sugg_span) = expr.span.trim_start(caller.span)
|
||||
&& let Some(call_span) = expr.span.trim_start(caller.span)
|
||||
{
|
||||
// If the result of `.map(identity)` is used as a mutable reference,
|
||||
// the caller must not be an immutable binding.
|
||||
if cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr()
|
||||
&& let Some(hir_id) = path_to_local(caller)
|
||||
&& let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
|
||||
&& !matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
|
||||
{
|
||||
return;
|
||||
}
|
||||
let main_sugg = (call_span, String::new());
|
||||
let mut app = if is_copy(cx, caller_ty) {
|
||||
// there is technically a behavioral change here for `Copy` iterators, where
|
||||
// `iter.map(|x| x).next()` would mutate a temporary copy of the iterator and
|
||||
// changing it to `iter.next()` mutates iter directly
|
||||
Applicability::Unspecified
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_IDENTITY,
|
||||
sugg_span,
|
||||
"unnecessary map of the identity function",
|
||||
format!("remove the call to `{name}`"),
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
let needs_to_be_mutable = cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr();
|
||||
if needs_to_be_mutable && !is_mutable(cx, caller) {
|
||||
if let Some(hir_id) = path_to_local_with_projections(caller)
|
||||
&& let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
|
||||
&& let PatKind::Binding(_, _, ident, _) = pat.kind
|
||||
{
|
||||
// We can reach the binding -- suggest making it mutable
|
||||
let suggs = vec![main_sugg, (ident.span.shrink_to_lo(), String::from("mut "))];
|
||||
|
||||
let ident = snippet_with_applicability(cx.sess(), ident.span, "_", &mut app);
|
||||
|
||||
span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| {
|
||||
diag.multipart_suggestion(
|
||||
format!("remove the call to `{name}`, and make `{ident}` mutable"),
|
||||
suggs,
|
||||
app,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// If we can't make the binding mutable, prevent the suggestion from being automatically applied,
|
||||
// and add a complementary help message.
|
||||
app = Applicability::Unspecified;
|
||||
|
||||
let method_requiring_mut = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id)
|
||||
&& let ExprKind::MethodCall(method, ..) = expr.kind
|
||||
{
|
||||
Some(method.ident)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| {
|
||||
diag.span_suggestion(main_sugg.0, format!("remove the call to `{name}`"), main_sugg.1, app);
|
||||
|
||||
let note = if let Some(method_requiring_mut) = method_requiring_mut {
|
||||
format!("this must be made mutable to use `{method_requiring_mut}`")
|
||||
} else {
|
||||
"this must be made mutable".to_string()
|
||||
};
|
||||
diag.span_note(caller.span, note);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_IDENTITY,
|
||||
main_sugg.0,
|
||||
MSG,
|
||||
format!("remove the call to `{name}`"),
|
||||
main_sugg.1,
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,17 +38,13 @@ pub(super) fn check(
|
|||
];
|
||||
|
||||
let is_deref = match map_arg.kind {
|
||||
hir::ExprKind::Path(ref expr_qpath) => {
|
||||
cx.qpath_res(expr_qpath, map_arg.hir_id)
|
||||
.opt_def_id()
|
||||
.is_some_and(|fun_def_id| {
|
||||
cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::deref_mut_method, fun_def_id)
|
||||
|| deref_aliases
|
||||
.iter()
|
||||
.any(|&sym| cx.tcx.is_diagnostic_item(sym, fun_def_id))
|
||||
})
|
||||
},
|
||||
hir::ExprKind::Path(ref expr_qpath) => cx
|
||||
.qpath_res(expr_qpath, map_arg.hir_id)
|
||||
.opt_def_id()
|
||||
.and_then(|fun_def_id| cx.tcx.get_diagnostic_name(fun_def_id))
|
||||
.is_some_and(|fun_name| {
|
||||
matches!(fun_name, sym::deref_method | sym::deref_mut_method) || deref_aliases.contains(&fun_name)
|
||||
}),
|
||||
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
|
||||
let closure_body = cx.tcx.hir_body(body);
|
||||
let closure_expr = peel_blocks(closure_body.value);
|
||||
|
|
@ -63,13 +59,11 @@ pub(super) fn check(
|
|||
.map(|x| &x.kind)
|
||||
.collect::<Box<[_]>>()
|
||||
&& let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj
|
||||
&& let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap()
|
||||
&& let Some(method_name) = cx.tcx.get_diagnostic_name(method_did)
|
||||
{
|
||||
let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap();
|
||||
cx.tcx.is_diagnostic_item(sym::deref_method, method_did)
|
||||
|| cx.tcx.is_diagnostic_item(sym::deref_mut_method, method_did)
|
||||
|| deref_aliases
|
||||
.iter()
|
||||
.any(|&sym| cx.tcx.is_diagnostic_item(sym, method_did))
|
||||
matches!(method_name, sym::deref_method | sym::deref_mut_method)
|
||||
|| deref_aliases.contains(&method_name)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: Lang
|
|||
if let ExprKind::Call(some_expr, [arg]) = expr.kind
|
||||
&& is_res_lang_ctor(cx, path_res(cx, some_expr), item)
|
||||
{
|
||||
Some(arg.span)
|
||||
Some(arg.span.source_callsite())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool {
|
|||
}
|
||||
|
||||
pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) {
|
||||
if let Some(recv_adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Stdin, recv_adt.did())
|
||||
let recv_ty = cx.typeck_results().expr_ty(recv);
|
||||
if is_type_diagnostic_item(cx, recv_ty, sym::Stdin)
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind
|
||||
&& let Res::Local(local_id) = path.res
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ use rustc_span::sym;
|
|||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||
if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
|
||||
if cx.tcx.is_diagnostic_item(sym::string_push_str, fn_def_id) {
|
||||
single_char_push_string::check(cx, expr, receiver, args);
|
||||
} else if cx.tcx.is_diagnostic_item(sym::string_insert_str, fn_def_id) {
|
||||
single_char_insert_string::check(cx, expr, receiver, args);
|
||||
match cx.tcx.get_diagnostic_name(fn_def_id) {
|
||||
Some(sym::string_push_str) => single_char_push_string::check(cx, expr, receiver, args),
|
||||
Some(sym::string_insert_str) => single_char_insert_string::check(cx, expr, receiver, args),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_diag_trait_item;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
use rustc_middle::ty::{self};
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::SUSPICIOUS_TO_OWNED;
|
||||
|
|
@ -14,8 +14,7 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -
|
|||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||
&& is_diag_trait_item(cx, method_def_id, sym::ToOwned)
|
||||
&& let input_type = cx.typeck_results().expr_ty(expr)
|
||||
&& let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Cow, adt.did())
|
||||
&& is_type_diagnostic_item(cx, input_type, sym::Cow)
|
||||
{
|
||||
let mut app = Applicability::MaybeIncorrect;
|
||||
let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ pub(super) fn check<'tcx>(
|
|||
let typeck_results = cx.typeck_results();
|
||||
let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), typeck_results);
|
||||
if let Some(id) = typeck_results.type_dependent_def_id(expr.hir_id)
|
||||
&& (cx.tcx.is_diagnostic_item(sym::cmp_ord_min, id) || cx.tcx.is_diagnostic_item(sym::cmp_ord_max, id))
|
||||
&& let Some(fn_name) = cx.tcx.get_diagnostic_name(id)
|
||||
&& matches!(fn_name, sym::cmp_ord_min | sym::cmp_ord_max)
|
||||
{
|
||||
if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv)
|
||||
&& let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::source::{SpanRangeExt, snippet};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::AdtDef;
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
use crate::loops::UNUSED_ENUMERATE_INDEX;
|
||||
|
|
@ -39,9 +39,8 @@ use crate::loops::UNUSED_ENUMERATE_INDEX;
|
|||
/// * `closure_arg`: The argument to the map function call containing the closure/function to apply
|
||||
pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) {
|
||||
let recv_ty = cx.typeck_results().expr_ty(recv);
|
||||
if let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did)
|
||||
// If we call a method on a `std::iter::Enumerate` instance
|
||||
&& cx.tcx.is_diagnostic_item(sym::Enumerate, recv_ty_defid)
|
||||
// If we call a method on a `std::iter::Enumerate` instance
|
||||
if is_type_diagnostic_item(cx, recv_ty, sym::Enumerate)
|
||||
// If we are calling a method of the `Iterator` trait
|
||||
&& is_trait_method(cx, call_expr, sym::Iterator)
|
||||
// And the map argument is a closure
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_hir};
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{self as hir, Attribute, find_attr};
|
||||
|
|
@ -64,14 +64,20 @@ declare_clippy_lint! {
|
|||
"detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)"
|
||||
}
|
||||
|
||||
fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[Attribute], sp: Span, desc: &'static str) {
|
||||
fn check_missing_inline_attrs(
|
||||
cx: &LateContext<'_>,
|
||||
attrs: &[Attribute],
|
||||
sp: Span,
|
||||
desc: &'static str,
|
||||
hir_id: Option<hir::HirId>,
|
||||
) {
|
||||
if !find_attr!(attrs, AttributeKind::Inline(..)) {
|
||||
span_lint(
|
||||
cx,
|
||||
MISSING_INLINE_IN_PUBLIC_ITEMS,
|
||||
sp,
|
||||
format!("missing `#[inline]` for {desc}"),
|
||||
);
|
||||
let msg = format!("missing `#[inline]` for {desc}");
|
||||
if let Some(hir_id) = hir_id {
|
||||
span_lint_hir(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, hir_id, sp, msg);
|
||||
} else {
|
||||
span_lint(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, sp, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -103,17 +109,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
|
|||
|
||||
let desc = "a function";
|
||||
let attrs = cx.tcx.hir_attrs(it.hir_id());
|
||||
check_missing_inline_attrs(cx, attrs, it.span, desc);
|
||||
check_missing_inline_attrs(cx, attrs, it.span, desc, None);
|
||||
},
|
||||
hir::ItemKind::Trait(
|
||||
ref _constness,
|
||||
ref _is_auto,
|
||||
ref _unsafe,
|
||||
_ident,
|
||||
_generics,
|
||||
_bounds,
|
||||
trait_items,
|
||||
) => {
|
||||
hir::ItemKind::Trait(.., trait_items) => {
|
||||
// note: we need to check if the trait is exported so we can't use
|
||||
// `LateLintPass::check_trait_item` here.
|
||||
for &tit in trait_items {
|
||||
|
|
@ -127,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
|
|||
let desc = "a default trait method";
|
||||
let item = cx.tcx.hir_trait_item(tit);
|
||||
let attrs = cx.tcx.hir_attrs(item.hir_id());
|
||||
check_missing_inline_attrs(cx, attrs, item.span, desc);
|
||||
check_missing_inline_attrs(cx, attrs, item.span, desc, Some(tit.hir_id()));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -182,7 +180,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
|
|||
}
|
||||
|
||||
let attrs = cx.tcx.hir_attrs(impl_item.hir_id());
|
||||
check_missing_inline_attrs(cx, attrs, impl_item.span, desc);
|
||||
check_missing_inline_attrs(cx, attrs, impl_item.span, desc, None);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
|
@ -83,13 +85,18 @@ fn check_arguments<'tcx>(
|
|||
let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs();
|
||||
for (argument, parameter) in iter::zip(arguments, parameters) {
|
||||
if let ty::Ref(_, _, Mutability::Not) | ty::RawPtr(_, Mutability::Not) = parameter.kind()
|
||||
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind
|
||||
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, arg) = argument.kind
|
||||
{
|
||||
span_lint(
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let sugg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability).addr();
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_MUT_PASSED,
|
||||
argument.span,
|
||||
format!("the {fn_kind} `{name}` doesn't need a mutable reference"),
|
||||
"remove this `mut`",
|
||||
sugg.to_string(),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,14 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
|||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{
|
||||
SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt,
|
||||
is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_contains_comment, sym,
|
||||
SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_parent_stmt, is_receiver_of_method_call,
|
||||
peel_blocks, peel_blocks_with_stmt, span_contains_comment,
|
||||
};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -50,31 +48,6 @@ declare_clippy_lint! {
|
|||
"if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for expressions of the form `x == true`,
|
||||
/// `x != true` and order comparisons such as `x < true` (or vice versa) and
|
||||
/// suggest using the variable directly.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Unnecessary code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// if x == true {}
|
||||
/// if y == false {}
|
||||
/// ```
|
||||
/// use `x` directly:
|
||||
/// ```rust,ignore
|
||||
/// if x {}
|
||||
/// if !y {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub BOOL_COMPARISON,
|
||||
complexity,
|
||||
"comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for expressions of the form `if c { x = true } else { x = false }`
|
||||
|
|
@ -224,201 +197,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
|
|||
}
|
||||
}
|
||||
|
||||
declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BoolComparison {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if e.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Binary(Spanned { node, .. }, ..) = e.kind {
|
||||
let ignore_case = None::<(fn(_) -> _, &str)>;
|
||||
let ignore_no_literal = None::<(fn(_, _) -> _, &str)>;
|
||||
match node {
|
||||
BinOpKind::Eq => {
|
||||
let true_case = Some((|h| h, "equality checks against true are unnecessary"));
|
||||
let false_case = Some((
|
||||
|h: Sugg<'tcx>| !h,
|
||||
"equality checks against false can be replaced by a negation",
|
||||
));
|
||||
check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal);
|
||||
},
|
||||
BinOpKind::Ne => {
|
||||
let true_case = Some((
|
||||
|h: Sugg<'tcx>| !h,
|
||||
"inequality checks against true can be replaced by a negation",
|
||||
));
|
||||
let false_case = Some((|h| h, "inequality checks against false are unnecessary"));
|
||||
check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal);
|
||||
},
|
||||
BinOpKind::Lt => check_comparison(
|
||||
cx,
|
||||
e,
|
||||
ignore_case,
|
||||
Some((|h| h, "greater than checks against false are unnecessary")),
|
||||
Some((
|
||||
|h: Sugg<'tcx>| !h,
|
||||
"less than comparison against true can be replaced by a negation",
|
||||
)),
|
||||
ignore_case,
|
||||
Some((
|
||||
|l: Sugg<'tcx>, r: Sugg<'tcx>| (!l).bit_and(&r),
|
||||
"order comparisons between booleans can be simplified",
|
||||
)),
|
||||
),
|
||||
BinOpKind::Gt => check_comparison(
|
||||
cx,
|
||||
e,
|
||||
Some((
|
||||
|h: Sugg<'tcx>| !h,
|
||||
"less than comparison against true can be replaced by a negation",
|
||||
)),
|
||||
ignore_case,
|
||||
ignore_case,
|
||||
Some((|h| h, "greater than checks against false are unnecessary")),
|
||||
Some((
|
||||
|l: Sugg<'tcx>, r: Sugg<'tcx>| l.bit_and(&(!r)),
|
||||
"order comparisons between booleans can be simplified",
|
||||
)),
|
||||
),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ExpressionInfoWithSpan {
|
||||
one_side_is_unary_not: bool,
|
||||
left_span: Span,
|
||||
right_span: Span,
|
||||
}
|
||||
|
||||
fn is_unary_not(e: &Expr<'_>) -> (bool, Span) {
|
||||
if let ExprKind::Unary(UnOp::Not, operand) = e.kind {
|
||||
return (true, operand.span);
|
||||
}
|
||||
(false, e.span)
|
||||
}
|
||||
|
||||
fn one_side_is_unary_not<'tcx>(left_side: &'tcx Expr<'_>, right_side: &'tcx Expr<'_>) -> ExpressionInfoWithSpan {
|
||||
let left = is_unary_not(left_side);
|
||||
let right = is_unary_not(right_side);
|
||||
|
||||
ExpressionInfoWithSpan {
|
||||
one_side_is_unary_not: left.0 != right.0,
|
||||
left_span: left.1,
|
||||
right_span: right.1,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_comparison<'a, 'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>,
|
||||
left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>,
|
||||
right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>,
|
||||
right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>,
|
||||
no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &'static str)>,
|
||||
) {
|
||||
if let ExprKind::Binary(op, left_side, right_side) = e.kind {
|
||||
let (l_ty, r_ty) = (
|
||||
cx.typeck_results().expr_ty(left_side),
|
||||
cx.typeck_results().expr_ty(right_side),
|
||||
);
|
||||
if is_expn_of(left_side.span, sym::cfg).is_some() || is_expn_of(right_side.span, sym::cfg).is_some() {
|
||||
return;
|
||||
}
|
||||
if l_ty.is_bool() && r_ty.is_bool() {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
// Eliminate parentheses in `e` by using the lo pos of lhs and hi pos of rhs,
|
||||
// calling `source_callsite` make sure macros are handled correctly, see issue #9907
|
||||
let binop_span = left_side
|
||||
.span
|
||||
.source_callsite()
|
||||
.with_hi(right_side.span.source_callsite().hi());
|
||||
|
||||
if op.node == BinOpKind::Eq {
|
||||
let expression_info = one_side_is_unary_not(left_side, right_side);
|
||||
if expression_info.one_side_is_unary_not {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BOOL_COMPARISON,
|
||||
binop_span,
|
||||
"this comparison might be written more concisely",
|
||||
"try simplifying it as shown",
|
||||
format!(
|
||||
"{} != {}",
|
||||
snippet_with_applicability(
|
||||
cx,
|
||||
expression_info.left_span.source_callsite(),
|
||||
"..",
|
||||
&mut applicability
|
||||
),
|
||||
snippet_with_applicability(
|
||||
cx,
|
||||
expression_info.right_span.source_callsite(),
|
||||
"..",
|
||||
&mut applicability
|
||||
)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
|
||||
(Some(true), None) => left_true.map_or((), |(h, m)| {
|
||||
suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h);
|
||||
}),
|
||||
(None, Some(true)) => right_true.map_or((), |(h, m)| {
|
||||
suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h);
|
||||
}),
|
||||
(Some(false), None) => left_false.map_or((), |(h, m)| {
|
||||
suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h);
|
||||
}),
|
||||
(None, Some(false)) => right_false.map_or((), |(h, m)| {
|
||||
suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h);
|
||||
}),
|
||||
(None, None) => no_literal.map_or((), |(h, m)| {
|
||||
let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability);
|
||||
let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BOOL_COMPARISON,
|
||||
binop_span,
|
||||
m,
|
||||
"try simplifying it as shown",
|
||||
h(left_side, right_side).into_string(),
|
||||
applicability,
|
||||
);
|
||||
}),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_bool_comparison<'a, 'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
span: Span,
|
||||
expr: &Expr<'_>,
|
||||
mut app: Applicability,
|
||||
message: &'static str,
|
||||
conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
|
||||
) {
|
||||
let hint = Sugg::hir_with_context(cx, expr, span.ctxt(), "..", &mut app);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BOOL_COMPARISON,
|
||||
span,
|
||||
message,
|
||||
"try simplifying it as shown",
|
||||
conv_hint(hint).into_string(),
|
||||
app,
|
||||
);
|
||||
}
|
||||
|
||||
enum Expression {
|
||||
Bool(bool),
|
||||
RetBool(bool),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{Visitor, walk_expr};
|
||||
use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind};
|
||||
use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
|
@ -70,12 +70,24 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
|
|||
&& has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some()
|
||||
// Skip the lint if the body is not block because this is simpler than `for` loop.
|
||||
// e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop.
|
||||
&& let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind
|
||||
&& let ExprKind::Closure(&Closure { body, fn_decl, .. }) = for_each_arg.kind
|
||||
&& let body = cx.tcx.hir_body(body)
|
||||
// Skip the lint if the body is not safe, so as not to suggest `for … in … unsafe {}`
|
||||
// and suggesting `for … in … { unsafe { } }` is a little ugly.
|
||||
&& !matches!(body.value.kind, ExprKind::Block(Block { rules: BlockCheckMode::UnsafeBlock(_), .. }, ..))
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
// If any closure parameter has an explicit type specified, applying the lint would necessarily
|
||||
// remove that specification, possibly breaking type inference
|
||||
if fn_decl
|
||||
.inputs
|
||||
.iter()
|
||||
.any(|input| matches!(input.kind, TyKind::Infer(..)))
|
||||
{
|
||||
applicability = Applicability::MaybeIncorrect;
|
||||
}
|
||||
|
||||
let mut ret_collector = RetCollector::default();
|
||||
ret_collector.visit_expr(body.value);
|
||||
|
||||
|
|
@ -84,18 +96,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
|
|||
return;
|
||||
}
|
||||
|
||||
let (mut applicability, ret_suggs) = if ret_collector.spans.is_empty() {
|
||||
(Applicability::MachineApplicable, None)
|
||||
let ret_suggs = if ret_collector.spans.is_empty() {
|
||||
None
|
||||
} else {
|
||||
(
|
||||
Applicability::MaybeIncorrect,
|
||||
Some(
|
||||
ret_collector
|
||||
.spans
|
||||
.into_iter()
|
||||
.map(|span| (span, "continue".to_string()))
|
||||
.collect(),
|
||||
),
|
||||
applicability = Applicability::MaybeIncorrect;
|
||||
Some(
|
||||
ret_collector
|
||||
.spans
|
||||
.into_iter()
|
||||
.map(|span| (span, "continue".to_string()))
|
||||
.collect(),
|
||||
)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -134,7 +134,8 @@ impl LateLintPass<'_> for NonCanonicalImpls {
|
|||
return;
|
||||
}
|
||||
|
||||
if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl.def_id)
|
||||
let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id);
|
||||
if trait_name == Some(sym::Clone)
|
||||
&& let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy)
|
||||
&& implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[])
|
||||
{
|
||||
|
|
@ -170,12 +171,8 @@ impl LateLintPass<'_> for NonCanonicalImpls {
|
|||
String::new(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if cx.tcx.is_diagnostic_item(sym::PartialOrd, trait_impl.def_id)
|
||||
} else if trait_name == Some(sym::PartialOrd)
|
||||
&& impl_item.ident.name == sym::partial_cmp
|
||||
&& let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord)
|
||||
&& implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[])
|
||||
|
|
|
|||
|
|
@ -43,13 +43,11 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
|
|||
match &expr.kind {
|
||||
ExprKind::MethodCall(path, func, [param], _) => {
|
||||
if let Some(adt) = cx.typeck_results().expr_ty(func).peel_refs().ty_adt_def()
|
||||
&& ((path.ident.name == sym::mode
|
||||
&& matches!(
|
||||
cx.tcx.get_diagnostic_name(adt.did()),
|
||||
Some(sym::FsOpenOptions | sym::DirBuilder)
|
||||
))
|
||||
|| (path.ident.name == sym::set_mode
|
||||
&& cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did())))
|
||||
&& matches!(
|
||||
(cx.tcx.get_diagnostic_name(adt.did()), path.ident.name),
|
||||
(Some(sym::FsOpenOptions | sym::DirBuilder), sym::mode)
|
||||
| (Some(sym::FsPermissions), sym::set_mode)
|
||||
)
|
||||
&& let ExprKind::Lit(_) = param.kind
|
||||
&& param.span.eq_ctxt(expr.span)
|
||||
&& param
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::Msrv;
|
||||
use clippy_utils::qualify_min_const_fn::is_stable_const_fn;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::visitors::for_each_expr_without_closures;
|
||||
|
|
@ -20,7 +21,7 @@ pub(super) fn check<'tcx>(
|
|||
expr: &'tcx hir::Expr<'_>,
|
||||
assignee: &'tcx hir::Expr<'_>,
|
||||
e: &'tcx hir::Expr<'_>,
|
||||
_msrv: Msrv,
|
||||
msrv: Msrv,
|
||||
) {
|
||||
if let hir::ExprKind::Binary(op, l, r) = &e.kind {
|
||||
let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| {
|
||||
|
|
@ -43,10 +44,28 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
// Skip if the trait is not stable in const contexts
|
||||
// FIXME: reintroduce a better check after this is merged back into Clippy
|
||||
// Skip if the trait or the implementation is not stable in const contexts
|
||||
if is_in_const_context(cx) {
|
||||
return;
|
||||
if cx
|
||||
.tcx
|
||||
.associated_item_def_ids(trait_id)
|
||||
.first()
|
||||
.is_none_or(|binop_id| !is_stable_const_fn(cx, *binop_id, msrv))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let impls = cx.tcx.non_blanket_impls_for_ty(trait_id, rty).collect::<Vec<_>>();
|
||||
if impls.is_empty()
|
||||
|| impls.into_iter().any(|impl_id| {
|
||||
cx.tcx
|
||||
.associated_item_def_ids(impl_id)
|
||||
.first()
|
||||
.is_none_or(|fn_id| !is_stable_const_fn(cx, *fn_id, msrv))
|
||||
})
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
|
|
|
|||
|
|
@ -47,14 +47,10 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool)
|
|||
(arg, arg.span)
|
||||
},
|
||||
ExprKind::Call(path, [arg])
|
||||
if path_def_id(cx, path).is_some_and(|did| {
|
||||
if cx.tcx.is_diagnostic_item(sym::from_str_method, did) {
|
||||
true
|
||||
} else if cx.tcx.is_diagnostic_item(sym::from_fn, did) {
|
||||
!is_copy(cx, typeck.expr_ty(expr))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
if path_def_id(cx, path).is_some_and(|did| match cx.tcx.get_diagnostic_name(did) {
|
||||
Some(sym::from_str_method) => true,
|
||||
Some(sym::from_fn) => !is_copy(cx, typeck.expr_ty(expr)),
|
||||
_ => false,
|
||||
}) =>
|
||||
{
|
||||
(arg, arg.span)
|
||||
|
|
|
|||
|
|
@ -34,10 +34,11 @@ pub(crate) fn check<'tcx>(
|
|||
val_r,
|
||||
) = lhs.kind
|
||||
|
||||
// right hand side matches either f32::EPSILON or f64::EPSILON
|
||||
// right hand side matches _::EPSILON
|
||||
&& let ExprKind::Path(ref epsilon_path) = rhs.kind
|
||||
&& let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id)
|
||||
&& ([sym::f32_epsilon, sym::f64_epsilon].into_iter().any(|sym| cx.tcx.is_diagnostic_item(sym, def_id)))
|
||||
&& let Some(sym) = cx.tcx.get_diagnostic_name(def_id)
|
||||
&& matches!(sym, sym::f16_epsilon | sym::f32_epsilon | sym::f64_epsilon | sym::f128_epsilon)
|
||||
|
||||
// values of the subtractions on the left hand side are of the type float
|
||||
&& let t_val_l = cx.typeck_results().expr_ty(val_l)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::is_in_test;
|
||||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||
use clippy_utils::{is_in_test, is_inside_always_const_context};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -99,7 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
|
|||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr) {
|
||||
if is_panic(cx, macro_call.def_id) {
|
||||
if cx.tcx.hir_is_inside_const_context(expr.hir_id)
|
||||
if is_inside_always_const_context(cx.tcx, expr.hir_id)
|
||||
|| self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id)
|
||||
{
|
||||
return;
|
||||
|
|
@ -140,7 +140,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
|
|||
&& let Res::Def(DefKind::Fn, def_id) = expr_path.res
|
||||
&& cx.tcx.is_diagnostic_item(sym::panic_any, def_id)
|
||||
{
|
||||
if cx.tcx.hir_is_inside_const_context(expr.hir_id)
|
||||
if is_inside_always_const_context(cx.tcx, expr.hir_id)
|
||||
|| self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id)
|
||||
{
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
|
|||
use clippy_utils::fn_has_unsatisfiable_preds;
|
||||
use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage};
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth};
|
||||
use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, walk_ptrs_ty_depth};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Body, FnDecl, LangItem, def_id};
|
||||
|
|
@ -96,14 +96,13 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
|
|||
let (fn_def_id, arg, arg_ty, clone_ret) =
|
||||
unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind));
|
||||
|
||||
let from_borrow = cx.tcx.lang_items().get(LangItem::CloneFn) == Some(fn_def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::to_owned_method, fn_def_id)
|
||||
|| (cx.tcx.is_diagnostic_item(sym::to_string_method, fn_def_id)
|
||||
&& is_type_lang_item(cx, arg_ty, LangItem::String));
|
||||
let fn_name = cx.tcx.get_diagnostic_name(fn_def_id);
|
||||
|
||||
let from_deref = !from_borrow
|
||||
&& (cx.tcx.is_diagnostic_item(sym::path_to_pathbuf, fn_def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::os_str_to_os_string, fn_def_id));
|
||||
let from_borrow = cx.tcx.lang_items().get(LangItem::CloneFn) == Some(fn_def_id)
|
||||
|| fn_name == Some(sym::to_owned_method)
|
||||
|| (fn_name == Some(sym::to_string_method) && is_type_lang_item(cx, arg_ty, LangItem::String));
|
||||
|
||||
let from_deref = !from_borrow && matches!(fn_name, Some(sym::path_to_pathbuf | sym::os_str_to_os_string));
|
||||
|
||||
if !from_borrow && !from_deref {
|
||||
continue;
|
||||
|
|
@ -148,8 +147,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
|
|||
is_call_with_ref_arg(cx, mir, &pred_terminator.kind)
|
||||
&& res == cloned
|
||||
&& cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id)
|
||||
&& (is_type_diagnostic_item(cx, pred_arg_ty, sym::PathBuf)
|
||||
|| is_type_diagnostic_item(cx, pred_arg_ty, sym::OsString))
|
||||
&& let ty::Adt(pred_arg_def, _) = pred_arg_ty.kind()
|
||||
&& let Some(pred_arg_name) = cx.tcx.get_diagnostic_name(pred_arg_def.did())
|
||||
&& matches!(pred_arg_name, sym::PathBuf | sym::OsString)
|
||||
{
|
||||
(pred_arg, res)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -155,6 +155,11 @@ impl LateLintPass<'_> for SemicolonBlock {
|
|||
kind: ExprKind::Block(block, _),
|
||||
..
|
||||
}) if !block.span.from_expansion() => {
|
||||
let attrs = cx.tcx.hir_attrs(stmt.hir_id);
|
||||
if !attrs.is_empty() && !cx.tcx.features().stmt_expr_attributes() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(tail) = block.expr {
|
||||
self.semicolon_inside_block(cx, block, tail, stmt.span);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,6 +126,8 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
|
|||
&& !is_from_proc_macro(cx, &first_segment.ident)
|
||||
&& !matches!(def_kind, DefKind::Macro(_))
|
||||
&& let Some(last_segment) = path.segments.last()
|
||||
&& let Res::Def(DefKind::Mod, crate_def_id) = first_segment.res
|
||||
&& crate_def_id.is_crate_root()
|
||||
{
|
||||
let (lint, used_mod, replace_with) = match first_segment.ident.name {
|
||||
sym::std => match cx.tcx.crate_name(def_id.krate) {
|
||||
|
|
|
|||
|
|
@ -457,7 +457,8 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace {
|
|||
}
|
||||
|
||||
fn is_one_of_trim_diagnostic_items(cx: &LateContext<'_>, trim_def_id: DefId) -> bool {
|
||||
cx.tcx.is_diagnostic_item(sym::str_trim, trim_def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::str_trim_start, trim_def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::str_trim_end, trim_def_id)
|
||||
matches!(
|
||||
cx.tcx.get_diagnostic_name(trim_def_id),
|
||||
Some(sym::str_trim | sym::str_trim_start | sym::str_trim_end)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{HasSession, SpanRangeExt as _};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{GenericArg, HirId, LetStmt, Node, Path, TyKind};
|
||||
use rustc_hir::{Expr, GenericArg, HirId, LetStmt, Node, Path, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS;
|
||||
|
||||
|
|
@ -38,6 +42,7 @@ fn is_function_block(cx: &LateContext<'_>, expr_hir_id: HirId) -> bool {
|
|||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
path: &Path<'tcx>,
|
||||
arg: &Expr<'tcx>,
|
||||
from_ty: Ty<'tcx>,
|
||||
to_ty: Ty<'tcx>,
|
||||
expr_hir_id: HirId,
|
||||
|
|
@ -68,14 +73,48 @@ pub(super) fn check<'tcx>(
|
|||
} else if is_function_block(cx, expr_hir_id) {
|
||||
return false;
|
||||
}
|
||||
span_lint_and_sugg(
|
||||
let span = last.ident.span.with_hi(path.span.hi());
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MISSING_TRANSMUTE_ANNOTATIONS,
|
||||
last.ident.span.with_hi(path.span.hi()),
|
||||
span,
|
||||
"transmute used without annotations",
|
||||
"consider adding missing annotations",
|
||||
format!("{}::<{from_ty}, {to_ty}>", last.ident),
|
||||
Applicability::MaybeIncorrect,
|
||||
|diag| {
|
||||
let from_ty_no_name = ty_cannot_be_named(from_ty);
|
||||
let to_ty_no_name = ty_cannot_be_named(to_ty);
|
||||
if from_ty_no_name || to_ty_no_name {
|
||||
let to_name = match (from_ty_no_name, to_ty_no_name) {
|
||||
(true, false) => maybe_name_by_expr(cx, arg.span, "the origin type"),
|
||||
(false, true) => "the destination type".into(),
|
||||
_ => "the source and destination types".into(),
|
||||
};
|
||||
diag.help(format!(
|
||||
"consider giving {to_name} a name, and adding missing type annotations"
|
||||
));
|
||||
} else {
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"consider adding missing annotations",
|
||||
format!("{}::<{from_ty}, {to_ty}>", last.ident),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
true
|
||||
}
|
||||
|
||||
fn ty_cannot_be_named(ty: Ty<'_>) -> bool {
|
||||
matches!(
|
||||
ty.kind(),
|
||||
ty::Alias(ty::AliasTyKind::Opaque | ty::AliasTyKind::Inherent, _)
|
||||
)
|
||||
}
|
||||
|
||||
fn maybe_name_by_expr<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> {
|
||||
span.with_source_text(sess, |name| {
|
||||
(name.len() + 9 < default.len()).then_some(format!("`{name}`'s type").into())
|
||||
})
|
||||
.flatten()
|
||||
.unwrap_or(default.into())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -520,7 +520,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
|
|||
| transmuting_null::check(cx, e, arg, to_ty)
|
||||
| transmute_null_to_fn::check(cx, e, arg, to_ty)
|
||||
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
|
||||
| missing_transmute_annotations::check(cx, path, from_ty, to_ty, e.hir_id)
|
||||
| missing_transmute_annotations::check(cx, path, arg, from_ty, to_ty, e.hir_id)
|
||||
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||
| transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, self.msrv)
|
||||
| transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use super::TRANSMUTE_INT_TO_NON_ZERO;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::sugg;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -16,35 +16,24 @@ pub(super) fn check<'tcx>(
|
|||
to_ty: Ty<'tcx>,
|
||||
arg: &'tcx Expr<'_>,
|
||||
) -> bool {
|
||||
let tcx = cx.tcx;
|
||||
|
||||
let (ty::Int(_) | ty::Uint(_), ty::Adt(adt, substs)) = (&from_ty.kind(), to_ty.kind()) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) {
|
||||
return false;
|
||||
if let ty::Int(_) | ty::Uint(_) = from_ty.kind()
|
||||
&& let ty::Adt(adt, substs) = to_ty.kind()
|
||||
&& cx.tcx.is_diagnostic_item(sym::NonZero, adt.did())
|
||||
&& let int_ty = substs.type_at(0)
|
||||
&& from_ty == int_ty
|
||||
{
|
||||
let arg = Sugg::hir(cx, arg, "..");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
TRANSMUTE_INT_TO_NON_ZERO,
|
||||
e.span,
|
||||
format!("transmute from a `{from_ty}` to a `{}<{int_ty}>`", sym::NonZero),
|
||||
"consider using",
|
||||
format!("{}::{}({arg})", sym::NonZero, sym::new_unchecked),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
let int_ty = substs.type_at(0);
|
||||
if from_ty != int_ty {
|
||||
return false;
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_INT_TO_NON_ZERO,
|
||||
e.span,
|
||||
format!("transmute from a `{from_ty}` to a `{}<{int_ty}>`", sym::NonZero),
|
||||
|diag| {
|
||||
let arg = sugg::Sugg::hir(cx, arg, "..");
|
||||
diag.span_suggestion(
|
||||
e.span,
|
||||
"consider using",
|
||||
format!("{}::{}({arg})", sym::NonZero, sym::new_unchecked),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
},
|
||||
);
|
||||
true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ use super::RC_BUFFER;
|
|||
|
||||
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
|
||||
let app = Applicability::Unspecified;
|
||||
if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
|
||||
let name = cx.tcx.get_diagnostic_name(def_id);
|
||||
if name == Some(sym::Rc) {
|
||||
if let Some(alternate) = match_buffer_type(cx, qpath) {
|
||||
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
|
||||
span_lint_and_then(
|
||||
|
|
@ -56,7 +57,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
|
|||
);
|
||||
return true;
|
||||
}
|
||||
} else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
|
||||
} else if name == Some(sym::Arc) {
|
||||
if let Some(alternate) = match_buffer_type(cx, qpath) {
|
||||
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
|
||||
span_lint_and_then(
|
||||
|
|
|
|||
|
|
@ -13,14 +13,11 @@ use super::{REDUNDANT_ALLOCATION, utils};
|
|||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: &QPath<'tcx>, def_id: DefId) -> bool {
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() {
|
||||
"Box"
|
||||
} else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
|
||||
"Rc"
|
||||
} else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
|
||||
"Arc"
|
||||
} else {
|
||||
return false;
|
||||
let outer_sym = match cx.tcx.get_diagnostic_name(def_id) {
|
||||
_ if Some(def_id) == cx.tcx.lang_items().owned_box() => "Box",
|
||||
Some(sym::Rc) => "Rc",
|
||||
Some(sym::Arc) => "Arc",
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
|
||||
use clippy_utils::sym;
|
||||
use clippy_utils::{is_unit_expr, sym};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
|
|
@ -16,10 +16,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
sym::assert_ne_macro | sym::debug_assert_ne_macro => "fail",
|
||||
_ => return,
|
||||
};
|
||||
let Some((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else {
|
||||
let Some((lhs, rhs, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else {
|
||||
return;
|
||||
};
|
||||
if !cx.typeck_results().expr_ty(left).is_unit() {
|
||||
if is_unit_expr(lhs) || is_unit_expr(rhs) {
|
||||
return;
|
||||
}
|
||||
if !cx.typeck_results().expr_ty(lhs).is_unit() {
|
||||
return;
|
||||
}
|
||||
span_lint(
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings {
|
|||
&& let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind()
|
||||
&& inner_str.is_str()
|
||||
{
|
||||
if cx.tcx.is_diagnostic_item(sym::string_new, fun_def_id) {
|
||||
let fun_name = cx.tcx.get_diagnostic_name(fun_def_id);
|
||||
if fun_name == Some(sym::string_new) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_OWNED_EMPTY_STRINGS,
|
||||
|
|
@ -51,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings {
|
|||
"\"\"".to_owned(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if cx.tcx.is_diagnostic_item(sym::from_fn, fun_def_id)
|
||||
} else if fun_name == Some(sym::from_fn)
|
||||
&& let [arg] = args
|
||||
&& let ExprKind::Lit(spanned) = &arg.kind
|
||||
&& let LitKind::Str(symbol, _) = spanned.node
|
||||
|
|
|
|||
|
|
@ -107,12 +107,10 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
|
|||
// Get the wrapper and inner types, if can't, abort.
|
||||
let (return_type_label, lang_item, inner_type) =
|
||||
if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id.expect_owner()).kind() {
|
||||
if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()) {
|
||||
("Option", OptionSome, subst.type_at(0))
|
||||
} else if cx.tcx.is_diagnostic_item(sym::Result, adt_def.did()) {
|
||||
("Result", ResultOk, subst.type_at(0))
|
||||
} else {
|
||||
return;
|
||||
match cx.tcx.get_diagnostic_name(adt_def.did()) {
|
||||
Some(sym::Option) => ("Option", OptionSome, subst.type_at(0)),
|
||||
Some(sym::Result) => ("Result", ResultOk, subst.type_at(0)),
|
||||
_ => return,
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -326,7 +326,11 @@ fn extend_with_struct_pat(
|
|||
if idx_1 == idx {
|
||||
// In the case of `k`, we merely require identical field names
|
||||
// so that we will transform into `ident_k: p1_k | p2_k`.
|
||||
let pos = fps2.iter().position(|fp2| eq_id(fp1.ident, fp2.ident));
|
||||
let pos = fps2.iter().position(|fp2| {
|
||||
// Avoid `Foo { bar } | Foo { bar }` => `Foo { bar | bar }`
|
||||
!(fp1.is_shorthand && fp2.is_shorthand)
|
||||
&& eq_id(fp1.ident, fp2.ident)
|
||||
});
|
||||
pos_in_2.set(pos);
|
||||
pos.is_some()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -89,7 +89,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
|
|||
{
|
||||
// We don't want to lint inside io::Read or io::Write implementations, as the author has more
|
||||
// information about their trait implementation than our lint, see https://github.com/rust-lang/rust-clippy/issues/4836
|
||||
if cx.tcx.is_diagnostic_item(sym::IoRead, trait_id) || cx.tcx.is_diagnostic_item(sym::IoWrite, trait_id) {
|
||||
if let Some(trait_name) = cx.tcx.get_diagnostic_name(trait_id)
|
||||
&& matches!(trait_name, sym::IoRead | sym::IoWrite)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
AssocItemConstraintKind, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArgsParentheses, Node, PolyTraitRef, Term,
|
||||
Ty, TyKind,
|
||||
AssocItemConstraintKind, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArgsParentheses, PolyTraitRef, Term, Ty,
|
||||
TyKind,
|
||||
};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::{BytePos, Span, sym};
|
||||
use rustc_span::{BytePos, Pos as _, Span, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -49,19 +49,22 @@ impl<'tcx> LateLintPass<'tcx> for UnusedUnit {
|
|||
decl: &'tcx FnDecl<'tcx>,
|
||||
body: &'tcx Body<'tcx>,
|
||||
span: Span,
|
||||
def_id: LocalDefId,
|
||||
_def_id: LocalDefId,
|
||||
) {
|
||||
if let FnRetTy::Return(hir_ty) = decl.output
|
||||
&& is_unit_ty(hir_ty)
|
||||
&& !hir_ty.span.from_expansion()
|
||||
&& get_def(span) == get_def(hir_ty.span)
|
||||
{
|
||||
// implicit types in closure signatures are forbidden when `for<...>` is present
|
||||
if let FnKind::Closure = kind
|
||||
&& let Node::Expr(expr) = cx.tcx.hir_node_by_def_id(def_id)
|
||||
&& let ExprKind::Closure(closure) = expr.kind
|
||||
&& !closure.bound_generic_params.is_empty()
|
||||
{
|
||||
// The explicit `-> ()` in the closure signature might be necessary for multiple reasons:
|
||||
// - Implicit types in closure signatures are forbidden when `for<...>` is present
|
||||
// - If the closure body ends with a function call, and that function's return type is generic, the
|
||||
// `-> ()` could be required for it to be inferred
|
||||
//
|
||||
// There could be more reasons to have it, and, in general, we shouldn't discourage the users from
|
||||
// writing more type annotations than strictly necessary, because it can help readability and
|
||||
// maintainability
|
||||
if let FnKind::Closure = kind {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -97,16 +100,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedUnit {
|
|||
}
|
||||
|
||||
fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>) {
|
||||
let segments = &poly.trait_ref.path.segments;
|
||||
|
||||
if segments.len() == 1
|
||||
&& matches!(segments[0].ident.name, sym::Fn | sym::FnMut | sym::FnOnce)
|
||||
&& let Some(args) = segments[0].args
|
||||
if let [segment] = &poly.trait_ref.path.segments
|
||||
&& matches!(segment.ident.name, sym::Fn | sym::FnMut | sym::FnOnce)
|
||||
&& let Some(args) = segment.args
|
||||
&& args.parenthesized == GenericArgsParentheses::ParenSugar
|
||||
&& let constraints = &args.constraints
|
||||
&& constraints.len() == 1
|
||||
&& constraints[0].ident.name == sym::Output
|
||||
&& let AssocItemConstraintKind::Equality { term: Term::Ty(hir_ty) } = constraints[0].kind
|
||||
&& let [constraint] = &args.constraints
|
||||
&& constraint.ident.name == sym::Output
|
||||
&& let AssocItemConstraintKind::Equality { term: Term::Ty(hir_ty) } = constraint.kind
|
||||
&& args.span_ext.hi() != poly.span.hi()
|
||||
&& !hir_ty.span.from_expansion()
|
||||
&& args.span_ext.hi() != hir_ty.span.hi()
|
||||
|
|
@ -160,17 +160,15 @@ fn get_def(span: Span) -> Option<Span> {
|
|||
|
||||
fn lint_unneeded_unit_return(cx: &LateContext<'_>, ty_span: Span, span: Span) {
|
||||
let (ret_span, appl) =
|
||||
span.with_hi(ty_span.hi())
|
||||
.get_source_text(cx)
|
||||
.map_or((ty_span, Applicability::MaybeIncorrect), |src| {
|
||||
position_before_rarrow(&src).map_or((ty_span, Applicability::MaybeIncorrect), |rpos| {
|
||||
(
|
||||
#[expect(clippy::cast_possible_truncation)]
|
||||
ty_span.with_lo(BytePos(span.lo().0 + rpos as u32)),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
})
|
||||
});
|
||||
if let Some(Some(rpos)) = span.with_hi(ty_span.hi()).with_source_text(cx, position_before_rarrow) {
|
||||
(
|
||||
ty_span.with_lo(span.lo() + BytePos::from_usize(rpos)),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
} else {
|
||||
(ty_span, Applicability::MaybeIncorrect)
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNUSED_UNIT,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::{method_chain_args, return_ty};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::ImplItemKind;
|
||||
use clippy_utils::{return_ty, sym};
|
||||
use rustc_hir::{
|
||||
Body, BodyOwnerKind, Expr, ExprKind, FnSig, ImplItem, ImplItemKind, Item, ItemKind, OwnerId, PathSegment, QPath,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{Span, sym};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{Ident, Span, Symbol};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -57,62 +56,165 @@ declare_clippy_lint! {
|
|||
"functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]);
|
||||
impl_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for UnwrapInResult {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
|
||||
if let ImplItemKind::Fn(ref _signature, _) = impl_item.kind
|
||||
// first check if it's a method or function
|
||||
// checking if its return type is `result` or `option`
|
||||
&& (is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Result)
|
||||
|| is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Option))
|
||||
{
|
||||
lint_impl_body(cx, impl_item.span, impl_item);
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
enum OptionOrResult {
|
||||
Option,
|
||||
Result,
|
||||
}
|
||||
|
||||
impl OptionOrResult {
|
||||
fn with_article(self) -> &'static str {
|
||||
match self {
|
||||
Self::Option => "an `Option`",
|
||||
Self::Result => "a `Result`",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) {
|
||||
if let ImplItemKind::Fn(_, body_id) = impl_item.kind {
|
||||
let body = cx.tcx.hir_body(body_id);
|
||||
let typeck = cx.tcx.typeck(impl_item.owner_id.def_id);
|
||||
let mut result = Vec::new();
|
||||
let _: Option<!> = for_each_expr(cx, body.value, |e| {
|
||||
// check for `expect`
|
||||
if let Some(arglists) = method_chain_args(e, &[sym::expect]) {
|
||||
let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs();
|
||||
if is_type_diagnostic_item(cx, receiver_ty, sym::Option)
|
||||
|| is_type_diagnostic_item(cx, receiver_ty, sym::Result)
|
||||
{
|
||||
result.push(e.span);
|
||||
}
|
||||
}
|
||||
struct OptionOrResultFn {
|
||||
kind: OptionOrResult,
|
||||
return_ty_span: Option<Span>,
|
||||
}
|
||||
|
||||
// check for `unwrap`
|
||||
if let Some(arglists) = method_chain_args(e, &[sym::unwrap]) {
|
||||
let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs();
|
||||
if is_type_diagnostic_item(cx, receiver_ty, sym::Option)
|
||||
|| is_type_diagnostic_item(cx, receiver_ty, sym::Result)
|
||||
{
|
||||
result.push(e.span);
|
||||
}
|
||||
}
|
||||
#[derive(Default)]
|
||||
pub struct UnwrapInResult {
|
||||
fn_stack: Vec<Option<OptionOrResultFn>>,
|
||||
current_fn: Option<OptionOrResultFn>,
|
||||
}
|
||||
|
||||
ControlFlow::Continue(())
|
||||
impl UnwrapInResult {
|
||||
fn enter_item(&mut self, cx: &LateContext<'_>, fn_def_id: OwnerId, sig: &FnSig<'_>) {
|
||||
self.fn_stack.push(self.current_fn.take());
|
||||
self.current_fn = is_option_or_result(cx, return_ty(cx, fn_def_id)).map(|kind| OptionOrResultFn {
|
||||
kind,
|
||||
return_ty_span: Some(sig.decl.output.span()),
|
||||
});
|
||||
}
|
||||
|
||||
// if we've found one, lint
|
||||
if !result.is_empty() {
|
||||
fn leave_item(&mut self) {
|
||||
self.current_fn = self.fn_stack.pop().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for UnwrapInResult {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
|
||||
if let ImplItemKind::Fn(sig, _) = &impl_item.kind {
|
||||
self.enter_item(cx, impl_item.owner_id, sig);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_impl_item_post(&mut self, _: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) {
|
||||
if let ImplItemKind::Fn(..) = impl_item.kind {
|
||||
self.leave_item();
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
if let ItemKind::Fn {
|
||||
has_body: true, sig, ..
|
||||
} = &item.kind
|
||||
{
|
||||
self.enter_item(cx, item.owner_id, sig);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item_post(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
if let ItemKind::Fn { has_body: true, .. } = item.kind {
|
||||
self.leave_item();
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(OptionOrResultFn {
|
||||
kind,
|
||||
ref mut return_ty_span,
|
||||
}) = self.current_fn
|
||||
&& let Some((oor, fn_name)) = is_unwrap_or_expect_call(cx, expr)
|
||||
&& oor == kind
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
UNWRAP_IN_RESULT,
|
||||
impl_span,
|
||||
"used unwrap or expect in a function that returns result or option",
|
||||
move |diag| {
|
||||
diag.help("unwrap and expect should not be used in a function that returns result or option");
|
||||
diag.span_note(result, "potential non-recoverable error(s)");
|
||||
expr.span,
|
||||
format!("`{fn_name}` used in a function that returns {}", kind.with_article()),
|
||||
|diag| {
|
||||
// Issue the note and help only once per function
|
||||
if let Some(span) = return_ty_span.take() {
|
||||
diag.span_note(span, "in this function signature");
|
||||
let complement = if kind == OptionOrResult::Result {
|
||||
" or calling the `.map_err()` method"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
diag.help(format!("consider using the `?` operator{complement}"));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) {
|
||||
let body_def_id = cx.tcx.hir_body_owner_def_id(body.id());
|
||||
if !matches!(cx.tcx.hir_body_owner_kind(body_def_id), BodyOwnerKind::Fn) {
|
||||
// When entering a body which is not a function, mask the potential surrounding
|
||||
// function to not apply the lint.
|
||||
self.fn_stack.push(self.current_fn.take());
|
||||
}
|
||||
}
|
||||
|
||||
fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) {
|
||||
let body_def_id = cx.tcx.hir_body_owner_def_id(body.id());
|
||||
if !matches!(cx.tcx.hir_body_owner_kind(body_def_id), BodyOwnerKind::Fn) {
|
||||
// Unmask the potential surrounding function.
|
||||
self.current_fn = self.fn_stack.pop().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_option_or_result(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<OptionOrResult> {
|
||||
match ty.ty_adt_def().and_then(|def| cx.tcx.get_diagnostic_name(def.did())) {
|
||||
Some(sym::Option) => Some(OptionOrResult::Option),
|
||||
Some(sym::Result) => Some(OptionOrResult::Result),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_unwrap_or_expect_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(OptionOrResult, Symbol)> {
|
||||
if let ExprKind::Call(func, _) = expr.kind
|
||||
&& let ExprKind::Path(QPath::TypeRelative(
|
||||
hir_ty,
|
||||
PathSegment {
|
||||
ident:
|
||||
Ident {
|
||||
name: name @ (sym::unwrap | sym::expect),
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
)) = func.kind
|
||||
{
|
||||
is_option_or_result(cx, cx.typeck_results().node_type(hir_ty.hir_id)).map(|oor| (oor, *name))
|
||||
} else if let ExprKind::MethodCall(
|
||||
PathSegment {
|
||||
ident: Ident {
|
||||
name: name @ (sym::unwrap | sym::expect),
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
recv,
|
||||
_,
|
||||
_,
|
||||
) = expr.kind
|
||||
{
|
||||
is_option_or_result(cx, cx.typeck_results().expr_ty_adjusted(recv)).map(|oor| (oor, *name))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -386,12 +386,13 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
|||
|
||||
ExprKind::Call(path, [arg]) => {
|
||||
if let ExprKind::Path(ref qpath) = path.kind
|
||||
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
|
||||
&& !is_ty_alias(qpath)
|
||||
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
|
||||
&& let Some(name) = cx.tcx.get_diagnostic_name(def_id)
|
||||
{
|
||||
let a = cx.typeck_results().expr_ty(e);
|
||||
let b = cx.typeck_results().expr_ty(arg);
|
||||
if cx.tcx.is_diagnostic_item(sym::try_from_fn, def_id)
|
||||
if name == sym::try_from_fn
|
||||
&& is_type_diagnostic_item(cx, a, sym::Result)
|
||||
&& let ty::Adt(_, args) = a.kind()
|
||||
&& let Some(a_type) = args.types().next()
|
||||
|
|
@ -406,9 +407,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
|||
None,
|
||||
hint,
|
||||
);
|
||||
}
|
||||
|
||||
if cx.tcx.is_diagnostic_item(sym::from_fn, def_id) && same_type_and_consts(a, b) {
|
||||
} else if name == sym::from_fn && same_type_and_consts(a, b) {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "<expr>", &mut app).maybe_paren();
|
||||
let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::collections::btree_map::Entry;
|
||||
use std::mem;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use clippy_config::Conf;
|
||||
|
|
@ -20,15 +22,36 @@ use rustc_span::{DesugaringKind, Span};
|
|||
pub struct UselessVec {
|
||||
too_large_for_stack: u64,
|
||||
msrv: Msrv,
|
||||
span_to_lint_map: BTreeMap<Span, Option<(HirId, SuggestedType, String, Applicability)>>,
|
||||
/// Maps from a `vec![]` source callsite invocation span to the "state" (i.e., whether we can
|
||||
/// emit a warning there or not).
|
||||
///
|
||||
/// The purpose of this is to buffer lints up until `check_crate_post` so that we can cancel a
|
||||
/// lint while visiting, because a `vec![]` invocation span can appear multiple times when
|
||||
/// it is passed as a macro argument, once in a context that doesn't require a `Vec<_>` and
|
||||
/// another time that does. Consider:
|
||||
/// ```
|
||||
/// macro_rules! m {
|
||||
/// ($v:expr) => {
|
||||
/// let a = $v;
|
||||
/// $v.push(3);
|
||||
/// }
|
||||
/// }
|
||||
/// m!(vec![1, 2]);
|
||||
/// ```
|
||||
/// The macro invocation expands to two `vec![1, 2]` invocations. If we eagerly suggest changing
|
||||
/// the first `vec![1, 2]` (which is shared with the other expn) to an array which indeed would
|
||||
/// work, we get a false positive warning on the `$v.push(3)` which really requires `$v` to
|
||||
/// be a vector.
|
||||
span_to_state: BTreeMap<Span, VecState>,
|
||||
allow_in_test: bool,
|
||||
}
|
||||
|
||||
impl UselessVec {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
too_large_for_stack: conf.too_large_for_stack,
|
||||
msrv: conf.msrv,
|
||||
span_to_lint_map: BTreeMap::new(),
|
||||
span_to_state: BTreeMap::new(),
|
||||
allow_in_test: conf.allow_useless_vec_in_tests,
|
||||
}
|
||||
}
|
||||
|
|
@ -62,17 +85,28 @@ declare_clippy_lint! {
|
|||
|
||||
impl_lint_pass!(UselessVec => [USELESS_VEC]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for UselessVec {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) else {
|
||||
return;
|
||||
};
|
||||
if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
// the parent callsite of this `vec!` expression, or span to the borrowed one such as `&vec!`
|
||||
let callsite = expr.span.parent_callsite().unwrap_or(expr.span);
|
||||
/// The "state" of a `vec![]` invocation, indicating whether it can or cannot be changed.
|
||||
enum VecState {
|
||||
Change {
|
||||
suggest_ty: SuggestedType,
|
||||
vec_snippet: String,
|
||||
expr_hir_id: HirId,
|
||||
},
|
||||
NoChange,
|
||||
}
|
||||
|
||||
enum VecToArray {
|
||||
/// Expression does not need to be a `Vec<_>` and its type can be changed to an array (or
|
||||
/// slice).
|
||||
Possible,
|
||||
/// Expression must be a `Vec<_>`. Type cannot change.
|
||||
Impossible,
|
||||
}
|
||||
|
||||
impl UselessVec {
|
||||
/// Checks if the surrounding environment requires this expression to actually be of type
|
||||
/// `Vec<_>`, or if it can be changed to `&[]`/`[]` without causing type errors.
|
||||
fn expr_usage_requires_vec(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) -> VecToArray {
|
||||
match cx.tcx.parent_hir_node(expr.hir_id) {
|
||||
// search for `let foo = vec![_]` expressions where all uses of `foo`
|
||||
// adjust to slices or call a method that exist on slices (e.g. len)
|
||||
|
|
@ -100,107 +134,123 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
|
|||
.is_continue();
|
||||
|
||||
if only_slice_uses {
|
||||
self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, SuggestedType::Array);
|
||||
VecToArray::Possible
|
||||
} else {
|
||||
self.span_to_lint_map.insert(callsite, None);
|
||||
VecToArray::Impossible
|
||||
}
|
||||
},
|
||||
// if the local pattern has a specified type, do not lint.
|
||||
Node::LetStmt(LetStmt { ty: Some(_), .. }) if higher::VecArgs::hir(cx, expr).is_some() => {
|
||||
self.span_to_lint_map.insert(callsite, None);
|
||||
VecToArray::Impossible
|
||||
},
|
||||
// search for `for _ in vec![...]`
|
||||
Node::Expr(Expr { span, .. })
|
||||
if span.is_desugaring(DesugaringKind::ForLoop) && self.msrv.meets(cx, msrvs::ARRAY_INTO_ITERATOR) =>
|
||||
{
|
||||
let suggest_slice = suggest_type(expr);
|
||||
self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, suggest_slice);
|
||||
VecToArray::Possible
|
||||
},
|
||||
// search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]`
|
||||
_ => {
|
||||
let suggest_slice = suggest_type(expr);
|
||||
|
||||
if adjusts_to_slice(cx, expr) {
|
||||
self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, suggest_slice);
|
||||
VecToArray::Possible
|
||||
} else {
|
||||
self.span_to_lint_map.insert(callsite, None);
|
||||
VecToArray::Impossible
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
|
||||
for (span, lint_opt) in &self.span_to_lint_map {
|
||||
if let Some((hir_id, suggest_slice, snippet, applicability)) = lint_opt {
|
||||
let help_msg = format!("you can use {} directly", suggest_slice.desc());
|
||||
span_lint_hir_and_then(cx, USELESS_VEC, *hir_id, *span, "useless use of `vec!`", |diag| {
|
||||
// If the `vec!` macro contains comment, better not make the suggestion machine
|
||||
// applicable as it would remove them.
|
||||
let applicability = if *applicability != Applicability::Unspecified
|
||||
&& let source_map = cx.tcx.sess.source_map()
|
||||
&& span_contains_comment(source_map, *span)
|
||||
{
|
||||
Applicability::Unspecified
|
||||
} else {
|
||||
*applicability
|
||||
};
|
||||
diag.span_suggestion(*span, help_msg, snippet, applicability);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UselessVec {
|
||||
fn check_vec_macro<'tcx>(
|
||||
&mut self,
|
||||
cx: &LateContext<'tcx>,
|
||||
vec_args: &higher::VecArgs<'tcx>,
|
||||
span: Span,
|
||||
hir_id: HirId,
|
||||
suggest_slice: SuggestedType,
|
||||
) {
|
||||
if span.from_expansion() {
|
||||
return;
|
||||
impl<'tcx> LateLintPass<'tcx> for UselessVec {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows())
|
||||
// The `vec![]` or `&vec![]` invocation span.
|
||||
&& let vec_span = expr.span.parent_callsite().unwrap_or(expr.span)
|
||||
&& !vec_span.from_expansion()
|
||||
{
|
||||
if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
match self.expr_usage_requires_vec(cx, expr) {
|
||||
VecToArray::Possible => {
|
||||
let suggest_ty = suggest_type(expr);
|
||||
|
||||
// Size and `Copy` checks don't depend on the enclosing usage of the expression
|
||||
// and don't need to be inserted into the state map.
|
||||
let vec_snippet = match vec_args {
|
||||
higher::VecArgs::Repeat(expr, len) => {
|
||||
if is_copy(cx, cx.typeck_results().expr_ty(expr))
|
||||
&& let Some(Constant::Int(length)) = ConstEvalCtxt::new(cx).eval(len)
|
||||
&& let Ok(length) = u64::try_from(length)
|
||||
&& size_of(cx, expr)
|
||||
.checked_mul(length)
|
||||
.is_some_and(|size| size <= self.too_large_for_stack)
|
||||
{
|
||||
suggest_ty.snippet(
|
||||
cx,
|
||||
Some(expr.span.source_callsite()),
|
||||
Some(len.span.source_callsite()),
|
||||
)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
higher::VecArgs::Vec(args) => {
|
||||
if let Ok(length) = u64::try_from(args.len())
|
||||
&& size_of(cx, expr)
|
||||
.checked_mul(length)
|
||||
.is_some_and(|size| size <= self.too_large_for_stack)
|
||||
{
|
||||
suggest_ty.snippet(
|
||||
cx,
|
||||
args.first().zip(args.last()).map(|(first, last)| {
|
||||
first.span.source_callsite().to(last.span.source_callsite())
|
||||
}),
|
||||
None,
|
||||
)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if let Entry::Vacant(entry) = self.span_to_state.entry(vec_span) {
|
||||
entry.insert(VecState::Change {
|
||||
suggest_ty,
|
||||
vec_snippet,
|
||||
expr_hir_id: expr.hir_id,
|
||||
});
|
||||
}
|
||||
},
|
||||
VecToArray::Impossible => {
|
||||
self.span_to_state.insert(vec_span, VecState::NoChange);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let snippet = match *vec_args {
|
||||
higher::VecArgs::Repeat(elem, len) => {
|
||||
if let Some(Constant::Int(len_constant)) = ConstEvalCtxt::new(cx).eval(len) {
|
||||
// vec![ty; N] works when ty is Clone, [ty; N] requires it to be Copy also
|
||||
if !is_copy(cx, cx.typeck_results().expr_ty(elem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#[expect(clippy::cast_possible_truncation)]
|
||||
if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack {
|
||||
return;
|
||||
}
|
||||
|
||||
suggest_slice.snippet(cx, Some(elem.span), Some(len.span))
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
higher::VecArgs::Vec(args) => {
|
||||
let args_span = if let Some(last) = args.iter().last() {
|
||||
if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack {
|
||||
return;
|
||||
}
|
||||
Some(args[0].span.source_callsite().to(last.span.source_callsite()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
suggest_slice.snippet(cx, args_span, None)
|
||||
},
|
||||
};
|
||||
|
||||
self.span_to_lint_map.entry(span).or_insert(Some((
|
||||
hir_id,
|
||||
suggest_slice,
|
||||
snippet,
|
||||
Applicability::MachineApplicable,
|
||||
)));
|
||||
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
|
||||
for (span, state) in mem::take(&mut self.span_to_state) {
|
||||
if let VecState::Change {
|
||||
suggest_ty,
|
||||
vec_snippet,
|
||||
expr_hir_id,
|
||||
} = state
|
||||
{
|
||||
span_lint_hir_and_then(cx, USELESS_VEC, expr_hir_id, span, "useless use of `vec!`", |diag| {
|
||||
let help_msg = format!("you can use {} directly", suggest_ty.desc());
|
||||
// If the `vec!` macro contains comment, better not make the suggestion machine applicable as it
|
||||
// would remove them.
|
||||
let applicability = if span_contains_comment(cx.tcx.sess.source_map(), span) {
|
||||
Applicability::Unspecified
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
};
|
||||
diag.span_suggestion(span, help_msg, vec_snippet, applicability);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -221,11 +271,17 @@ impl SuggestedType {
|
|||
}
|
||||
|
||||
fn snippet(self, cx: &LateContext<'_>, args_span: Option<Span>, len_span: Option<Span>) -> String {
|
||||
// Invariant of the lint as implemented: all spans are from the root context (and as a result,
|
||||
// always trivially crate-local).
|
||||
assert!(args_span.is_none_or(|s| !s.from_expansion()));
|
||||
assert!(len_span.is_none_or(|s| !s.from_expansion()));
|
||||
|
||||
let maybe_args = args_span
|
||||
.and_then(|sp| sp.get_source_text(cx))
|
||||
.map(|sp| sp.get_source_text(cx).expect("spans are always crate-local"))
|
||||
.map_or(String::new(), |x| x.to_owned());
|
||||
let maybe_len = len_span
|
||||
.and_then(|sp| sp.get_source_text(cx).map(|s| format!("; {s}")))
|
||||
.map(|sp| sp.get_source_text(cx).expect("spans are always crate-local"))
|
||||
.map(|st| format!("; {st}"))
|
||||
.unwrap_or_default();
|
||||
|
||||
match self {
|
||||
|
|
|
|||
|
|
@ -537,7 +537,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
|
|||
|
||||
sug_span = Some(sug_span.unwrap_or(arg.expr.span).to(arg.expr.span));
|
||||
|
||||
if let Some((_, index)) = positional_arg_piece_span(piece) {
|
||||
if let Some((_, index)) = format_arg_piece_span(piece) {
|
||||
replaced_position.push(index);
|
||||
}
|
||||
|
||||
|
|
@ -569,16 +569,11 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Extract Span and its index from the given `piece`, if it's positional argument.
|
||||
fn positional_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> {
|
||||
/// Extract Span and its index from the given `piece`
|
||||
fn format_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> {
|
||||
match piece {
|
||||
FormatArgsPiece::Placeholder(FormatPlaceholder {
|
||||
argument:
|
||||
FormatArgPosition {
|
||||
index: Ok(index),
|
||||
kind: FormatArgPositionKind::Number,
|
||||
..
|
||||
},
|
||||
argument: FormatArgPosition { index: Ok(index), .. },
|
||||
span: Some(span),
|
||||
..
|
||||
}) => Some((*span, *index)),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use ControlFlow::{Break, Continue};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{fn_def_id, get_enclosing_block, path_to_local_id};
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_ast::visit::visit_opt;
|
||||
|
|
@ -58,8 +59,8 @@ declare_lint_pass!(ZombieProcesses => [ZOMBIE_PROCESSES]);
|
|||
impl<'tcx> LateLintPass<'tcx> for ZombieProcesses {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let ExprKind::Call(..) | ExprKind::MethodCall(..) = expr.kind
|
||||
&& let Some(child_adt) = cx.typeck_results().expr_ty(expr).ty_adt_def()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Child, child_adt.did())
|
||||
&& let child_ty = cx.typeck_results().expr_ty(expr)
|
||||
&& is_type_diagnostic_item(cx, child_ty, sym::Child)
|
||||
{
|
||||
match cx.tcx.parent_hir_node(expr.hir_id) {
|
||||
Node::LetStmt(local)
|
||||
|
|
@ -177,8 +178,8 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> {
|
|||
Node::Expr(expr) if let ExprKind::AddrOf(_, Mutability::Not, _) = expr.kind => {},
|
||||
Node::Expr(expr)
|
||||
if let Some(fn_did) = fn_def_id(self.cx, expr)
|
||||
&& (self.cx.tcx.is_diagnostic_item(sym::child_id, fn_did)
|
||||
|| self.cx.tcx.is_diagnostic_item(sym::child_kill, fn_did)) => {},
|
||||
&& let Some(fn_name) = self.cx.tcx.get_diagnostic_name(fn_did)
|
||||
&& matches!(fn_name, sym::child_id | sym::child_kill) => {},
|
||||
|
||||
// Conservatively assume that all other kinds of nodes call `.wait()` somehow.
|
||||
_ => return Break(MaybeWait(ex.span)),
|
||||
|
|
@ -351,9 +352,14 @@ fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, cause: Caus
|
|||
|
||||
/// Checks if the given expression exits the process.
|
||||
fn is_exit_expression(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
fn_def_id(cx, expr).is_some_and(|fn_did| {
|
||||
cx.tcx.is_diagnostic_item(sym::process_exit, fn_did) || cx.tcx.is_diagnostic_item(sym::process_abort, fn_did)
|
||||
})
|
||||
if let Some(fn_did) = fn_def_id(cx, expr)
|
||||
&& let Some(fn_name) = cx.tcx.get_diagnostic_name(fn_did)
|
||||
&& matches!(fn_name, sym::process_exit | sym::process_abort)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain:
|
|||
|
||||
<!-- begin autogenerated nightly -->
|
||||
```
|
||||
nightly-2025-08-22
|
||||
nightly-2025-09-04
|
||||
```
|
||||
<!-- end autogenerated nightly -->
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool {
|
|||
}
|
||||
|
||||
/// Checks if each element in the first slice is contained within the latter as per `eq_fn`.
|
||||
pub fn unordered_over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
|
||||
pub fn unordered_over<X, Y>(left: &[X], right: &[Y], mut eq_fn: impl FnMut(&X, &Y) -> bool) -> bool {
|
||||
left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r)))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -287,23 +287,22 @@ impl<'a> VecArgs<'a> {
|
|||
&& let ExprKind::Path(ref qpath) = fun.kind
|
||||
&& is_expn_of(fun.span, sym::vec).is_some()
|
||||
&& let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id()
|
||||
&& let Some(name) = cx.tcx.get_diagnostic_name(fun_def_id)
|
||||
{
|
||||
return if cx.tcx.is_diagnostic_item(sym::vec_from_elem, fun_def_id) && args.len() == 2 {
|
||||
// `vec![elem; size]` case
|
||||
Some(VecArgs::Repeat(&args[0], &args[1]))
|
||||
} else if cx.tcx.is_diagnostic_item(sym::slice_into_vec, fun_def_id) && args.len() == 1 {
|
||||
// `vec![a, b, c]` case
|
||||
if let ExprKind::Call(_, [arg]) = &args[0].kind
|
||||
&& let ExprKind::Array(args) = arg.kind
|
||||
return match (name, args) {
|
||||
(sym::vec_from_elem, [elem, size]) => {
|
||||
// `vec![elem; size]` case
|
||||
Some(VecArgs::Repeat(elem, size))
|
||||
},
|
||||
(sym::slice_into_vec, [slice])
|
||||
if let ExprKind::Call(_, [arg]) = slice.kind
|
||||
&& let ExprKind::Array(args) = arg.kind =>
|
||||
{
|
||||
// `vec![a, b, c]` case
|
||||
Some(VecArgs::Vec(args))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if cx.tcx.is_diagnostic_item(sym::vec_new, fun_def_id) && args.is_empty() {
|
||||
Some(VecArgs::Vec(&[]))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
(sym::vec_new, []) => Some(VecArgs::Vec(&[])),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -757,7 +757,7 @@ pub fn both_some_and<X, Y>(l: Option<X>, r: Option<Y>, mut pred: impl FnMut(X, Y
|
|||
}
|
||||
|
||||
/// Checks if two slices are equal as per `eq_fn`.
|
||||
pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
|
||||
pub fn over<X, Y>(left: &[X], right: &[Y], mut eq_fn: impl FnMut(&X, &Y) -> bool) -> bool {
|
||||
left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -102,9 +102,9 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet};
|
|||
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
|
||||
use rustc_hir::{
|
||||
self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring,
|
||||
CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl,
|
||||
ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode,
|
||||
Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
|
||||
CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs,
|
||||
HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId,
|
||||
OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
|
||||
TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr,
|
||||
};
|
||||
use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
|
||||
|
|
@ -126,6 +126,7 @@ use rustc_span::{InnerSpan, Span};
|
|||
use source::{SpanRangeExt, walk_span_to_context};
|
||||
use visitors::{Visitable, for_each_unconsumed_temporary};
|
||||
|
||||
use crate::ast_utils::unordered_over;
|
||||
use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
|
||||
use crate::higher::Range;
|
||||
use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
|
||||
|
|
@ -308,6 +309,7 @@ pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) ->
|
|||
cx.tcx.lang_items().get(item) == Some(did)
|
||||
}
|
||||
|
||||
/// Checks if `expr` is an empty block or an empty tuple.
|
||||
pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
|
||||
matches!(
|
||||
expr.kind,
|
||||
|
|
@ -640,9 +642,8 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<
|
|||
&& let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
|
||||
&& let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
|
||||
{
|
||||
return std_types_symbols.iter().any(|&symbol| {
|
||||
cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
|
||||
});
|
||||
return Some(adt.did()) == cx.tcx.lang_items().string()
|
||||
|| (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name));
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
@ -1992,7 +1993,7 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<
|
|||
(PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
|
||||
if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
|
||||
{
|
||||
zip(pats, tup).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
|
||||
over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir))
|
||||
},
|
||||
(PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
|
||||
zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
|
||||
|
|
@ -2004,7 +2005,7 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<
|
|||
if let ExprKind::Path(ident) = &ident.kind
|
||||
&& qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
|
||||
// check fields
|
||||
&& zip(field_pats, fields).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr,by_hir))
|
||||
&& over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir))
|
||||
{
|
||||
true
|
||||
} else {
|
||||
|
|
@ -2017,10 +2018,8 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<
|
|||
// check ident
|
||||
qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
|
||||
// check fields
|
||||
&& field_pats.iter().all(|field_pat| {
|
||||
fields.iter().any(|field| {
|
||||
field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
|
||||
})
|
||||
&& unordered_over(field_pats, fields, |field_pat, field| {
|
||||
field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
|
||||
})
|
||||
},
|
||||
_ => false,
|
||||
|
|
@ -2405,6 +2404,24 @@ pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>
|
|||
expr
|
||||
}
|
||||
|
||||
/// Returns a `Vec` of `Expr`s containing `AddrOf` operators (`&`) or deref operators (`*`) of a
|
||||
/// given expression.
|
||||
pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> {
|
||||
let mut operators = Vec::new();
|
||||
peel_hir_expr_while(expr, |expr| match expr.kind {
|
||||
ExprKind::AddrOf(_, _, e) => {
|
||||
operators.push(expr);
|
||||
Some(e)
|
||||
},
|
||||
ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => {
|
||||
operators.push(expr);
|
||||
Some(e)
|
||||
},
|
||||
_ => None,
|
||||
});
|
||||
operators
|
||||
}
|
||||
|
||||
pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
|
||||
&& let Res::Def(_, def_id) = path.res
|
||||
|
|
@ -3633,3 +3650,17 @@ pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>)
|
|||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks if the expression is an async block (i.e., `async { ... }`).
|
||||
pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
|
||||
matches!(
|
||||
expr.kind,
|
||||
ExprKind::Closure(Closure {
|
||||
kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
|
||||
CoroutineDesugaring::Async,
|
||||
CoroutineSource::Block
|
||||
)),
|
||||
..
|
||||
})
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -387,10 +387,7 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
|
|||
/// Checks if the type is a reference equals to a diagnostic item
|
||||
pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
|
||||
match ty.kind() {
|
||||
ty::Ref(_, ref_ty, _) => match ref_ty.kind() {
|
||||
ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()),
|
||||
_ => false,
|
||||
},
|
||||
ty::Ref(_, ref_ty, _) => is_type_diagnostic_item(cx, *ref_ty, diag_item),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -1378,9 +1375,7 @@ pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'
|
|||
|
||||
/// Check if `ty` is slice-like, i.e., `&[T]`, `[T; N]`, or `Vec<T>`.
|
||||
pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
ty.is_slice()
|
||||
|| ty.is_array()
|
||||
|| matches!(ty.kind(), ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::Vec, adt_def.did()))
|
||||
ty.is_slice() || ty.is_array() || is_type_diagnostic_item(cx, ty, sym::Vec)
|
||||
}
|
||||
|
||||
pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option<usize> {
|
||||
|
|
|
|||
|
|
@ -144,11 +144,9 @@ impl<'tcx> Visitor<'tcx> for BindingUsageFinder<'_, 'tcx> {
|
|||
|
||||
/// Checks if the given expression is a macro call to `todo!()` or `unimplemented!()`.
|
||||
pub fn is_todo_unimplemented_macro(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
root_macro_call_first_node(cx, expr).is_some_and(|macro_call| {
|
||||
[sym::todo_macro, sym::unimplemented_macro]
|
||||
.iter()
|
||||
.any(|&sym| cx.tcx.is_diagnostic_item(sym, macro_call.def_id))
|
||||
})
|
||||
root_macro_call_first_node(cx, expr)
|
||||
.and_then(|macro_call| cx.tcx.get_diagnostic_name(macro_call.def_id))
|
||||
.is_some_and(|macro_name| matches!(macro_name, sym::todo_macro | sym::unimplemented_macro))
|
||||
}
|
||||
|
||||
/// Checks if the given expression is a stub, i.e., a `todo!()` or `unimplemented!()` expression,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[toolchain]
|
||||
# begin autogenerated nightly
|
||||
channel = "nightly-2025-08-22"
|
||||
channel = "nightly-2025-09-04"
|
||||
# end autogenerated nightly
|
||||
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||
profile = "minimal"
|
||||
|
|
|
|||
|
|
@ -330,7 +330,8 @@ pub fn main() {
|
|||
|
||||
// Do not run Clippy for Cargo's info queries so that invalid CLIPPY_ARGS are not cached
|
||||
// https://github.com/rust-lang/cargo/issues/14385
|
||||
let info_query = has_arg(&orig_args, "-vV") || has_arg(&orig_args, "--print");
|
||||
let info_query = has_arg(&orig_args, "-vV")
|
||||
|| arg_value(&orig_args, "--print", |val| val != "crate-root-lint-levels").is_some();
|
||||
|
||||
let clippy_enabled = !cap_lints_allow && relevant_package && !info_query;
|
||||
if clippy_enabled {
|
||||
|
|
|
|||
30
tests/config-consistency.rs
Normal file
30
tests/config-consistency.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#![feature(rustc_private)]
|
||||
|
||||
// This test checks that all lints defined in `clippy_config::conf` in `#[lints]`
|
||||
// attributes exist as Clippy lints.
|
||||
//
|
||||
// This test is a no-op if run as part of the compiler test suite
|
||||
// and will always succeed.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[test]
|
||||
fn config_consistency() {
|
||||
if option_env!("RUSTC_TEST_SUITE").is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let lint_names: HashSet<String> = clippy_lints::declared_lints::LINTS
|
||||
.iter()
|
||||
.map(|lint_info| lint_info.lint.name.strip_prefix("clippy::").unwrap().to_lowercase())
|
||||
.collect();
|
||||
for conf in clippy_config::get_configuration_metadata() {
|
||||
for lint in conf.lints {
|
||||
assert!(
|
||||
lint_names.contains(*lint),
|
||||
"Configuration option {} references lint `{lint}` which does not exist",
|
||||
conf.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
tests/no-profile-in-cargo-toml.rs
Normal file
34
tests/no-profile-in-cargo-toml.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// Check that we do not have `profile.*` sections in our `Cargo.toml` files,
|
||||
// as this causes warnings when run from the compiler repository which includes
|
||||
// Clippy in a workspace.
|
||||
//
|
||||
// Those sections can be put into `.cargo/config.toml` which will be read
|
||||
// when commands are issued from the top-level Clippy directory, outside of
|
||||
// a workspace.
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufRead as _};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
#[test]
|
||||
fn no_profile_in_cargo_toml() {
|
||||
// This check could parse `Cargo.toml` using a TOML deserializer, but in practice
|
||||
// profile sections would be added at the beginning of a line as `[profile.*]`, so
|
||||
// keep it fast and simple.
|
||||
for entry in WalkDir::new(".")
|
||||
.into_iter()
|
||||
.filter_map(Result::ok)
|
||||
.filter(|e| e.file_name().to_str() == Some("Cargo.toml"))
|
||||
{
|
||||
for line in io::BufReader::new(File::open(entry.path()).unwrap())
|
||||
.lines()
|
||||
.map(Result::unwrap)
|
||||
{
|
||||
if line.starts_with("[profile.") {
|
||||
eprintln!("Profile section `{line}` found in file `{}`.", entry.path().display());
|
||||
eprintln!("Use `.cargo/config.toml` for profiles specific to the standalone Clippy repository.");
|
||||
panic!("Profile section found in `Cargo.toml`");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ error: use of a disallowed method `rustc_lint::context::LintContext::span_lint`
|
|||
LL | cx.span_lint(lint, span, |lint| {
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead
|
||||
= note: this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead
|
||||
note: the lint level is defined here
|
||||
--> tests/ui-internal/disallow_span_lint.rs:2:9
|
||||
|
|
||||
|
|
@ -17,7 +17,7 @@ error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_
|
|||
LL | tcx.node_span_lint(lint, hir_id, span, |lint| {
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead
|
||||
= note: this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
|
|||
1
tests/ui-toml/excessive_precision/clippy.toml
Normal file
1
tests/ui-toml/excessive_precision/clippy.toml
Normal file
|
|
@ -0,0 +1 @@
|
|||
const-literal-digits-threshold = 20
|
||||
38
tests/ui-toml/excessive_precision/excessive_precision.fixed
Normal file
38
tests/ui-toml/excessive_precision/excessive_precision.fixed
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#![warn(clippy::excessive_precision)]
|
||||
#![allow(
|
||||
dead_code,
|
||||
overflowing_literals,
|
||||
unused_variables,
|
||||
clippy::print_literal,
|
||||
clippy::useless_vec
|
||||
)]
|
||||
|
||||
fn main() {
|
||||
// Overly specified constants
|
||||
let _: f32 = 1.012_345_7;
|
||||
//~^ excessive_precision
|
||||
let _: f64 = 1.012_345_678_901_234_6;
|
||||
//~^ excessive_precision
|
||||
const _: f32 = 1.012345678901234567890;
|
||||
const _: f64 = 1.012345678901234567890;
|
||||
|
||||
static STATIC1: f32 = 1.012345678901234567890;
|
||||
static STATIC2: f64 = 1.012345678901234567890;
|
||||
|
||||
static mut STATIC_MUT1: f32 = 1.012345678901234567890;
|
||||
static mut STATIC_MUT2: f64 = 1.012345678901234567890;
|
||||
}
|
||||
|
||||
trait ExcessivelyPreciseTrait {
|
||||
// Overly specified constants
|
||||
const GOOD1: f32 = 1.012345678901234567890;
|
||||
const GOOD2: f64 = 1.012345678901234567890;
|
||||
}
|
||||
|
||||
struct ExcessivelyPreciseStruct;
|
||||
|
||||
impl ExcessivelyPreciseStruct {
|
||||
// Overly specified constants
|
||||
const GOOD1: f32 = 1.012345678901234567890;
|
||||
const GOOD2: f64 = 1.012345678901234567890;
|
||||
}
|
||||
38
tests/ui-toml/excessive_precision/excessive_precision.rs
Normal file
38
tests/ui-toml/excessive_precision/excessive_precision.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#![warn(clippy::excessive_precision)]
|
||||
#![allow(
|
||||
dead_code,
|
||||
overflowing_literals,
|
||||
unused_variables,
|
||||
clippy::print_literal,
|
||||
clippy::useless_vec
|
||||
)]
|
||||
|
||||
fn main() {
|
||||
// Overly specified constants
|
||||
let _: f32 = 1.012345678901234567890;
|
||||
//~^ excessive_precision
|
||||
let _: f64 = 1.012345678901234567890;
|
||||
//~^ excessive_precision
|
||||
const _: f32 = 1.012345678901234567890;
|
||||
const _: f64 = 1.012345678901234567890;
|
||||
|
||||
static STATIC1: f32 = 1.012345678901234567890;
|
||||
static STATIC2: f64 = 1.012345678901234567890;
|
||||
|
||||
static mut STATIC_MUT1: f32 = 1.012345678901234567890;
|
||||
static mut STATIC_MUT2: f64 = 1.012345678901234567890;
|
||||
}
|
||||
|
||||
trait ExcessivelyPreciseTrait {
|
||||
// Overly specified constants
|
||||
const GOOD1: f32 = 1.012345678901234567890;
|
||||
const GOOD2: f64 = 1.012345678901234567890;
|
||||
}
|
||||
|
||||
struct ExcessivelyPreciseStruct;
|
||||
|
||||
impl ExcessivelyPreciseStruct {
|
||||
// Overly specified constants
|
||||
const GOOD1: f32 = 1.012345678901234567890;
|
||||
const GOOD2: f64 = 1.012345678901234567890;
|
||||
}
|
||||
38
tests/ui-toml/excessive_precision/excessive_precision.stderr
Normal file
38
tests/ui-toml/excessive_precision/excessive_precision.stderr
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
error: float has excessive precision
|
||||
--> tests/ui-toml/excessive_precision/excessive_precision.rs:12:18
|
||||
|
|
||||
LL | let _: f32 = 1.012345678901234567890;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: consider making it a `const` item
|
||||
--> tests/ui-toml/excessive_precision/excessive_precision.rs:12:5
|
||||
|
|
||||
LL | let _: f32 = 1.012345678901234567890;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: `-D clippy::excessive-precision` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::excessive_precision)]`
|
||||
help: consider changing the type or truncating it to
|
||||
|
|
||||
LL - let _: f32 = 1.012345678901234567890;
|
||||
LL + let _: f32 = 1.012_345_7;
|
||||
|
|
||||
|
||||
error: float has excessive precision
|
||||
--> tests/ui-toml/excessive_precision/excessive_precision.rs:14:18
|
||||
|
|
||||
LL | let _: f64 = 1.012345678901234567890;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: consider making it a `const` item
|
||||
--> tests/ui-toml/excessive_precision/excessive_precision.rs:14:5
|
||||
|
|
||||
LL | let _: f64 = 1.012345678901234567890;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: consider changing the type or truncating it to
|
||||
|
|
||||
LL - let _: f64 = 1.012345678901234567890;
|
||||
LL + let _: f64 = 1.012_345_678_901_234_6;
|
||||
|
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
@ -35,6 +35,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
|
|||
check-inconsistent-struct-field-initializers
|
||||
check-private-items
|
||||
cognitive-complexity-threshold
|
||||
const-literal-digits-threshold
|
||||
disallowed-macros
|
||||
disallowed-methods
|
||||
disallowed-names
|
||||
|
|
@ -129,6 +130,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
|
|||
check-inconsistent-struct-field-initializers
|
||||
check-private-items
|
||||
cognitive-complexity-threshold
|
||||
const-literal-digits-threshold
|
||||
disallowed-macros
|
||||
disallowed-methods
|
||||
disallowed-names
|
||||
|
|
@ -223,6 +225,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
|
|||
check-inconsistent-struct-field-initializers
|
||||
check-private-items
|
||||
cognitive-complexity-threshold
|
||||
const-literal-digits-threshold
|
||||
disallowed-macros
|
||||
disallowed-methods
|
||||
disallowed-names
|
||||
|
|
|
|||
|
|
@ -82,9 +82,18 @@ fn main() {
|
|||
assert!(r.is_err());
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn issue9450() {
|
||||
let res: Result<i32, i32> = Ok(1);
|
||||
res.unwrap_err();
|
||||
//~^ assertions_on_result_states
|
||||
}
|
||||
|
||||
fn issue9916(res: Result<u32, u32>) {
|
||||
let a = 0;
|
||||
match a {
|
||||
0 => {},
|
||||
1 => { res.unwrap(); },
|
||||
//~^ assertions_on_result_states
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue