Merge from rustc

This commit is contained in:
Ralf Jung 2025-01-08 09:23:40 +01:00
commit 2d180714e1
1351 changed files with 53386 additions and 14824 deletions

View file

@ -114,6 +114,7 @@ static TARGETS: &[&str] = &[
"loongarch64-unknown-none",
"loongarch64-unknown-none-softfloat",
"m68k-unknown-linux-gnu",
"m68k-unknown-none-elf",
"csky-unknown-linux-gnuabiv2",
"csky-unknown-linux-gnuabiv2hf",
"mips-unknown-linux-gnu",
@ -128,6 +129,8 @@ static TARGETS: &[&str] = &[
"mipsisa64r6el-unknown-linux-gnuabi64",
"mipsel-unknown-linux-gnu",
"mipsel-unknown-linux-musl",
"mips-mti-none-elf",
"mipsel-mti-none-elf",
"nvptx64-nvidia-cuda",
"powerpc-unknown-linux-gnu",
"powerpc64-unknown-linux-gnu",

@ -1 +1 @@
Subproject commit fd784878cfa843e3e29a6654ecf564c62fae6735
Subproject commit 088d496082726091024f1689c124a0c3dccbd775

View file

@ -17,6 +17,9 @@ jobs:
# Setup
- name: Checkout
uses: actions/checkout@v4
with:
# Unsetting this would make so that any malicious package could get our Github Token
persist-credentials: false
# Run
- name: Build

View file

@ -23,6 +23,8 @@ jobs:
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
# Unsetting this would make so that any malicious package could get our Github Token
persist-credentials: false
# Run
- name: Check Changelog
@ -63,6 +65,8 @@ jobs:
# Setup
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install i686 dependencies
if: matrix.host == 'i686-unknown-linux-gnu'
@ -74,7 +78,8 @@ jobs:
- name: Install toolchain
run: |
rustup set default-host ${{ matrix.host }}
rustup show active-toolchain
# Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0
rustup show active-toolchain || rustup toolchain install
# Run
- name: Build
@ -121,9 +126,13 @@ jobs:
# Setup
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install toolchain
run: rustup show active-toolchain
run: |
# Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0
rustup show active-toolchain || rustup toolchain install
- name: Test metadata collection
run: cargo collect-metadata
@ -136,9 +145,13 @@ jobs:
# Setup
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install toolchain
run: rustup show active-toolchain
run: |
# Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0
rustup show active-toolchain || rustup toolchain install
# Run
- name: Build Integration Test
@ -188,9 +201,13 @@ jobs:
# Setup
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install toolchain
run: rustup show active-toolchain
run: |
# Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0
rustup show active-toolchain || rustup toolchain install
# Download
- name: Download target dir
@ -205,7 +222,7 @@ jobs:
# Run
- name: Test ${{ matrix.integration }}
run: |
TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ')
TOOLCHAIN=$(rustup show active-toolchain | head -n 1 | cut -f1 -d' ')
rustup run $TOOLCHAIN $CARGO_TARGET_DIR/debug/integration --show-output
env:
INTEGRATION: ${{ matrix.integration }}

View file

@ -25,9 +25,14 @@ jobs:
# Setup
- name: Checkout
uses: actions/checkout@v4
with:
# Unsetting this would make so that any malicious package could get our Github Token
persist-credentials: false
- name: Install toolchain
run: rustup show active-toolchain
run: |
# Use a way compatible with Rustup pre-1.28.0 and Rustup 1.28.0
rustup show active-toolchain || rustup toolchain install
# Run
- name: Build

View file

@ -22,19 +22,27 @@ jobs:
# Setup
- name: Checkout
uses: actions/checkout@v4
with:
# Unsetting this would make so that any malicious package could get our Github Token
persist-credentials: false
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ env.TARGET_BRANCH }}
path: 'out'
# Unsetting this would make so that any malicious package could get our Github Token
persist-credentials: false
# Run
- name: Set tag name
if: startswith(github.ref, 'refs/tags/')
run: |
TAG=$(basename ${{ github.ref }})
TAG=$(basename "${TAGNAME}")
echo "TAG_NAME=$TAG" >> $GITHUB_ENV
env:
# Make sure that the reference gets expanded before injecting it
TAGNAME: ${{ github.ref }}
- name: Set beta to true
if: github.ref == 'refs/heads/beta'
run: echo "BETA=true" >> $GITHUB_ENV

View file

@ -21,6 +21,8 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 2
# Unsetting this would make so that any malicious package could get our Github Token
persist-credentials: false
# HEAD is the generated merge commit `refs/pull/N/merge` between the PR and `master`, `HEAD^`
# being the commit from `master` that is the base of the merge
@ -73,6 +75,9 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Unsetting this would make so that any malicious package could get our Github Token
persist-credentials: false
- name: Cache lintcheck bin
id: cache-lintcheck-bin
@ -103,6 +108,9 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Unsetting this would make so that any malicious package could get our Github Token
persist-credentials: false
- name: Restore lintcheck bin
uses: actions/cache/restore@v4

View file

@ -12,6 +12,9 @@ jobs:
# Setup
- name: Checkout
uses: actions/checkout@v4
with:
# Unsetting this would make so that any malicious package could get our Github Token
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v4

View file

@ -6,11 +6,52 @@ document.
## Unreleased / Beta / In Rust Nightly
[aa0d5513...master](https://github.com/rust-lang/rust-clippy/compare/aa0d5513...master)
[786fbd6d...master](https://github.com/rust-lang/rust-clippy/compare/786fbd6d...master)
## Rust 1.84
Current stable, released 2025-01-09
[View all 84 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-10-03T21%3A23%3A58Z..2024-11-14T17%3A41%3A37Z+base%3Amaster)
### New Lints
* Added [`unnecessary_map_or`] to `style`
[#11796](https://github.com/rust-lang/rust-clippy/pull/11796)
* Added [`arbitrary_source_item_ordering`] to `restriction`
[#13376](https://github.com/rust-lang/rust-clippy/pull/13376)
* Added [`map_with_unused_argument_over_ranges`] to `restriction`
[#13034](https://github.com/rust-lang/rust-clippy/pull/13034)
* Added [`map_all_any_identity`] to `complexity`
[#13499](https://github.com/rust-lang/rust-clippy/pull/13499)
* Added [`needless_as_bytes`] to `complexity`
[#13437](https://github.com/rust-lang/rust-clippy/pull/13437)
* Added [`unnecessary_literal_bound`] to `pedantic`
[#13395](https://github.com/rust-lang/rust-clippy/pull/13395)
* Added [`manual_ignore_case_cmp`] to `perf`
[#13334](https://github.com/rust-lang/rust-clippy/pull/13334)
* Added [`regex_creation_in_loops`] to `perf`
[#13412](https://github.com/rust-lang/rust-clippy/pull/13412)
### Moves and Deprecations
* Moved [`manual_is_power_of_two`] to `pedantic` (From `complexity`, now allow-by-default)
[#13553](https://github.com/rust-lang/rust-clippy/pull/13553)
* Move [`module_name_repetitions`] to `restriction` (from `pedantic`)
[#13541](https://github.com/rust-lang/rust-clippy/pull/13541)
### Enhancements
* [`doc_markdown`]: Added the following identifiers to [`doc-valid-idents`]:
CoAP, MHz, GHz, and THz
[#13633](https://github.com/rust-lang/rust-clippy/pull/13633)
[#13460](https://github.com/rust-lang/rust-clippy/pull/13460)
* [`large_const_arrays`]: Changed the default of [`array-size-threshold`] to `16kb` (from `512kb`)
[#13485](https://github.com/rust-lang/rust-clippy/pull/13485)
## Rust 1.83
Current stable, released 2024-11-28
Released 2024-11-28
[View all 64 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-08-25T09%3A59%3A01Z..2024-10-03T13%3A42%3A56Z+base%3Amaster)
@ -5493,6 +5534,7 @@ Released 2018-09-13
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
[`doc_nested_refdefs`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_nested_refdefs
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
[`double_ended_iterator_last`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_ended_iterator_last
[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
@ -6252,6 +6294,7 @@ Released 2018-09-13
[`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold
[`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability
[`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold
[`lint-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-inconsistent-struct-field-initializers
[`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold
[`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else
[`max-fn-params-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-fn-params-bools

View file

@ -1,7 +1,7 @@
[package]
name = "clippy"
# begin autogenerated version
version = "0.1.85"
version = "0.1.86"
# end autogenerated version
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"

View file

@ -83,7 +83,12 @@ As section headers, we use:
```
### New Lints
* Added [`LINT`] to `GROUP`
### Moves and Deprecations
* Moved [`LINT`] to `GROUP` (From `GROUP`, now LEVEL-by-default)
* Renamed `LINT` to [`LINT`]
### Enhancements
### False Positive Fixes
### Suggestion Fixes/Improvements

View file

@ -21,7 +21,7 @@ use clippy_utils::is_trait_method;
impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
// Check our expr is calling a method with pattern matching
if let hir::ExprKind::MethodCall(path, _, [self_arg, ..]) = &expr.kind
if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind
// Check if the name of this method is `our_fancy_method`
&& path.ident.name.as_str() == "our_fancy_method"
// We can check the type of the self argument whenever necessary.

View file

@ -582,6 +582,33 @@ The maximum size of the `Err`-variant in a `Result` returned from a function
* [`result_large_err`](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err)
## `lint-inconsistent-struct-field-initializers`
Whether to suggest reordering constructor fields when initializers are present.
Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the
suggested code would compile, it can change semantics if the initializer expressions have side effects. The
following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors:
```rust
struct MyStruct {
vector: Vec<u32>,
length: usize
}
fn main() {
let vector = vec![1,2,3];
MyStruct { length: vector.len(), vector};
}
```
[from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924
**Default Value:** `false`
---
**Affected lints:**
* [`inconsistent_struct_constructor`](https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor)
## `literal-representation-threshold`
The lower bound for linting decimal literals

View file

@ -1,5 +1,7 @@
avoid-breaking-exported-api = false
lint-inconsistent-struct-field-initializers = 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"

View file

@ -1,7 +1,7 @@
[package]
name = "clippy_config"
# begin autogenerated version
version = "0.1.85"
version = "0.1.86"
# end autogenerated version
edition = "2021"
publish = false

View file

@ -532,6 +532,26 @@ define_Conf! {
/// The maximum size of the `Err`-variant in a `Result` returned from a function
#[lints(result_large_err)]
large_error_threshold: u64 = 128,
/// Whether to suggest reordering constructor fields when initializers are present.
///
/// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the
/// suggested code would compile, it can change semantics if the initializer expressions have side effects. The
/// following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors:
///
/// ```rust
/// struct MyStruct {
/// vector: Vec<u32>,
/// length: usize
/// }
/// fn main() {
/// let vector = vec![1,2,3];
/// MyStruct { length: vector.len(), vector};
/// }
/// ```
///
/// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924
#[lints(inconsistent_struct_constructor)]
lint_inconsistent_struct_field_initializers: bool = false,
/// The lower bound for linting decimal literals
#[lints(decimal_literal_representation)]
literal_representation_threshold: u64 = 16384,

View file

@ -179,8 +179,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> {
#[expect(clippy::drain_collect)]
fields.push(ClippyConf {
name,
lints: lints.drain(..).collect(),
attrs: &conf[attrs_start..attrs_end],
lints: lints.drain(..).collect(),
field: conf[field_start..i].trim_end(),
});
attrs_start = i;
@ -191,8 +191,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> {
#[expect(clippy::drain_collect)]
fields.push(ClippyConf {
name,
lints: lints.drain(..).collect(),
attrs: &conf[attrs_start..attrs_end],
lints: lints.drain(..).collect(),
field: conf[field_start..i].trim_end(),
});
attrs_start = i;
@ -220,8 +220,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> {
}
fields.push(ClippyConf {
name,
lints,
attrs: &conf[attrs_start..attrs_end],
lints,
field: conf[field_start..].trim_end(),
});

View file

@ -1,7 +1,7 @@
[package]
name = "clippy_lints"
# begin autogenerated version
version = "0.1.85"
version = "0.1.86"
# end autogenerated version
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"

View file

@ -428,8 +428,8 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
// Makes a note of the current item for comparison with the next.
cur_t = Some(CurItem {
order: module_level_order,
item,
order: module_level_order,
name: get_item_name(item),
});
}
@ -464,7 +464,7 @@ fn convert_module_item_kind(value: &ItemKind<'_>) -> SourceItemOrderingModuleIte
ItemKind::Use(..) => Use,
ItemKind::Static(..) => Static,
ItemKind::Const(..) => Const,
ItemKind::Fn{ .. } => Fn,
ItemKind::Fn { .. } => Fn,
ItemKind::Macro(..) => Macro,
ItemKind::Mod(..) => Mod,
ItemKind::ForeignMod { .. } => ForeignMod,

View file

@ -66,7 +66,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item_span: Span, attrs: &[Attribute])
fn lint_mixed_attrs(cx: &EarlyContext<'_>, attrs: &[Attribute]) {
let mut attrs_iter = attrs.iter().filter(|attr| !attr.span.from_expansion());
let span = if let (Some(first), Some(last)) = (attrs_iter.next(), attrs_iter.last()) {
let span = if let (Some(first), Some(last)) = (attrs_iter.next(), attrs_iter.next_back()) {
first.span.with_hi(last.span.hi())
} else {
return;

View file

@ -1,11 +1,13 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::Msrv;
use clippy_utils::source::snippet_with_context;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::{is_lint_allowed, msrvs, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind};
use rustc_lint::LateContext;
use rustc_middle::ty::adjustment::Adjust;
use rustc_span::BytePos;
use super::BORROW_AS_PTR;
@ -32,12 +34,21 @@ pub(super) fn check<'tcx>(
return false;
}
let suggestion = if msrv.meets(msrvs::RAW_REF_OP) {
let (suggestion, span) = if msrv.meets(msrvs::RAW_REF_OP) {
let operator_kind = match mutability {
Mutability::Not => "const",
Mutability::Mut => "mut",
};
format!("&raw {operator_kind} {snip}")
// Make sure that the span to be replaced doesn't include parentheses, that could break the
// suggestion.
let span = if has_enclosing_paren(snippet_with_applicability(cx, expr.span, "", &mut app)) {
expr.span
.with_lo(expr.span.lo() + BytePos(1))
.with_hi(expr.span.hi() - BytePos(1))
} else {
expr.span
};
(format!("&raw {operator_kind} {snip}"), span)
} else {
let Some(std_or_core) = std_or_core(cx) else {
return false;
@ -46,18 +57,10 @@ pub(super) fn check<'tcx>(
Mutability::Not => "addr_of",
Mutability::Mut => "addr_of_mut",
};
format!("{std_or_core}::ptr::{macro_name}!({snip})")
(format!("{std_or_core}::ptr::{macro_name}!({snip})"), expr.span)
};
span_lint_and_sugg(
cx,
BORROW_AS_PTR,
expr.span,
"borrow as raw pointer",
"try",
suggestion,
Applicability::MachineApplicable,
);
span_lint_and_sugg(cx, BORROW_AS_PTR, span, "borrow as raw pointer", "try", suggestion, app);
return true;
}
false

View file

@ -836,11 +836,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
as_underscore::check(cx, expr, cast_to_hir);
as_pointer_underscore::check(cx, cast_to, cast_to_hir);
let was_borrow_as_ptr_emitted = if self.msrv.meets(msrvs::BORROW_AS_PTR) {
borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir, &self.msrv)
} else {
false
};
let was_borrow_as_ptr_emitted = self.msrv.meets(msrvs::BORROW_AS_PTR)
&& borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir, &self.msrv);
if self.msrv.meets(msrvs::PTR_FROM_REF) && !was_borrow_as_ptr_emitted {
ref_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
}

View file

@ -372,6 +372,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::methods::CLONE_ON_REF_PTR_INFO,
crate::methods::COLLAPSIBLE_STR_REPLACE_INFO,
crate::methods::CONST_IS_EMPTY_INFO,
crate::methods::DOUBLE_ENDED_ITERATOR_LAST_INFO,
crate::methods::DRAIN_COLLECT_INFO,
crate::methods::ERR_EXPECT_INFO,
crate::methods::EXPECT_FUN_CALL_INFO,

View file

@ -3,8 +3,11 @@ use clippy_utils::numeric_literal;
use clippy_utils::source::snippet_opt;
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::intravisit::{Visitor, walk_expr, walk_stmt};
use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind, StructTailExpr};
use rustc_hir::intravisit::{Visitor, walk_expr, walk_pat, walk_stmt};
use rustc_hir::{
Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Pat, PatExpr, PatExprKind, PatKind, Stmt, StmtKind,
StructTailExpr,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty};
@ -219,6 +222,22 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> {
walk_expr(self, expr);
}
fn visit_pat(&mut self, pat: &'tcx Pat<'_>) {
match pat.kind {
PatKind::Expr(&PatExpr {
hir_id,
kind: PatExprKind::Lit { lit, .. },
..
}) => {
let ty = self.cx.typeck_results().node_type(hir_id);
self.check_lit(lit, ty, hir_id);
return;
},
_ => {},
}
walk_pat(self, pat)
}
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
match stmt.kind {
// we cannot check the exact type since it's a hir::Ty which does not implement `is_numeric`

View file

@ -1,6 +1,6 @@
use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC};
use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::ty::{implements_trait_with_env, is_type_diagnostic_item};
use clippy_utils::{is_doc_hidden, return_ty};
use rustc_hir::{BodyId, FnSig, OwnerId, Safety};
use rustc_lint::LateContext;
@ -70,7 +70,14 @@ pub fn check(
&& let typeck = cx.tcx.typeck_body(body_id)
&& let body = cx.tcx.hir().body(body_id)
&& let ret_ty = typeck.expr_ty(body.value)
&& implements_trait(cx, ret_ty, future, &[])
&& implements_trait_with_env(
cx.tcx,
ty::TypingEnv::non_body_analysis(cx.tcx, owner_id.def_id),
ret_ty,
future,
Some(owner_id.def_id.to_def_id()),
&[],
)
&& let ty::Coroutine(_, subs) = ret_ty.kind()
&& is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result)
{

View file

@ -797,8 +797,8 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
parser.into_offset_iter(),
&doc,
Fragments {
fragments: &fragments,
doc: &doc,
fragments: &fragments,
},
))
}

View file

@ -25,7 +25,7 @@ pub(super) fn check(
// page. So associated items or impl blocks are not part of this list.
ItemKind::Static(..)
| ItemKind::Const(..)
| ItemKind::Fn{ .. }
| ItemKind::Fn { .. }
| ItemKind::Macro(..)
| ItemKind::Mod(..)
| ItemKind::TyAlias(..)

View file

@ -678,12 +678,12 @@ fn find_insert_calls<'tcx>(
map: contains_expr.map,
key: contains_expr.key,
ctxt: expr.span.ctxt(),
edits: Vec::new(),
is_map_used: false,
allow_insert_closure: true,
can_use_entry: true,
in_tail_pos: true,
is_single_insert: true,
is_map_used: false,
edits: Vec::new(),
loops: Vec::new(),
locals: HirIdSet::default(),
};

View file

@ -55,8 +55,8 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
| PatKind::Err(_) => false,
PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a),
PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) => unary_pattern(x),
PatKind::Path(_) | PatKind::Lit(_) => true,
PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x),
PatKind::Path(_) | PatKind::Expr(_) => true,
}
}

View file

@ -182,7 +182,7 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc
// will succeed iff `T: 'static`. But the region of `T` is always erased by `typeck.expr_ty()` when
// T is a generic type. For example, return type of `Option<String>::as_deref()` is a generic.
// So we have a hack like this.
&& generic_args.len() > 0
&& !generic_args.is_empty()
{
return;
}

View file

@ -243,11 +243,11 @@ fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds
&& !predicates.is_empty()
{
Some(ImplTraitBound {
span: bound.span(),
predicates,
trait_def_id,
args: path.args.map_or([].as_slice(), |p| p.args),
constraints: path.args.map_or([].as_slice(), |p| p.constraints),
trait_def_id,
span: bound.span(),
})
} else {
None

View file

@ -1,19 +1,21 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::fulfill_or_allowed;
use clippy_utils::source::snippet;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir::{self as hir, ExprKind, StructTailExpr};
use rustc_hir::{self as hir, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_middle::ty::TyCtxt;
use rustc_session::impl_lint_pass;
use rustc_span::Span;
use rustc_span::symbol::Symbol;
use std::fmt::{self, Write as _};
declare_clippy_lint! {
/// ### What it does
/// Checks for struct constructors where all fields are shorthand and
/// the order of the field init shorthand in the constructor is inconsistent
/// with the order in the struct definition.
/// Checks for struct constructors where the order of the field
/// init in the constructor is inconsistent with the order in the
/// struct definition.
///
/// ### Why is this bad?
/// Since the order of fields in a constructor doesn't affect the
@ -59,16 +61,37 @@ declare_clippy_lint! {
#[clippy::version = "1.52.0"]
pub INCONSISTENT_STRUCT_CONSTRUCTOR,
pedantic,
"the order of the field init shorthand is inconsistent with the order in the struct definition"
"the order of the field init is inconsistent with the order in the struct definition"
}
declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]);
pub struct InconsistentStructConstructor {
lint_inconsistent_struct_field_initializers: bool,
}
impl InconsistentStructConstructor {
pub fn new(conf: &'static Conf) -> Self {
Self {
lint_inconsistent_struct_field_initializers: conf.lint_inconsistent_struct_field_initializers,
}
}
}
impl_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]);
impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if let ExprKind::Struct(qpath, fields, base) = expr.kind
&& fields.iter().all(|f| f.is_shorthand)
&& !expr.span.from_expansion()
let ExprKind::Struct(_, fields, _) = expr.kind else {
return;
};
let all_fields_are_shorthand = fields.iter().all(|f| f.is_shorthand);
let applicability = if all_fields_are_shorthand {
Applicability::MachineApplicable
} else if self.lint_inconsistent_struct_field_initializers {
Applicability::MaybeIncorrect
} else {
return;
};
if !expr.span.from_expansion()
&& let ty = cx.typeck_results().expr_ty(expr)
&& let Some(adt_def) = ty.ty_adt_def()
&& adt_def.is_struct()
@ -85,36 +108,24 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
return;
}
let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect();
ordered_fields.sort_unstable_by_key(|id| def_order_map[id]);
let mut fields_snippet = String::new();
let (last_ident, idents) = ordered_fields.split_last().unwrap();
for ident in idents {
let _: fmt::Result = write!(fields_snippet, "{ident}, ");
}
fields_snippet.push_str(&last_ident.to_string());
let base_snippet = if let StructTailExpr::Base(base) = base {
format!(", ..{}", snippet(cx, base.span, ".."))
} else {
String::new()
};
let sugg = format!(
"{} {{ {fields_snippet}{base_snippet} }}",
snippet(cx, qpath.span(), ".."),
);
let span = field_with_attrs_span(cx.tcx, fields.first().unwrap())
.with_hi(field_with_attrs_span(cx.tcx, fields.last().unwrap()).hi());
if !fulfill_or_allowed(cx, INCONSISTENT_STRUCT_CONSTRUCTOR, Some(ty_hir_id)) {
span_lint_and_sugg(
span_lint_and_then(
cx,
INCONSISTENT_STRUCT_CONSTRUCTOR,
expr.span,
span,
"struct constructor field order is inconsistent with struct definition field order",
"try",
sugg,
Applicability::MachineApplicable,
|diag| {
let msg = if all_fields_are_shorthand {
"try"
} else {
"if the field evaluation order doesn't matter, try"
};
let sugg = suggestion(cx, fields, &def_order_map);
diag.span_suggestion(span, msg, sugg, applicability);
},
);
}
}
@ -135,3 +146,45 @@ fn is_consistent_order<'tcx>(fields: &'tcx [hir::ExprField<'tcx>], def_order_map
true
}
fn suggestion<'tcx>(
cx: &LateContext<'_>,
fields: &'tcx [hir::ExprField<'tcx>],
def_order_map: &FxHashMap<Symbol, usize>,
) -> String {
let ws = fields
.windows(2)
.map(|w| {
let w0_span = field_with_attrs_span(cx.tcx, &w[0]);
let w1_span = field_with_attrs_span(cx.tcx, &w[1]);
let span = w0_span.between(w1_span);
snippet(cx, span, " ")
})
.collect::<Vec<_>>();
let mut fields = fields.to_vec();
fields.sort_unstable_by_key(|field| def_order_map[&field.ident.name]);
let field_snippets = fields
.iter()
.map(|field| snippet(cx, field_with_attrs_span(cx.tcx, field), ".."))
.collect::<Vec<_>>();
assert_eq!(field_snippets.len(), ws.len() + 1);
let mut sugg = String::new();
for i in 0..field_snippets.len() {
sugg += &field_snippets[i];
if i < ws.len() {
sugg += &ws[i];
}
}
sugg
}
fn field_with_attrs_span(tcx: TyCtxt<'_>, field: &hir::ExprField<'_>) -> Span {
if let Some(attr) = tcx.hir().attrs(field.hir_id).first() {
field.span.with_lo(attr.span.lo())
} else {
field.span
}
}

View file

@ -1,6 +1,7 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{SpanRangeExt, snippet_with_context};
use clippy_utils::sugg::{Sugg, has_enclosing_paren};
use clippy_utils::ty::implements_trait;
use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, is_trait_method, peel_ref_operators};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
@ -8,8 +9,8 @@ use rustc_hir::def::Res;
use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_hir::{
AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, ImplItem, ImplItemKind,
ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatKind, PathSegment, PrimTy, QPath,
TraitItemRef, TyKind,
ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatExprKind, PatKind, PathSegment, PrimTy,
QPath, TraitItemRef, TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, AssocKind, FnSig, Ty};
@ -163,7 +164,13 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
if let ExprKind::Let(lt) = expr.kind
&& match lt.pat.kind {
PatKind::Slice([], None, []) => true,
PatKind::Lit(lit) if is_empty_string(lit) => true,
PatKind::Expr(lit) => match lit.kind {
PatExprKind::Lit { lit, .. } => match lit.node {
LitKind::Str(lit, _) => lit.as_str().is_empty(),
_ => false,
},
_ => false,
},
_ => false,
}
&& !expr.span.from_expansion()
@ -620,18 +627,30 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
})
}
let ty = &cx.typeck_results().expr_ty(expr).peel_refs();
match ty.kind() {
ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| {
let is_empty = sym!(is_empty);
cx.tcx
.associated_items(principal.def_id())
.filter_by_name_unhygienic(is_empty)
.any(|item| is_is_empty(cx, item))
}),
ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id),
ty::Adt(id, _) => has_is_empty_impl(cx, id.did()),
ty::Array(..) | ty::Slice(..) | ty::Str => true,
_ => false,
fn ty_has_is_empty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, depth: usize) -> bool {
match ty.kind() {
ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| {
let is_empty = sym!(is_empty);
cx.tcx
.associated_items(principal.def_id())
.filter_by_name_unhygienic(is_empty)
.any(|item| is_is_empty(cx, item))
}),
ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id),
ty::Adt(id, _) => {
has_is_empty_impl(cx, id.did())
|| (cx.tcx.recursion_limit().value_within_limit(depth)
&& cx.tcx.get_diagnostic_item(sym::Deref).is_some_and(|deref_id| {
implements_trait(cx, ty, deref_id, &[])
&& cx
.get_associated_type(ty, deref_id, "Target")
.is_some_and(|deref_ty| ty_has_is_empty(cx, deref_ty, depth + 1))
}))
},
ty::Array(..) | ty::Slice(..) | ty::Str => true,
_ => false,
}
}
ty_has_is_empty(cx, cx.typeck_results().expr_ty(expr).peel_refs(), 0)
}

View file

@ -649,7 +649,11 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn));
store.register_late_pass(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf)));
store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback));
store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor));
store.register_late_pass(move |_| {
Box::new(inconsistent_struct_constructor::InconsistentStructConstructor::new(
conf,
))
});
store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(conf)));

View file

@ -38,8 +38,8 @@ pub(super) fn check<'tcx>(
cx,
label,
inner_labels: label.into_iter().collect(),
is_finite: false,
loop_depth: 0,
is_finite: false,
};
loop_visitor.visit_block(loop_block);

View file

@ -220,11 +220,11 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe {
// `check_stmt_post` on `(Late)LintPass`, which we'd need to detect when we're leaving a macro span
let mut vis = BodyVisitor {
macro_unsafe_blocks: Vec::new(),
#[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning
expn_depth: if body.value.span.from_expansion() { 1 } else { 0 },
macro_unsafe_blocks: Vec::new(),
lint: self,
cx
cx,
lint: self
};
vis.visit_body(body);
}

View file

@ -2,14 +2,15 @@ use clippy_utils::SpanlessEq;
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 rustc_ast::{BinOpKind, LitKind};
use clippy_utils::sugg::{Sugg, has_enclosing_paren};
use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp};
use rustc_data_structures::packed::Pu128;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self};
use rustc_session::impl_lint_pass;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::Symbol;
use clippy_config::Conf;
@ -138,9 +139,40 @@ fn build_suggestion(
applicability: &mut Applicability,
) {
let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_par();
let type_suffix = if cx.typeck_results().expr_ty(lhs).is_numeric()
&& matches!(
lhs.kind,
ExprKind::Lit(Spanned {
node: LitKind::Int(_, LitIntType::Unsuffixed),
..
}) | ExprKind::Unary(UnOp::Neg, Expr {
kind: ExprKind::Lit(Spanned {
node: LitKind::Int(_, LitIntType::Unsuffixed),
..
}),
..
})
) {
format!("_{}", cx.typeck_results().expr_ty(rhs))
} else {
String::new()
};
let dividend_sugg_str = dividend_sugg.into_string();
// If `dividend_sugg` has enclosing paren like `(-2048)` and we need to add type suffix in the
// suggestion message, we want to make a suggestion string before `div_ceil` like
// `(-2048_{type_suffix})`.
let suggestion_before_div_ceil = if has_enclosing_paren(&dividend_sugg_str) {
format!(
"{}{})",
&dividend_sugg_str[..dividend_sugg_str.len() - 1].to_string(),
type_suffix
)
} else {
format!("{dividend_sugg_str}{type_suffix}")
};
let divisor_snippet = snippet_with_applicability(cx, rhs.span.source_callsite(), "..", applicability);
let sugg = format!("{dividend_sugg}.div_ceil({divisor_snippet})");
let sugg = format!("{suggestion_before_div_ceil}.div_ceil({divisor_snippet})");
span_lint_and_sugg(
cx,

View file

@ -32,7 +32,7 @@ declare_clippy_lint! {
/// a.eq_ignore_ascii_case(b) || a.eq_ignore_ascii_case("abc")
/// }
/// ```
#[clippy::version = "1.82.0"]
#[clippy::version = "1.84.0"]
pub MANUAL_IGNORE_CASE_CMP,
perf,
"manual case-insensitive ASCII comparison"

View file

@ -7,9 +7,9 @@ use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operator
use rustc_ast::LitKind::{Byte, Char};
use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd};
use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd, PatExpr, PatExprKind, Lit};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_middle::ty::{self, Ty};
use rustc_session::impl_lint_pass;
use rustc_span::{Span, sym};
@ -114,8 +114,8 @@ 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, start);
let range = check_range(start, end);
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);
}
}
@ -123,19 +123,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
extract_msrv_attr!(LateContext);
}
fn get_ty_sugg(cx: &LateContext<'_>, arg: &Expr<'_>, bound_expr: &Expr<'_>) -> Option<(Span, &'static str)> {
if let ExprKind::Lit(lit) = bound_expr.kind
&& let local_hid = path_to_local(arg)?
&& let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid)
fn get_ty_sugg<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'_>) -> Option<(Span, Ty<'tcx>)> {
let local_hid = path_to_local(arg)?;
if let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid)
// `ty_span` and `span` are the same for inferred type, thus a type suggestion must be given
&& ty_span == span
{
let ty_str = match lit.node {
Char(_) => "char",
Byte(_) => "u8",
_ => return None,
};
return Some((*ty_span, ty_str));
let arg_type = cx.typeck_results().expr_ty(arg);
return Some((*ty_span, arg_type));
}
None
}
@ -145,7 +140,7 @@ fn check_is_ascii(
span: Span,
recv: &Expr<'_>,
range: &CharRange,
ty_sugg: Option<(Span, &'_ str)>,
ty_sugg: Option<(Span, Ty<'_>)>,
) {
let sugg = match range {
CharRange::UpperChar => "is_ascii_uppercase",
@ -159,8 +154,8 @@ fn check_is_ascii(
let mut app = Applicability::MachineApplicable;
let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par();
let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))];
if let Some((ty_span, ty_str)) = ty_sugg {
suggestion.push((ty_span, format!("{recv}: {ty_str}")));
if let Some((ty_span, ty)) = ty_sugg {
suggestion.push((ty_span, format!("{recv}: {ty}")));
}
span_lint_and_then(
@ -196,19 +191,33 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange {
}
}
fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange {
fn check_expr_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange {
if let ExprKind::Lit(start_lit) = &start.kind
&& let ExprKind::Lit(end_lit) = &end.kind
{
match (&start_lit.node, &end_lit.node) {
(Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar,
(Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar,
(Char('a'), Char('f')) | (Byte(b'a'), Byte(b'f')) => CharRange::LowerHexLetter,
(Char('A'), Char('F')) | (Byte(b'A'), Byte(b'F')) => CharRange::UpperHexLetter,
(Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit,
_ => CharRange::Otherwise,
}
check_lit_range(start_lit, end_lit)
} else {
CharRange::Otherwise
}
}
fn check_range(start: &PatExpr<'_>, end: &PatExpr<'_>) -> CharRange {
if let PatExprKind::Lit{ lit: start_lit, negated: false } = &start.kind
&& let PatExprKind::Lit{ lit: end_lit, negated: false } = &end.kind
{
check_lit_range(start_lit, end_lit)
} else {
CharRange::Otherwise
}
}
fn check_lit_range(start_lit: &Lit, end_lit: &Lit) -> CharRange {
match (&start_lit.node, &end_lit.node) {
(Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar,
(Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar,
(Char('a'), Char('f')) | (Byte(b'a'), Byte(b'f')) => CharRange::LowerHexLetter,
(Char('A'), Char('F')) | (Byte(b'A'), Byte(b'F')) => CharRange::UpperHexLetter,
(Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit,
_ => CharRange::Otherwise,
}
}

View file

@ -3,7 +3,7 @@ use clippy_utils::source::SpanRangeExt;
use rustc_ast::LitKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd, UnOp};
use rustc_hir::{PatExpr, PatExprKind, PatKind, RangeEnd};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::declare_lint_pass;
@ -38,14 +38,13 @@ declare_clippy_lint! {
}
declare_lint_pass!(ManualRangePatterns => [MANUAL_RANGE_PATTERNS]);
fn expr_as_i128(expr: &Expr<'_>) -> Option<i128> {
if let ExprKind::Unary(UnOp::Neg, expr) = expr.kind {
expr_as_i128(expr).map(|num| -num)
} else if let ExprKind::Lit(lit) = expr.kind
fn expr_as_i128(expr: &PatExpr<'_>) -> Option<i128> {
if let PatExprKind::Lit { lit, negated } = expr.kind
&& let LitKind::Int(num, _) = lit.node
{
// Intentionally not handling numbers greater than i128::MAX (for u128 literals) for now.
num.get().try_into().ok()
let n = i128::try_from(num.get()).ok()?;
Some(if negated { -n } else { n })
} else {
None
}
@ -58,7 +57,7 @@ struct Num {
}
impl Num {
fn new(expr: &Expr<'_>) -> Option<Self> {
fn new(expr: &PatExpr<'_>) -> Option<Self> {
Some(Self {
val: expr_as_i128(expr)?,
span: expr.span,
@ -90,7 +89,7 @@ impl LateLintPass<'_> for ManualRangePatterns {
let mut ranges_found = Vec::new();
for pat in pats {
if let PatKind::Lit(lit) = pat.kind
if let PatKind::Expr(lit) = pat.kind
&& let Some(num) = Num::new(lit)
{
numbers_found.insert(num.val);

View file

@ -4,7 +4,7 @@ use clippy_utils::source::{expr_block, snippet};
use clippy_utils::sugg::Sugg;
use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Arm, Expr, ExprKind, PatKind};
use rustc_hir::{Arm, Expr, PatExprKind, PatKind};
use rustc_lint::LateContext;
use rustc_middle::ty;
@ -21,8 +21,8 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]
move |diag| {
if arms.len() == 2 {
// no guards
let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind {
if let ExprKind::Lit(lit) = arm_bool.kind {
let exprs = if let PatKind::Expr(arm_bool) = arms[0].pat.kind {
if let PatExprKind::Lit { lit, .. } = arm_bool.kind {
match lit.node {
LitKind::Bool(true) => Some((arms[0].body, arms[1].body)),
LitKind::Bool(false) => Some((arms[1].body, arms[0].body)),

View file

@ -7,7 +7,7 @@ use rustc_arena::DroplessArena;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd};
use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExprKind, PatKind, RangeEnd};
use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty;
@ -254,9 +254,11 @@ impl<'a> NormalizedPat<'a> {
fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self {
match pat.kind {
PatKind::Wild | PatKind::Binding(.., None) => Self::Wild,
PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Deref(pat) | PatKind::Ref(pat, _) => {
Self::from_pat(cx, arena, pat)
},
PatKind::Binding(.., Some(pat))
| PatKind::Box(pat)
| PatKind::Deref(pat)
| PatKind::Ref(pat, _)
| PatKind::Guard(pat, _) => Self::from_pat(cx, arena, pat),
PatKind::Never => Self::Never,
PatKind::Struct(ref path, fields, _) => {
let fields =
@ -309,9 +311,9 @@ impl<'a> NormalizedPat<'a> {
);
Self::Tuple(None, pats)
},
PatKind::Lit(e) => match &e.kind {
PatKind::Expr(e) => match &e.kind {
// TODO: Handle negative integers. They're currently treated as a wild match.
ExprKind::Lit(lit) => match lit.node {
PatExprKind::Lit { lit, negated: false } => match lit.node {
LitKind::Str(sym, _) => Self::LitStr(sym),
LitKind::ByteStr(ref bytes, _) | LitKind::CStr(ref bytes, _) => Self::LitBytes(bytes),
LitKind::Byte(val) => Self::LitInt(val.into()),
@ -328,7 +330,7 @@ impl<'a> NormalizedPat<'a> {
let start = match start {
None => 0,
Some(e) => match &e.kind {
ExprKind::Lit(lit) => match lit.node {
PatExprKind::Lit { lit, negated: false } => match lit.node {
LitKind::Int(val, _) => val.get(),
LitKind::Char(val) => val.into(),
LitKind::Byte(val) => val.into(),
@ -340,7 +342,7 @@ impl<'a> NormalizedPat<'a> {
let (end, bounds) = match end {
None => (u128::MAX, RangeEnd::Included),
Some(e) => match &e.kind {
ExprKind::Lit(lit) => match lit.node {
PatExprKind::Lit { lit, negated: false } => match lit.node {
LitKind::Int(val, _) => (val.get(), bounds),
LitKind::Char(val) => (val.into(), bounds),
LitKind::Byte(val) => (val.into(), bounds),

View file

@ -5,7 +5,7 @@ use clippy_utils::ty::is_type_lang_item;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{Visitor, walk_expr};
use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatKind};
use rustc_hir::{Arm, Expr, ExprKind, PatExpr, PatExprKind, LangItem, PatKind};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::Span;
@ -85,8 +85,8 @@ fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<(
};
for arm in arms {
if let PatKind::Lit(Expr {
kind: ExprKind::Lit(lit),
if let PatKind::Expr(PatExpr {
kind: PatExprKind::Lit { lit, negated: false },
..
}) = arm.pat.kind
&& let LitKind::Str(symbol, _) = lit.node

View file

@ -583,11 +583,6 @@ declare_clippy_lint! {
/// are the same on purpose, you can factor them
/// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
///
/// ### Known problems
/// False positive possible with order dependent `match`
/// (see issue
/// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
///
/// ### Example
/// ```rust,ignore
/// match foo {

View file

@ -8,7 +8,7 @@ use clippy_utils::{
};
use rustc_errors::Applicability;
use rustc_hir::LangItem::OptionNone;
use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatKind, Path, QPath};
use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatExprKind, PatKind, Path, QPath};
use rustc_lint::LateContext;
use rustc_span::sym;
@ -133,7 +133,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>
},
// compare match_expr ty with RetTy in `fn foo() -> RetTy`
Node::Item(item) => {
if let ItemKind::Fn{ .. } = item.kind {
if let ItemKind::Fn { .. } = item.kind {
let output = cx
.tcx
.fn_sig(item.owner_id)
@ -189,8 +189,12 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
});
},
// Example: `5 => 5`
(PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => {
if let ExprKind::Lit(pat_spanned) = &pat_lit_expr.kind {
(PatKind::Expr(pat_expr_expr), ExprKind::Lit(expr_spanned)) => {
if let PatExprKind::Lit {
lit: pat_spanned,
negated: false,
} = &pat_expr_expr.kind
{
return pat_spanned.node == expr_spanned.node;
}
},

View file

@ -34,13 +34,13 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
if let Arm { pat, guard: None, .. } = *arm {
if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
let lhs_const = if let Some(lhs) = lhs {
ConstEvalCtxt::new(cx).eval(lhs)?
ConstEvalCtxt::new(cx).eval_pat_expr(lhs)?
} else {
let min_val_const = ty.numeric_min_val(cx.tcx)?;
mir_to_const(cx.tcx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))?
};
let rhs_const = if let Some(rhs) = rhs {
ConstEvalCtxt::new(cx).eval(rhs)?
ConstEvalCtxt::new(cx).eval_pat_expr(rhs)?
} else {
let max_val_const = ty.numeric_max_val(cx.tcx)?;
mir_to_const(cx.tcx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))?
@ -57,8 +57,10 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
});
}
if let PatKind::Lit(value) = pat.kind {
let value = ConstEvalCtxt::new(cx).eval_full_int(value)?;
if let PatKind::Expr(value) = pat.kind {
let value = ConstEvalCtxt::new(cx)
.eval_pat_expr(value)?
.int_value(cx.tcx, cx.typeck_results().node_type(pat.hir_id))?;
return Some(SpannedRange {
span: pat.span,
node: (value, EndBound::Included(value)),

View file

@ -1,6 +1,6 @@
use super::REDUNDANT_PATTERN_MATCHING;
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, walk_span_to_context};
use clippy_utils::source::walk_span_to_context;
use clippy_utils::sugg::{Sugg, make_unop};
use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop};
use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures};
@ -9,7 +9,7 @@ use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp};
use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExprKind, PatKind, QPath, UnOp};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, GenericArgKind, Ty};
use rustc_span::{Span, Symbol, sym};
@ -74,8 +74,8 @@ fn find_match_true<'tcx>(
span: Span,
message: &'static str,
) {
if let PatKind::Lit(lit) = pat.kind
&& let ExprKind::Lit(lit) = lit.kind
if let PatKind::Expr(lit) = pat.kind
&& let PatExprKind::Lit { lit, negated: false } = lit.kind
&& let LitKind::Bool(pat_is_true) = lit.node
{
let mut applicability = Applicability::MachineApplicable;
@ -274,7 +274,9 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
ExprKind::AddrOf(_, _, borrowed) => borrowed,
_ => op,
};
let mut sugg = format!("{}.{good_method}", snippet(cx, result_expr.span, "_"));
let mut app = Applicability::MachineApplicable;
let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_par();
let mut sugg = format!("{receiver_sugg}.{good_method}");
if let Some(guard) = maybe_guard {
// wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying!
@ -307,7 +309,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
format!("redundant pattern matching, consider using `{good_method}`"),
"try",
sugg,
Applicability::MachineApplicable,
app,
);
}
}

View file

@ -9,7 +9,7 @@ use rustc_arena::DroplessArena;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{Visitor, walk_pat};
use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind};
use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, StmtKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, AdtDef, TyCtxt, TypeckResults, VariantDef};
use rustc_span::{Span, sym};
@ -114,7 +114,7 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp
}
let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat);
let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind
let (msg, sugg) = if let PatKind::Path(_) | PatKind::Expr(_) = pat.kind
&& let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex))
&& let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait()
&& let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait()
@ -126,8 +126,8 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp
// scrutinee derives PartialEq and the pattern is a constant.
let pat_ref_count = match pat.kind {
// string literals are already a reference.
PatKind::Lit(Expr {
kind: ExprKind::Lit(lit),
PatKind::Expr(PatExpr {
kind: PatExprKind::Lit { lit, negated: false },
..
}) if lit.node.is_str() || lit.node.is_bytestr() => pat_ref_count + 1,
_ => pat_ref_count,
@ -343,6 +343,10 @@ impl<'a> PatState<'a> {
matches!(self, Self::Wild)
},
PatKind::Guard(..) => {
matches!(self, Self::Wild)
},
// Patterns for things which can only contain a single sub-pattern.
PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) | PatKind::Box(pat) | PatKind::Deref(pat) => {
self.add_pat(cx, pat)
@ -380,7 +384,7 @@ impl<'a> PatState<'a> {
PatKind::Wild
| PatKind::Binding(_, _, _, None)
| PatKind::Lit(_)
| PatKind::Expr(_)
| PatKind::Range(..)
| PatKind::Path(_)
| PatKind::Never

View file

@ -0,0 +1,41 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_trait_method;
use clippy_utils::ty::implements_trait;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::Instance;
use rustc_span::{Span, sym};
use super::DOUBLE_ENDED_ITERATOR_LAST;
pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Expr<'_>, call_span: Span) {
let typeck = cx.typeck_results();
// if the "last" method is that of Iterator
if is_trait_method(cx, expr, sym::Iterator)
// if self implements DoubleEndedIterator
&& let Some(deiter_id) = cx.tcx.get_diagnostic_item(sym::DoubleEndedIterator)
&& let self_type = cx.typeck_results().expr_ty(self_expr)
&& implements_trait(cx, self_type.peel_refs(), deiter_id, &[])
// resolve the method definition
&& let id = typeck.type_dependent_def_id(expr.hir_id).unwrap()
&& let args = typeck.node_args(expr.hir_id)
&& let Ok(Some(fn_def)) = Instance::try_resolve(cx.tcx, cx.typing_env(), id, args)
// find the provided definition of Iterator::last
&& let Some(item) = cx.tcx.get_diagnostic_item(sym::Iterator)
&& let Some(last_def) = cx.tcx.provided_trait_methods(item).find(|m| m.name.as_str() == "last")
// if the resolved method is the same as the provided definition
&& fn_def.def_id() == last_def.def_id
{
span_lint_and_sugg(
cx,
DOUBLE_ENDED_ITERATOR_LAST,
call_span,
"called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator",
"try",
"next_back()".to_string(),
Applicability::MachineApplicable,
);
}
}

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_trait_method;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_trait_method, span_contains_comment};
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
@ -17,10 +17,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_
let mut applicability = Applicability::MachineApplicable;
let closure_snippet = snippet_with_applicability(cx, map_arg.span, "..", &mut applicability);
let span = expr.span.with_lo(map_span.lo());
// If the methods are separated with comments, we don't apply suggestion automatically.
if span_contains_comment(cx.tcx.sess.source_map(), span) {
applicability = Applicability::Unspecified;
}
span_lint_and_sugg(
cx,
MAP_FLATTEN,
expr.span.with_lo(map_span.lo()),
span,
format!("called `map(..).flatten()` on `{caller_ty_name}`"),
format!("try replacing `map` with `{method_to_use}` and remove the `.flatten()`"),
format!("{method_to_use}({closure_snippet})"),

View file

@ -1,8 +1,9 @@
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};
use clippy_utils::{is_expr_untyped_identity_function, is_trait_method, path_to_local};
use rustc_ast::BindingMode;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::{self as hir, Node, PatKind};
use rustc_lint::LateContext;
use rustc_span::{Span, sym};
@ -24,6 +25,16 @@ pub(super) fn check(
&& is_expr_untyped_identity_function(cx, map_arg)
&& let Some(sugg_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;
}
span_lint_and_sugg(
cx,
MAP_IDENTITY,

View file

@ -14,6 +14,7 @@ mod clone_on_copy;
mod clone_on_ref_ptr;
mod cloned_instead_of_copied;
mod collapsible_str_replace;
mod double_ended_iterator_last;
mod drain_collect;
mod err_expect;
mod expect_fun_call;
@ -4284,6 +4285,32 @@ declare_clippy_lint! {
"map of a trivial closure (not dependent on parameter) over a range"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for `Iterator::last` being called on a `DoubleEndedIterator`, which can be replaced
/// with `DoubleEndedIterator::next_back`.
///
/// ### Why is this bad?
///
/// `Iterator::last` is implemented by consuming the iterator, which is unnecessary if
/// the iterator is a `DoubleEndedIterator`. Since Rust traits do not allow specialization,
/// `Iterator::last` cannot be optimized for `DoubleEndedIterator`.
///
/// ### Example
/// ```no_run
/// let last_arg = "echo hello world".split(' ').last();
/// ```
/// Use instead:
/// ```no_run
/// let last_arg = "echo hello world".split(' ').next_back();
/// ```
#[clippy::version = "1.85.0"]
pub DOUBLE_ENDED_ITERATOR_LAST,
perf,
"using `Iterator::last` on a `DoubleEndedIterator`"
}
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
@ -4449,6 +4476,7 @@ impl_lint_pass!(Methods => [
MAP_ALL_ANY_IDENTITY,
MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
UNNECESSARY_MAP_OR,
DOUBLE_ENDED_ITERATOR_LAST,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@ -4931,6 +4959,7 @@ impl Methods {
false,
);
}
double_ended_iterator_last::check(cx, expr, recv, call_span);
},
("len", []) => {
if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) {

View file

@ -7,6 +7,7 @@ use rustc_span::Span;
use super::NEEDLESS_CHARACTER_ITERATION;
use super::utils::get_last_chain_binding_hir_id;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::paths::CHAR_IS_ASCII;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::{match_def_path, path_to_local_id, peel_blocks};
@ -77,7 +78,7 @@ fn handle_expr(
if revert != is_all
&& let ExprKind::Path(path) = fn_path.kind
&& let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id()
&& match_def_path(cx, fn_def_id, &["core", "char", "methods", "<impl char>", "is_ascii"])
&& match_def_path(cx, fn_def_id, &CHAR_IS_ASCII)
&& path_to_local_id(peels_expr_ref(arg), first_param)
&& let Some(snippet) = before_chars.get_source_text(cx)
{

View file

@ -470,14 +470,14 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>(
captured_ids: HirIdSet,
) -> Option<Vec<IterFunction>> {
let mut visitor = IterFunctionVisitor {
uses: Vec::new(),
target: id,
seen_other: false,
cx,
current_mutably_captured_ids: HirIdSet::default(),
illegal_mutable_capture_ids: captured_ids,
current_mutably_captured_ids: HirIdSet::default(),
cx,
uses: Vec::new(),
hir_id_uses_map: FxHashMap::default(),
current_statement_hir_id: None,
seen_other: false,
target: id,
};
visitor.visit_block(block);
if visitor.seen_other {

View file

@ -1,6 +1,7 @@
use std::ops::ControlFlow;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::paths::STDIN;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::for_each_local_use_after_expr;
@ -33,7 +34,7 @@ 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()
&& match_def_path(cx, recv_adt.did(), &["std", "io", "stdio", "Stdin"])
&& match_def_path(cx, recv_adt.did(), &STDIN)
&& let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind
&& let Res::Local(local_id) = path.res
{

View file

@ -297,8 +297,8 @@ fn parse_iter_usage<'tcx>(
{
Some(IterUsage {
kind: IterUsageKind::NextTuple,
span: e.span,
unwrap_kind: None,
span: e.span,
})
} else {
None

View file

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

View file

@ -7,7 +7,7 @@ use clippy_utils::source::snippet_opt;
use clippy_utils::sugg::{Sugg, make_binop};
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait};
use clippy_utils::visitors::is_local_used;
use clippy_utils::{is_from_proc_macro, path_to_local_id};
use clippy_utils::{get_parent_expr, is_from_proc_macro, path_to_local_id};
use rustc_ast::LitKind::Bool;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
@ -96,11 +96,25 @@ pub(super) fn check<'a>(
Sugg::hir(cx, non_binding_location, "")
)));
let binop = make_binop(op.node, &Sugg::hir(cx, recv, ".."), &inner_non_binding)
.maybe_par()
.into_string();
let mut app = Applicability::MachineApplicable;
let binop = make_binop(
op.node,
&Sugg::hir_with_applicability(cx, recv, "..", &mut app),
&inner_non_binding,
);
(binop, "a standard comparison", Applicability::MaybeIncorrect)
let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) {
match parent_expr.kind {
ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_par(),
ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_par(),
_ => binop,
}
} else {
binop
}
.into_string();
(sugg, "a standard comparison", app)
} else if !def_bool
&& msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND)
&& let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite())

View file

@ -494,7 +494,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
match node {
Node::Stmt(_) => return true,
Node::Block(..) => continue,
Node::Block(..) => {},
Node::Item(item) => {
if let ItemKind::Fn { body: body_id, .. } = &item.kind
&& let output_ty = return_ty(cx, item.owner_id)

View file

@ -2,7 +2,7 @@ use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method};
use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, is_in_test, trait_ref_of_method};
use rustc_errors::Applicability;
use rustc_hir::def_id::CRATE_DEF_ID;
use rustc_hir::intravisit::FnKind;
@ -97,6 +97,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
span: Span,
def_id: LocalDefId,
) {
let hir_id = cx.tcx.local_def_id_to_hir_id(def_id);
if is_in_test(cx.tcx, hir_id) {
return;
}
if !self.msrv.meets(msrvs::CONST_IF_MATCH) {
return;
}
@ -136,8 +141,6 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
return;
}
let hir_id = cx.tcx.local_def_id_to_hir_id(def_id);
// Const fns are not allowed as methods in a trait.
{
let parent = cx.tcx.hir().get_parent_item(hir_id).def_id;

View file

@ -192,7 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) {
match it.kind {
hir::ItemKind::Fn{ .. } => {
hir::ItemKind::Fn { .. } => {
// ignore main()
if it.ident.name == sym::main {
let at_root = cx.tcx.local_parent(it.owner_id.def_id) == CRATE_DEF_ID;

View file

@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
return;
}
match it.kind {
hir::ItemKind::Fn{ .. } => {
hir::ItemKind::Fn { .. } => {
let desc = "a function";
let attrs = cx.tcx.hir().attrs(it.hir_id());
check_missing_inline_attrs(cx, attrs, it.span, desc);

View file

@ -1,4 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use rustc_ast::ast::{BindingMode, ByRef, Lifetime, Mutability, Param, PatKind, Path, TyKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
@ -80,7 +81,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
applicability = Applicability::HasPlaceholders;
"&'_ mut self".to_string()
} else {
format!("&{} mut self", &lifetime.ident.name)
let lt_name = snippet_with_applicability(cx, lifetime.ident.span, "..", &mut applicability);
format!("&{lt_name} mut self")
}
},
(Mode::Ref(None), Mutability::Not) => "&self".to_string(),
@ -89,7 +91,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod
applicability = Applicability::HasPlaceholders;
"&'_ self".to_string()
} else {
format!("&{} self", &lifetime.ident.name)
let lt_name = snippet_with_applicability(cx, lifetime.ident.span, "..", &mut applicability);
format!("&{lt_name} self")
}
},
(Mode::Value, Mutability::Mut) => "mut self".to_string(),

View file

@ -9,7 +9,7 @@ use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath};
use rustc_index::bit_set::BitSet;
use rustc_index::bit_set::DenseBitSet;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir::{Rvalue, StatementKind};
@ -390,7 +390,7 @@ fn replace_types<'tcx>(
projection_predicates: &[ProjectionPredicate<'tcx>],
args: &mut [GenericArg<'tcx>],
) -> bool {
let mut replaced = BitSet::new_empty(args.len());
let mut replaced = DenseBitSet::new_empty(args.len());
let mut deque = VecDeque::with_capacity(args.len());
deque.push_back((param_ty, new_ty));

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::source::{indent_of, snippet, snippet_block};
use rustc_ast::ast;
use rustc_ast::{Block, Label, ast};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_session::declare_lint_pass;
use rustc_span::Span;
@ -11,6 +11,7 @@ declare_clippy_lint! {
/// that contain a `continue` statement in either their main blocks or their
/// `else`-blocks, when omitting the `else`-block possibly with some
/// rearrangement of code can make the code easier to understand.
/// The lint also checks if the last statement in the loop is a `continue`
///
/// ### Why is this bad?
/// Having explicit `else` blocks for `if` statements
@ -75,6 +76,49 @@ declare_clippy_lint! {
/// # break;
/// }
/// ```
///
/// ```rust
/// # use std::io::ErrorKind;
///
/// fn foo() -> ErrorKind { ErrorKind::NotFound }
/// for _ in 0..10 {
/// match foo() {
/// ErrorKind::NotFound => {
/// eprintln!("not found");
/// continue
/// }
/// ErrorKind::TimedOut => {
/// eprintln!("timeout");
/// continue
/// }
/// _ => {
/// eprintln!("other error");
/// continue
/// }
/// }
/// }
/// ```
/// Could be rewritten as
///
///
/// ```rust
/// # use std::io::ErrorKind;
///
/// fn foo() -> ErrorKind { ErrorKind::NotFound }
/// for _ in 0..10 {
/// match foo() {
/// ErrorKind::NotFound => {
/// eprintln!("not found");
/// }
/// ErrorKind::TimedOut => {
/// eprintln!("timeout");
/// }
/// _ => {
/// eprintln!("other error");
/// }
/// }
/// }
/// ```
#[clippy::version = "pre 1.29.0"]
pub NEEDLESS_CONTINUE,
pedantic,
@ -144,7 +188,7 @@ impl EarlyLintPass for NeedlessContinue {
///
/// - The expression is a `continue` node.
/// - The expression node is a block with the first statement being a `continue`.
fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool {
fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&Label>) -> bool {
match else_expr.kind {
ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label),
ast::ExprKind::Continue(l) => compare_labels(label, l.as_ref()),
@ -152,7 +196,7 @@ fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>)
}
}
fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool {
fn is_first_block_stmt_continue(block: &Block, label: Option<&Label>) -> bool {
block.stmts.first().is_some_and(|stmt| match stmt.kind {
ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
if let ast::ExprKind::Continue(ref l) = e.kind {
@ -166,7 +210,7 @@ fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>)
}
/// If the `continue` has a label, check it matches the label of the loop.
fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast::Label>) -> bool {
fn compare_labels(loop_label: Option<&Label>, continue_label: Option<&Label>) -> bool {
match (loop_label, continue_label) {
// `loop { continue; }` or `'a loop { continue; }`
(_, None) => true,
@ -181,7 +225,7 @@ fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast::
/// the AST object representing the loop block of `expr`.
fn with_loop_block<F>(expr: &ast::Expr, mut func: F)
where
F: FnMut(&ast::Block, Option<&ast::Label>),
F: FnMut(&Block, Option<&Label>),
{
if let ast::ExprKind::While(_, loop_block, label)
| ast::ExprKind::ForLoop {
@ -205,7 +249,7 @@ where
/// - The `else` expression.
fn with_if_expr<F>(stmt: &ast::Stmt, mut func: F)
where
F: FnMut(&ast::Expr, &ast::Expr, &ast::Block, &ast::Expr),
F: FnMut(&ast::Expr, &ast::Expr, &Block, &ast::Expr),
{
match stmt.kind {
ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
@ -231,14 +275,14 @@ struct LintData<'a> {
/// The condition expression for the above `if`.
if_cond: &'a ast::Expr,
/// The `then` block of the `if` statement.
if_block: &'a ast::Block,
if_block: &'a Block,
/// The `else` block of the `if` statement.
/// Note that we only work with `if` exprs that have an `else` branch.
else_expr: &'a ast::Expr,
/// The 0-based index of the `if` statement in the containing loop block.
stmt_idx: usize,
/// The statements of the loop block.
loop_block: &'a ast::Block,
loop_block: &'a Block,
}
const MSG_REDUNDANT_CONTINUE_EXPRESSION: &str = "this `continue` expression is redundant";
@ -329,33 +373,74 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin
)
}
fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
if let ast::ExprKind::Loop(loop_block, loop_label, ..) = &expr.kind
&& let Some(last_stmt) = loop_block.stmts.last()
&& let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind
&& let ast::ExprKind::Continue(continue_label) = inner_expr.kind
&& compare_labels(loop_label.as_ref(), continue_label.as_ref())
{
span_lint_and_help(
cx,
NEEDLESS_CONTINUE,
last_stmt.span,
MSG_REDUNDANT_CONTINUE_EXPRESSION,
None,
DROP_CONTINUE_EXPRESSION_MSG,
);
fn check_last_stmt_in_expr<F>(inner_expr: &ast::Expr, func: &F)
where
F: Fn(Option<&Label>, Span),
{
match &inner_expr.kind {
ast::ExprKind::Continue(continue_label) => {
func(continue_label.as_ref(), inner_expr.span);
},
ast::ExprKind::If(_, then_block, else_block) => {
check_last_stmt_in_block(then_block, func);
if let Some(else_block) = else_block {
check_last_stmt_in_expr(else_block, func);
}
},
ast::ExprKind::Match(_, arms, _) => {
for arm in arms {
if let Some(expr) = &arm.body {
check_last_stmt_in_expr(expr, func);
}
}
},
ast::ExprKind::Block(b, _) => {
check_last_stmt_in_block(b, func);
},
_ => {},
}
}
fn check_last_stmt_in_block<F>(b: &Block, func: &F)
where
F: Fn(Option<&Label>, Span),
{
if let Some(last_stmt) = b.stmts.last()
&& let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind
{
check_last_stmt_in_expr(inner_expr, func);
}
}
fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
with_loop_block(expr, |loop_block, label| {
for (i, stmt) in loop_block.stmts.iter().enumerate() {
let p = |continue_label: Option<&Label>, span: Span| {
if compare_labels(label, continue_label) {
span_lint_and_help(
cx,
NEEDLESS_CONTINUE,
span,
MSG_REDUNDANT_CONTINUE_EXPRESSION,
None,
DROP_CONTINUE_EXPRESSION_MSG,
);
}
};
let stmts = &loop_block.stmts;
for (i, stmt) in stmts.iter().enumerate() {
let mut maybe_emitted_in_if = false;
with_if_expr(stmt, |if_expr, cond, then_block, else_expr| {
let data = &LintData {
stmt_idx: i,
if_expr,
if_cond: cond,
if_block: then_block,
else_expr,
stmt_idx: i,
loop_block,
};
maybe_emitted_in_if = true;
if needless_continue_in_else(else_expr, label) {
emit_warning(
cx,
@ -365,8 +450,14 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
);
} else if is_first_block_stmt_continue(then_block, label) {
emit_warning(cx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock);
} else {
maybe_emitted_in_if = false;
}
});
if i == stmts.len() - 1 && !maybe_emitted_in_if {
check_last_stmt_in_block(loop_block, &p);
}
}
});
}
@ -400,7 +491,7 @@ fn erode_from_back(s: &str) -> String {
if ret.is_empty() { s.to_string() } else { ret }
}
fn span_of_first_expr_in_block(block: &ast::Block) -> Option<Span> {
fn span_of_first_expr_in_block(block: &Block) -> Option<Span> {
block.stmts.first().map(|stmt| stmt.span)
}

View file

@ -144,7 +144,7 @@ impl NoEffect {
|diag| {
for parent in cx.tcx.hir().parent_iter(stmt.hir_id) {
if let Node::Item(item) = parent.1
&& let ItemKind::Fn{ .. } = item.kind
&& let ItemKind::Fn { .. } = item.kind
&& let Node::Block(block) = cx.tcx.parent_hir_node(stmt.hir_id)
&& let [.., final_stmt] = block.stmts
&& final_stmt.hir_id == stmt.hir_id

View file

@ -189,6 +189,8 @@ impl<'tcx> NonCopyConst<'tcx> {
}
fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool {
// No branch that we check (yet) should continue if val isn't a ValTree::Branch
let ty::ValTree::Branch(val) = val else { return false };
match *ty.kind() {
// the fact that we have to dig into every structs to search enums
// leads us to the point checking `UnsafeCell` directly is the only option.
@ -197,12 +199,13 @@ impl<'tcx> NonCopyConst<'tcx> {
// contained value.
ty::Adt(def, ..) if def.is_union() => false,
ty::Array(ty, _) => val
.unwrap_branch()
.iter()
.any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
ty::Adt(def, args) if def.is_enum() => {
let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap();
let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().to_u32());
let Some((&ty::ValTree::Leaf(variant_index), fields)) = val.split_first() else {
return false;
};
let variant_index = VariantIdx::from_u32(variant_index.to_u32());
fields
.iter()
.copied()
@ -215,12 +218,10 @@ impl<'tcx> NonCopyConst<'tcx> {
.any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, field, ty))
},
ty::Adt(def, args) => val
.unwrap_branch()
.iter()
.zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args)))
.any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
ty::Tuple(tys) => val
.unwrap_branch()
.iter()
.zip(tys)
.any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),

View file

@ -270,10 +270,10 @@ impl SimilarNamesNameVisitor<'_, '_, '_> {
return;
}
self.0.names.push(ExistingName {
exemptions: get_exemptions(interned_name).unwrap_or(&[]),
interned: ident.name,
span: ident.span,
len: count,
exemptions: get_exemptions(interned_name).unwrap_or(&[]),
});
}

View file

@ -47,6 +47,7 @@ impl ArithmeticSideEffects {
Self {
allowed_binary,
allowed_unary,
const_span: None,
disallowed_int_methods: [
sym::saturating_div,
sym::wrapping_div,
@ -55,7 +56,6 @@ impl ArithmeticSideEffects {
]
.into_iter()
.collect(),
const_span: None,
expr_span: None,
}
}

View file

@ -143,11 +143,11 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> {
self.searcher = Some(PathbufPushSearcher {
local_id: id,
lhs_is_let: true,
name: name.name,
let_ty_span: local.ty.map(|ty| ty.span),
err_span: local.span,
init_val: *init_expr,
arg: None,
name: name.name,
err_span: local.span,
});
}
}
@ -165,10 +165,10 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> {
local_id: id,
lhs_is_let: false,
let_ty_span: None,
name: name.ident.name,
err_span: expr.span,
init_val: *right,
arg: None,
name: name.ident.name,
err_span: expr.span,
});
}
}

View file

@ -69,7 +69,6 @@ impl EarlyLintPass for RedundantElse {
ExprKind::If(_, next_then, Some(next_els)) => {
then = next_then;
els = next_els;
continue;
},
// else if without else
ExprKind::If(..) => return,

View file

@ -17,9 +17,9 @@ declare_clippy_lint! {
/// Checks for redundant redefinitions of local bindings.
///
/// ### Why is this bad?
/// Redundant redefinitions of local bindings do not change behavior and are likely to be unintended.
/// Redundant redefinitions of local bindings do not change behavior other than variable's lifetimes and are likely to be unintended.
///
/// Note that although these bindings do not affect your code's meaning, they _may_ affect `rustc`'s stack allocation.
/// These rebindings can be intentional to shorten the lifetimes of variables because they affect when the `Drop` implementation is called. Other than that, they do not affect your code's meaning but they _may_ affect `rustc`'s stack allocation.
///
/// ### Example
/// ```no_run
@ -41,7 +41,7 @@ declare_clippy_lint! {
/// ```
#[clippy::version = "1.73.0"]
pub REDUNDANT_LOCALS,
correctness,
suspicious,
"redundant redefinition of a local binding"
}
declare_lint_pass!(RedundantLocals => [REDUNDANT_LOCALS]);

View file

@ -24,6 +24,11 @@ declare_clippy_lint! {
/// ```ignore
/// Regex::new("(")
/// ```
///
/// Use instead:
/// ```ignore
/// Regex::new("\(")
/// ```
#[clippy::version = "pre 1.29.0"]
pub INVALID_REGEX,
correctness,
@ -49,6 +54,11 @@ declare_clippy_lint! {
/// ```ignore
/// Regex::new("^foobar")
/// ```
///
/// Use instead:
/// ```ignore
/// str::starts_with("foobar")
/// ```
#[clippy::version = "pre 1.29.0"]
pub TRIVIAL_REGEX,
nursery,
@ -87,7 +97,7 @@ declare_clippy_lint! {
/// }
/// }
/// ```
#[clippy::version = "1.83.0"]
#[clippy::version = "1.84.0"]
pub REGEX_CREATION_IN_LOOPS,
perf,
"regular expression compilation performed in a loop"

View file

@ -282,9 +282,9 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> {
}
{
let mut apa = AuxParamsAttr {
first_bind_ident: ident,
first_block_hir_id: self.ap.curr_block_hir_id,
first_block_span: self.ap.curr_block_span,
first_bind_ident: ident,
first_method_span: {
let expr_or_init = expr_or_init(self.cx, expr);
if let hir::ExprKind::MethodCall(_, local_expr, _, span) = expr_or_init.kind {
@ -395,8 +395,8 @@ impl Default for AuxParamsAttr {
counter: 0,
has_expensive_expr_after_last_attr: false,
first_block_hir_id: HirId::INVALID,
first_bind_ident: Ident::empty(),
first_block_span: DUMMY_SP,
first_bind_ident: Ident::empty(),
first_method_span: DUMMY_SP,
first_stmt_span: DUMMY_SP,
last_bind_ident: Ident::empty(),

View file

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::matching_root_macro_call;
use clippy_utils::sugg::Sugg;
use clippy_utils::{
@ -203,14 +203,18 @@ impl SlowVectorInit {
"len",
);
span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| {
diag.span_suggestion(
vec_alloc.allocation_expr.span.source_callsite(),
"consider replacing this with",
format!("vec![0; {len_expr}]"),
Applicability::Unspecified,
);
});
let span_to_replace = slow_fill
.span
.with_lo(vec_alloc.allocation_expr.span.source_callsite().lo());
span_lint_and_sugg(
cx,
SLOW_VECTOR_INITIALIZATION,
span_to_replace,
msg,
"consider replacing this with",
format!("vec![0; {len_expr}]"),
Applicability::Unspecified,
);
}
}

View file

@ -11,7 +11,7 @@ use clippy_utils::visitors::{Descend, for_each_expr};
use itertools::Itertools;
use rustc_ast::{BinOpKind, LitKind};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, PatKind};
use rustc_hir::{Expr, ExprKind, PatExprKind, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::impl_lint_pass;
@ -171,7 +171,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr<
return ControlFlow::Break(());
}
if arm.pat.walk_short(|pat| match pat.kind {
PatKind::Lit(expr) if let ExprKind::Lit(lit) = expr.kind => {
PatKind::Expr(expr) if let PatExprKind::Lit { lit, negated: false } = expr.kind => {
if let LitKind::Char(_) = lit.node {
set_char_spans.push(lit.span);
}

View file

@ -384,9 +384,9 @@ impl<'tcx> IndexBinding<'_, 'tcx> {
fn is_used_after_swap(&mut self, idx_ident: Ident) -> bool {
let mut v = IndexBindingVisitor {
found_used: false,
suggest_span: self.suggest_span,
idx: idx_ident,
suggest_span: self.suggest_span,
found_used: false,
};
for stmt in self.block.stmts {

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::has_repr_attr;
use clippy_utils::{has_repr_attr, is_in_test};
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
@ -37,7 +37,10 @@ declare_lint_pass!(TrailingEmptyArray => [TRAILING_EMPTY_ARRAY]);
impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.hir_id()) {
if is_struct_with_trailing_zero_sized_array(cx, item)
&& !has_repr_attr(cx, item.hir_id())
&& !is_in_test(cx.tcx, item.hir_id())
{
span_lint_and_help(
cx,
TRAILING_EMPTY_ARRAY,

View file

@ -30,17 +30,14 @@ pub(super) fn check<'tcx>(
| (ReducedTy::UnorderedFields(from_sub_ty), ReducedTy::OrderedFields(Some(to_sub_ty))) => {
from_ty = from_sub_ty;
to_ty = to_sub_ty;
continue;
},
(ReducedTy::OrderedFields(Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) if reduced_tys.to_fat_ptr => {
from_ty = from_sub_ty;
to_ty = to_sub_ty;
continue;
},
(ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(Some(to_sub_ty))) if reduced_tys.from_fat_ptr => {
from_ty = from_sub_ty;
to_ty = to_sub_ty;
continue;
},
// ptr <-> ptr
@ -50,7 +47,6 @@ pub(super) fn check<'tcx>(
{
from_ty = from_sub_ty;
to_ty = to_sub_ty;
continue;
},
// fat ptr <-> (*size, *size)

View file

@ -388,8 +388,8 @@ impl<'tcx> LateLintPass<'tcx> for Types {
self.check_fn_decl(cx, decl, CheckTyContext {
is_in_trait_impl,
is_exported,
in_body: matches!(fn_kind, FnKind::Closure),
is_exported,
..CheckTyContext::default()
});
}

View file

@ -47,7 +47,7 @@ declare_clippy_lint! {
/// }
/// }
/// ```
#[clippy::version = "1.83.0"]
#[clippy::version = "1.84.0"]
pub UNNECESSARY_LITERAL_BOUND,
pedantic,
"detects &str that could be &'static str in function return types"

View file

@ -92,7 +92,7 @@ impl EarlyLintPass for UnnestedOrPatterns {
}
fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) {
if let Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind {
if let Ident(.., None) | Expr(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind {
// This is a leaf pattern, so cloning is unprofitable.
return;
}
@ -228,7 +228,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<P<Pat>>, focus_idx: us
// Therefore they are not some form of constructor `C`,
// with which a pattern `C(p_0)` may be formed,
// which we would want to join with other `C(p_j)`s.
Ident(.., None) | Lit(_) | Wild | Err(_) | Never | Path(..) | Range(..) | Rest | MacCall(_)
Ident(.., None) | Expr(_) | Wild | Err(_) | Never | Path(..) | Range(..) | Rest | MacCall(_)
// Skip immutable refs, as grouping them saves few characters,
// and almost always requires adding parens (increasing noisiness).
// In the case of only two patterns, replacement adds net characters.

View file

@ -120,8 +120,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
let mut visitor = AsyncFnVisitor {
cx,
found_await: false,
async_depth: 0,
await_in_async_block: None,
async_depth: 0,
};
walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id);
if !visitor.found_await {
@ -129,9 +129,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
// The actual linting happens in `check_crate_post`, once we've found all
// uses of local async functions that do require asyncness to pass typeck
self.unused_async_fns.push(UnusedAsyncFn {
await_in_async_block: visitor.await_in_async_block,
fn_span: span,
def_id,
fn_span: span,
await_in_async_block: visitor.await_in_async_block,
});
}
}

View file

@ -245,9 +245,9 @@ impl<'tcx> UnwrappableVariablesVisitor<'_, 'tcx> {
let prev_len = self.unwrappables.len();
for unwrap_info in collect_unwrap_info(self.cx, if_expr, cond, branch, else_branch, true) {
let mut delegate = MutationVisitor {
tcx: self.cx.tcx,
is_mutated: false,
local_id: unwrap_info.local_id,
tcx: self.cx.tcx,
};
let vis = ExprUseVisitor::for_clippy(self.cx, cond.hir_id.owner.def_id, &mut delegate);
@ -397,8 +397,8 @@ impl<'tcx> LateLintPass<'tcx> for Unwrap {
}
let mut v = UnwrappableVariablesVisitor {
cx,
unwrappables: Vec::new(),
cx,
};
walk_fn(&mut v, kind, decl, body.id(), fn_id);

View file

@ -4,7 +4,7 @@ use rustc_ast::ast::{LitFloatType, LitKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{
self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind,
FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, StructTailExpr, TyKind,
FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, TyKind,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::declare_lint_pass;
@ -643,6 +643,27 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
self.expr(expr);
}
fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>) {
let kind = |kind| chain!(self, "let PatExprKind::{kind} = {lit}.kind");
macro_rules! kind {
($($t:tt)*) => (kind(format_args!($($t)*)));
}
match lit.value.kind {
PatExprKind::Lit { lit, negated } => {
bind!(self, lit);
bind!(self, negated);
kind!("Lit{{ref {lit}, {negated} }}");
self.lit(lit);
},
PatExprKind::ConstBlock(_) => kind!("ConstBlock(_)"),
PatExprKind::Path(ref qpath) => {
bind!(self, qpath);
kind!("Path(ref {qpath})");
self.qpath(qpath);
},
}
}
fn pat(&self, pat: &Binding<&hir::Pat<'_>>) {
let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind");
macro_rules! kind {
@ -712,16 +733,22 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
kind!("Ref({pat}, Mutability::{muta:?})");
self.pat(pat);
},
PatKind::Lit(lit_expr) => {
PatKind::Guard(pat, cond) => {
bind!(self, pat, cond);
kind!("Guard({pat}, {cond})");
self.pat(pat);
self.expr(cond);
},
PatKind::Expr(lit_expr) => {
bind!(self, lit_expr);
kind!("Lit({lit_expr})");
self.expr(lit_expr);
kind!("Expr({lit_expr})");
self.pat_expr(lit_expr);
},
PatKind::Range(start, end, end_kind) => {
opt_bind!(self, start, end);
kind!("Range({start}, {end}, RangeEnd::{end_kind:?})");
start.if_some(|e| self.expr(e));
end.if_some(|e| self.expr(e));
start.if_some(|e| self.pat_expr(e));
end.if_some(|e| self.pat_expr(e));
},
PatKind::Slice(start, middle, end) => {
bind!(self, start, end);

View file

@ -73,6 +73,7 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
SimplifiedType::Slice,
SimplifiedType::Str,
SimplifiedType::Bool,
SimplifiedType::Char,
]
.iter()
.flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter())

View file

@ -8,7 +8,7 @@ use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::is_copy;
use clippy_utils::visitors::for_each_local_use_after_expr;
use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method};
use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method, span_contains_comment};
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass};
@ -132,9 +132,19 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
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(),);
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| {
diag.span_suggestion(*span, help_msg, snippet, *applicability);
// 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);
});
}
}

View file

@ -166,8 +166,8 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush {
local_id: id,
init,
lhs_is_let: true,
name: name.name,
let_ty_span: local.ty.map(|ty| ty.span),
name: name.name,
err_span: local.span,
found: 0,
last_push_expr: init_expr.hir_id,
@ -206,8 +206,8 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush {
&& name.ident.as_str() == "push"
{
self.searcher = Some(VecPushSearcher {
found: searcher.found + 1,
err_span: searcher.err_span.to(stmt.span),
found: searcher.found + 1,
last_push_expr: expr.hir_id,
..searcher
});

View file

@ -248,8 +248,8 @@ impl Write {
pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self {
Self {
format_args,
allow_print_in_tests: conf.allow_print_in_tests,
in_debug_impl: false,
allow_print_in_tests: conf.allow_print_in_tests,
}
}
}

View file

@ -300,8 +300,8 @@ fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, cause: Caus
};
let mut vis = ExitPointFinder {
cx,
state: ExitPointState::WalkUpTo(spawn_expr.hir_id),
cx,
};
if let Break(ExitCallFound) = vis.visit_block(block) {
// Visitor found an unconditional `exit()` call, so don't lint.

View file

@ -1,7 +1,7 @@
[package]
name = "clippy_utils"
# begin autogenerated version
version = "0.1.85"
version = "0.1.86"
# end autogenerated version
edition = "2021"
description = "Helpful tools for writing lints, provided as they are used in Clippy"

View file

@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain:
<!-- begin autogenerated nightly -->
```
nightly-2024-12-26
nightly-2025-01-09
```
<!-- end autogenerated nightly -->

View file

@ -36,7 +36,7 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
(Paren(l), _) => eq_pat(l, r),
(_, Paren(r)) => eq_pat(l, r),
(Wild, Wild) | (Rest, Rest) => true,
(Lit(l), Lit(r)) => eq_expr(l, r),
(Expr(l), Expr(r)) => eq_expr(l, r),
(Ident(b1, i1, s1), Ident(b2, i2, s2)) => {
b1 == b2 && eq_id(*i1, *i2) && both(s1.as_deref(), s2.as_deref(), eq_pat)
},

View file

@ -4,7 +4,6 @@
//! executable MIR bodies, so we have to do this instead.
#![allow(clippy::float_cmp)]
use crate::macros::HirNode;
use crate::source::{SpanRangeExt, walk_span_to_context};
use crate::{clip, is_direct_expn_of, sext, unsext};
@ -13,7 +12,7 @@ use rustc_apfloat::ieee::{Half, Quad};
use rustc_ast::ast::{self, LitFloatType, LitKind};
use rustc_data_structures::sync::Lrc;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp, PatExpr, PatExprKind};
use rustc_lexer::tokenize;
use rustc_lint::LateContext;
use rustc_middle::mir::ConstValue;
@ -442,30 +441,48 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
}
}
pub fn eval_pat_expr(&self, pat_expr: &PatExpr<'_>) -> Option<Constant<'tcx>> {
match &pat_expr.kind {
PatExprKind::Lit { lit, negated } => {
let ty = self.typeck.node_type_opt(pat_expr.hir_id);
let val = lit_to_mir_constant(&lit.node, ty);
if *negated {
self.constant_negate(&val, ty?)
} else {
Some(val)
}
}
PatExprKind::ConstBlock(ConstBlock { body, ..}) => self.expr(self.tcx.hir().body(*body).value),
PatExprKind::Path(qpath) => self.qpath(qpath, pat_expr.hir_id),
}
}
fn qpath(&self, qpath: &QPath<'_>, hir_id: HirId) -> Option<Constant<'tcx>> {
let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, hir_id).opt_def_id() {
self.tcx.crate_name(def_id.krate) == sym::core
} else {
false
};
self.fetch_path_and_apply(qpath, hir_id, self.typeck.node_type(hir_id), |self_, result| {
let result = mir_to_const(self_.tcx, result)?;
// If source is already Constant we wouldn't want to override it with CoreConstant
self_.source.set(
if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) {
ConstantSource::CoreConstant
} else {
ConstantSource::Constant
},
);
Some(result)
})
}
/// Simple constant folding: Insert an expression, get a constant or none.
fn expr(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
match e.kind {
ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(body).value),
ExprKind::DropTemps(e) => self.expr(e),
ExprKind::Path(ref qpath) => {
let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, e.hir_id()).opt_def_id() {
self.tcx.crate_name(def_id.krate) == sym::core
} else {
false
};
self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| {
let result = mir_to_const(self_.tcx, result)?;
// If source is already Constant we wouldn't want to override it with CoreConstant
self_.source.set(
if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) {
ConstantSource::CoreConstant
} else {
ConstantSource::Constant
},
);
Some(result)
})
},
ExprKind::Path(ref qpath) => self.qpath(qpath, e.hir_id),
ExprKind::Block(block, _) => self.block(block),
ExprKind::Lit(lit) => {
if is_direct_expn_of(e.span, "cfg").is_some() {

View file

@ -196,8 +196,8 @@ impl<'hir> IfOrIfLet<'hir> {
if let ExprKind::DropTemps(new_cond) = cond.kind {
return Some(Self {
cond: new_cond,
r#else,
then,
r#else,
});
}
if let ExprKind::Let(..) = cond.kind {

View file

@ -9,8 +9,8 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::{
AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField,
ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName,
Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, TraitBoundModifiers, Ty,
TyKind,
Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr,
TraitBoundModifiers, Ty, TyKind,
};
use rustc_lexer::{TokenKind, tokenize};
use rustc_lint::LateContext;
@ -489,6 +489,24 @@ impl HirEqInterExpr<'_, '_, '_> {
li.name == ri.name && self.eq_pat(lp, rp)
}
fn eq_pat_expr(&mut self, left: &PatExpr<'_>, right: &PatExpr<'_>) -> bool {
match (&left.kind, &right.kind) {
(
&PatExprKind::Lit {
lit: left,
negated: left_neg,
},
&PatExprKind::Lit {
lit: right,
negated: right_neg,
},
) => left_neg == right_neg && left.node == right.node,
(PatExprKind::ConstBlock(left), PatExprKind::ConstBlock(right)) => self.eq_body(left.body, right.body),
(PatExprKind::Path(left), PatExprKind::Path(right)) => self.eq_qpath(left, right),
(PatExprKind::Lit { .. } | PatExprKind::ConstBlock(..) | PatExprKind::Path(..), _) => false,
}
}
/// Checks whether two patterns are the same.
fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
match (&left.kind, &right.kind) {
@ -507,11 +525,11 @@ impl HirEqInterExpr<'_, '_, '_> {
eq
},
(PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r),
(&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r),
(&PatKind::Expr(l), &PatKind::Expr(r)) => self.eq_pat_expr(l, r),
(&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)),
(&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_expr(a, b))
&& both(le.as_ref(), re.as_ref(), |a, b| self.eq_expr(a, b))
both(ls.as_ref(), rs.as_ref(), |a, b| self.eq_pat_expr(a, b))
&& both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_expr(a, b))
&& (li == ri)
},
(&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re),
@ -788,8 +806,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
Self {
cx,
maybe_typeck_results: cx.maybe_typeck_results(),
path_check: PathCheck::default(),
s: FxHasher::default(),
path_check: PathCheck::default(),
}
}
@ -1073,6 +1091,18 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
// self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
}
pub fn hash_pat_expr(&mut self, lit: &PatExpr<'_>) {
std::mem::discriminant(&lit.kind).hash(&mut self.s);
match &lit.kind {
PatExprKind::Lit { lit, negated } => {
lit.node.hash(&mut self.s);
negated.hash(&mut self.s);
},
PatExprKind::ConstBlock(c) => self.hash_body(c.body),
PatExprKind::Path(qpath) => self.hash_qpath(qpath),
}
}
pub fn hash_pat(&mut self, pat: &Pat<'_>) {
std::mem::discriminant(&pat.kind).hash(&mut self.s);
match pat.kind {
@ -1084,7 +1114,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
}
},
PatKind::Box(pat) | PatKind::Deref(pat) => self.hash_pat(pat),
PatKind::Lit(expr) => self.hash_expr(expr),
PatKind::Expr(expr) => self.hash_pat_expr(expr),
PatKind::Or(pats) => {
for pat in pats {
self.hash_pat(pat);
@ -1093,10 +1123,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
PatKind::Path(ref qpath) => self.hash_qpath(qpath),
PatKind::Range(s, e, i) => {
if let Some(s) = s {
self.hash_expr(s);
self.hash_pat_expr(s);
}
if let Some(e) = e {
self.hash_expr(e);
self.hash_pat_expr(e);
}
std::mem::discriminant(&i).hash(&mut self.s);
},
@ -1104,6 +1134,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_pat(pat);
std::mem::discriminant(&mu).hash(&mut self.s);
},
PatKind::Guard(pat, guard) => {
self.hash_pat(pat);
self.hash_expr(guard);
},
PatKind::Slice(l, m, r) => {
for pat in l {
self.hash_pat(pat);

View file

@ -1243,9 +1243,9 @@ pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'
let mut v = V {
cx,
allow_closure: true,
loops: Vec::new(),
locals: HirIdSet::default(),
allow_closure: true,
captures: HirIdMap::default(),
};
v.visit_expr(expr);
@ -1777,7 +1777,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
},
}
},
PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) => true,
PatKind::Expr(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) | PatKind::Guard(..) => true,
}
}

View file

@ -1,5 +1,5 @@
use rustc_hir::{Expr, HirId};
use rustc_index::bit_set::BitSet;
use rustc_index::bit_set::DenseBitSet;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{
BasicBlock, Body, InlineAsmOperand, Local, Location, Place, START_BLOCK, StatementKind, TerminatorKind, traversal,
@ -88,7 +88,7 @@ impl<'tcx> Visitor<'tcx> for V<'_> {
/// Checks if the block is part of a cycle
pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool {
let mut seen = BitSet::new_empty(body.basic_blocks.len());
let mut seen = DenseBitSet::new_empty(body.basic_blocks.len());
let mut to_visit = Vec::with_capacity(body.basic_blocks.len() / 2);
seen.insert(block);

View file

@ -2,7 +2,7 @@ use super::possible_origin::PossibleOriginVisitor;
use super::transitive_relation::TransitiveRelation;
use crate::ty::is_copy;
use rustc_data_structures::fx::FxHashMap;
use rustc_index::bit_set::BitSet;
use rustc_index::bit_set::DenseBitSet;
use rustc_lint::LateContext;
use rustc_middle::mir::visit::Visitor as _;
use rustc_middle::mir::{self, Mutability};
@ -21,19 +21,19 @@ struct PossibleBorrowerVisitor<'a, 'b, 'tcx> {
possible_borrower: TransitiveRelation,
body: &'b mir::Body<'tcx>,
cx: &'a LateContext<'tcx>,
possible_origin: FxHashMap<mir::Local, BitSet<mir::Local>>,
possible_origin: FxHashMap<mir::Local, DenseBitSet<mir::Local>>,
}
impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> {
fn new(
cx: &'a LateContext<'tcx>,
body: &'b mir::Body<'tcx>,
possible_origin: FxHashMap<mir::Local, BitSet<mir::Local>>,
possible_origin: FxHashMap<mir::Local, DenseBitSet<mir::Local>>,
) -> Self {
Self {
possible_borrower: TransitiveRelation::default(),
cx,
body,
cx,
possible_origin,
}
}
@ -56,7 +56,7 @@ impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> {
}
}
let bs = BitSet::new_empty(self.body.local_decls.len());
let bs = DenseBitSet::new_empty(self.body.local_decls.len());
PossibleBorrowerMap {
map,
maybe_live,
@ -119,7 +119,7 @@ impl<'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'_, '_, 'tcx> {
let mut mutable_variables: Vec<mir::Local> = mutable_borrowers
.iter()
.filter_map(|r| self.possible_origin.get(r))
.flat_map(BitSet::iter)
.flat_map(DenseBitSet::iter)
.collect();
if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() {
@ -171,10 +171,10 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
#[allow(clippy::module_name_repetitions)]
pub struct PossibleBorrowerMap<'b, 'tcx> {
/// Mapping `Local -> its possible borrowers`
pub map: FxHashMap<mir::Local, BitSet<mir::Local>>,
pub map: FxHashMap<mir::Local, DenseBitSet<mir::Local>>,
maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>,
// Caches to avoid allocation of `BitSet` on every query
pub bitset: (BitSet<mir::Local>, BitSet<mir::Local>),
// Caches to avoid allocation of `DenseBitSet` on every query
pub bitset: (DenseBitSet<mir::Local>, DenseBitSet<mir::Local>),
}
impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> {
@ -184,7 +184,7 @@ impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> {
vis.visit_body(mir);
vis.into_map(cx)
};
let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len())))
let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(DenseBitSet::new_empty(mir.local_decls.len())))
.iterate_to_fixpoint(cx.tcx, mir, Some("redundant_clone"))
.into_results_cursor(mir);
let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin);

View file

@ -1,7 +1,7 @@
use super::transitive_relation::TransitiveRelation;
use crate::ty::is_copy;
use rustc_data_structures::fx::FxHashMap;
use rustc_index::bit_set::BitSet;
use rustc_index::bit_set::DenseBitSet;
use rustc_lint::LateContext;
use rustc_middle::mir;
@ -22,7 +22,7 @@ impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> {
}
}
pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, BitSet<mir::Local>> {
pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, DenseBitSet<mir::Local>> {
let mut map = FxHashMap::default();
for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
if is_copy(cx, self.body.local_decls[row].ty) {

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