Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
b5bf09e57a
156 changed files with 2118 additions and 522 deletions
3
.github/workflows/clippy_dev.yml
vendored
3
.github/workflows/clippy_dev.yml
vendored
|
@ -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
|
||||
|
|
27
.github/workflows/clippy_mq.yml
vendored
27
.github/workflows/clippy_mq.yml
vendored
|
@ -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 }}
|
||||
|
|
7
.github/workflows/clippy_pr.yml
vendored
7
.github/workflows/clippy_pr.yml
vendored
|
@ -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
|
||||
|
|
10
.github/workflows/deploy.yml
vendored
10
.github/workflows/deploy.yml
vendored
|
@ -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
|
||||
|
|
8
.github/workflows/lintcheck.yml
vendored
8
.github/workflows/lintcheck.yml
vendored
|
@ -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
|
||||
|
|
3
.github/workflows/remark.yml
vendored
3
.github/workflows/remark.yml
vendored
|
@ -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
|
||||
|
|
47
CHANGELOG.md
47
CHANGELOG.md
|
@ -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
|
||||
|
|
|
@ -71,3 +71,10 @@ harness = false
|
|||
[[test]]
|
||||
name = "dogfood"
|
||||
harness = false
|
||||
|
||||
# quine-mc_cluskey makes up a significant part of the runtime in dogfood
|
||||
# due to the number of conditions in the clippy_lints crate
|
||||
# and enabling optimizations for that specific dependency helps a bit
|
||||
# without increasing total build times.
|
||||
[profile.dev.package.quine-mc_cluskey]
|
||||
opt-level = 3
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
|
||||
|
|
|
@ -455,7 +455,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
|
|||
});
|
||||
|
||||
// Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl
|
||||
while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token == TokenKind::Ident) {
|
||||
while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) {
|
||||
let mut iter = iter
|
||||
.by_ref()
|
||||
.filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
|
||||
|
@ -465,7 +465,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
|
|||
// matches `!{`
|
||||
match_tokens!(iter, Bang OpenBrace);
|
||||
if let Some(LintDeclSearchResult { range, .. }) =
|
||||
iter.find(|result| result.token == TokenKind::CloseBrace)
|
||||
iter.find(|result| result.token_kind == TokenKind::CloseBrace)
|
||||
{
|
||||
last_decl_curly_offset = Some(range.end);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
|
|
@ -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(..)
|
||||
|
|
|
@ -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(),
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -620,18 +621,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)
|
||||
}
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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(÷nd_sugg_str) {
|
||||
format!(
|
||||
"{}{})",
|
||||
÷nd_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,
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -9,7 +9,7 @@ use rustc_ast::ast::RangeLimits;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd};
|
||||
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,7 +114,7 @@ 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 ty_sugg = get_ty_sugg(cx, arg);
|
||||
let range = check_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(
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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};
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -345,7 +345,7 @@ impl<'a> PatState<'a> {
|
|||
|
||||
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) => {
|
||||
|
|
41
clippy_lints/src/methods/double_ended_iterator_last.rs
Normal file
41
clippy_lints/src/methods/double_ended_iterator_last.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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})"),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
});
|
||||
},
|
||||
_ => (),
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -5,7 +5,8 @@ use clippy_utils::{is_trait_method, std_or_core};
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, GenericArgKind};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
use std::iter;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -96,10 +96,6 @@ impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> {
|
|||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::If(..) => {
|
||||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::Path(_) => {
|
||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
|
||||
if adj
|
||||
|
|
|
@ -1,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(),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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(&[]),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -717,7 +717,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
kind!("Guard({pat}, {cond})");
|
||||
self.pat(pat);
|
||||
self.expr(cond);
|
||||
}
|
||||
},
|
||||
PatKind::Lit(lit_expr) => {
|
||||
bind!(self, lit_expr);
|
||||
kind!("Lit({lit_expr})");
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -788,8 +788,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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
@ -1640,7 +1640,6 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool
|
|||
|
||||
/// Checks whether the given expression is a constant literal of the given value.
|
||||
pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
|
||||
// FIXME: use constant folding
|
||||
if let ExprKind::Lit(spanned) = expr.kind {
|
||||
if let LitKind::Int(v, _) = spanned.node {
|
||||
return v == value;
|
||||
|
|
|
@ -32,8 +32,8 @@ impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> {
|
|||
) -> Self {
|
||||
Self {
|
||||
possible_borrower: TransitiveRelation::default(),
|
||||
cx,
|
||||
body,
|
||||
cx,
|
||||
possible_origin,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ impl Msrv {
|
|||
let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym_msrv]));
|
||||
|
||||
if let Some(msrv_attr) = msrv_attrs.next() {
|
||||
if let Some(duplicate) = msrv_attrs.last() {
|
||||
if let Some(duplicate) = msrv_attrs.next_back() {
|
||||
sess.dcx()
|
||||
.struct_span_err(duplicate.span(), "`clippy::msrv` is defined multiple times")
|
||||
.with_span_note(msrv_attr.span(), "first definition found here")
|
||||
|
|
|
@ -33,6 +33,8 @@ pub const CHILD: [&str; 3] = ["std", "process", "Child"];
|
|||
pub const CHILD_ID: [&str; 4] = ["std", "process", "Child", "id"];
|
||||
pub const CHILD_KILL: [&str; 4] = ["std", "process", "Child", "kill"];
|
||||
pub const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"];
|
||||
pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "<impl char>", "is_ascii"];
|
||||
pub const STDIN: [&str; 4] = ["std", "io", "stdio", "Stdin"];
|
||||
|
||||
// Paths in clippy itself
|
||||
pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"];
|
||||
|
|
|
@ -94,8 +94,8 @@ impl ClippyWarning {
|
|||
Some(Self {
|
||||
name,
|
||||
diag,
|
||||
url,
|
||||
krate: krate.to_string(),
|
||||
url,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -574,12 +574,12 @@ impl LintMetadata {
|
|||
id_location: None,
|
||||
group: "deprecated",
|
||||
level: "none",
|
||||
version,
|
||||
docs: format!(
|
||||
"### What it does\n\n\
|
||||
Nothing. This lint has been deprecated\n\n\
|
||||
### Deprecation reason\n\n{reason}.\n",
|
||||
),
|
||||
version,
|
||||
applicability: Applicability::Unspecified,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ fn explore_directory(dir: &Path) -> Vec<String> {
|
|||
missing_files.push(path.to_str().unwrap().to_string());
|
||||
}
|
||||
},
|
||||
_ => continue,
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
thread '<unnamed>' panicked at clippy_lints/src/utils/internal_lints/produce_ice.rs:
|
||||
Would you like some help with that?
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
|
|
|
@ -19,8 +19,8 @@ LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]
|
|||
error: hardcoded path to a diagnostic item
|
||||
--> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:43
|
||||
|
|
||||
LL | const OPS_MOD: [&str; 5] = ["core", "ops"];
|
||||
| ^^^^^^^^^^^^^^^
|
||||
LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: convert all references to use `sym::deref_method`
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
lint-inconsistent-struct-field-initializers = true
|
|
@ -0,0 +1,79 @@
|
|||
#![warn(clippy::inconsistent_struct_constructor)]
|
||||
#![allow(clippy::redundant_field_names)]
|
||||
#![allow(clippy::unnecessary_operation)]
|
||||
#![allow(clippy::no_effect)]
|
||||
|
||||
#[derive(Default)]
|
||||
struct Foo {
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = 1;
|
||||
let y = 1;
|
||||
let z = 1;
|
||||
|
||||
Foo { x, y, z: z };
|
||||
|
||||
Foo {
|
||||
x,
|
||||
z: z,
|
||||
..Default::default()
|
||||
};
|
||||
}
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1859261645
|
||||
mod field_attributes {
|
||||
struct HirId;
|
||||
struct BodyVisitor {
|
||||
macro_unsafe_blocks: Vec<HirId>,
|
||||
expn_depth: u32,
|
||||
}
|
||||
fn check_body(condition: bool) {
|
||||
BodyVisitor {
|
||||
macro_unsafe_blocks: Vec::new(),
|
||||
#[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning
|
||||
expn_depth: if condition { 1 } else { 0 },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1874539800
|
||||
mod cfgs_between_fields {
|
||||
#[allow(clippy::non_minimal_cfg)]
|
||||
fn cfg_all() {
|
||||
struct S {
|
||||
a: i32,
|
||||
b: i32,
|
||||
#[cfg(all())]
|
||||
c: i32,
|
||||
d: i32,
|
||||
}
|
||||
let s = S {
|
||||
a: 3,
|
||||
b: 2,
|
||||
#[cfg(all())]
|
||||
c: 1,
|
||||
d: 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn cfg_any() {
|
||||
struct S {
|
||||
a: i32,
|
||||
b: i32,
|
||||
#[cfg(any())]
|
||||
c: i32,
|
||||
d: i32,
|
||||
}
|
||||
let s = S {
|
||||
a: 3,
|
||||
#[cfg(any())]
|
||||
c: 1,
|
||||
b: 2,
|
||||
d: 0,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
#![warn(clippy::inconsistent_struct_constructor)]
|
||||
#![allow(clippy::redundant_field_names)]
|
||||
#![allow(clippy::unnecessary_operation)]
|
||||
#![allow(clippy::no_effect)]
|
||||
|
||||
#[derive(Default)]
|
||||
struct Foo {
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = 1;
|
||||
let y = 1;
|
||||
let z = 1;
|
||||
|
||||
Foo { y, x, z: z };
|
||||
|
||||
Foo {
|
||||
z: z,
|
||||
x,
|
||||
..Default::default()
|
||||
};
|
||||
}
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1859261645
|
||||
mod field_attributes {
|
||||
struct HirId;
|
||||
struct BodyVisitor {
|
||||
macro_unsafe_blocks: Vec<HirId>,
|
||||
expn_depth: u32,
|
||||
}
|
||||
fn check_body(condition: bool) {
|
||||
BodyVisitor {
|
||||
#[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning
|
||||
expn_depth: if condition { 1 } else { 0 },
|
||||
macro_unsafe_blocks: Vec::new(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1874539800
|
||||
mod cfgs_between_fields {
|
||||
#[allow(clippy::non_minimal_cfg)]
|
||||
fn cfg_all() {
|
||||
struct S {
|
||||
a: i32,
|
||||
b: i32,
|
||||
#[cfg(all())]
|
||||
c: i32,
|
||||
d: i32,
|
||||
}
|
||||
let s = S {
|
||||
d: 0,
|
||||
#[cfg(all())]
|
||||
c: 1,
|
||||
b: 2,
|
||||
a: 3,
|
||||
};
|
||||
}
|
||||
|
||||
fn cfg_any() {
|
||||
struct S {
|
||||
a: i32,
|
||||
b: i32,
|
||||
#[cfg(any())]
|
||||
c: i32,
|
||||
d: i32,
|
||||
}
|
||||
let s = S {
|
||||
d: 0,
|
||||
#[cfg(any())]
|
||||
c: 1,
|
||||
b: 2,
|
||||
a: 3,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
error: struct constructor field order is inconsistent with struct definition field order
|
||||
--> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:18:11
|
||||
|
|
||||
LL | Foo { y, x, z: z };
|
||||
| ^^^^^^^^^^ help: if the field evaluation order doesn't matter, try: `x, y, z: z`
|
||||
|
|
||||
= note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::inconsistent_struct_constructor)]`
|
||||
|
||||
error: struct constructor field order is inconsistent with struct definition field order
|
||||
--> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:21:9
|
||||
|
|
||||
LL | / z: z,
|
||||
LL | | x,
|
||||
| |_________^
|
||||
|
|
||||
help: if the field evaluation order doesn't matter, try
|
||||
|
|
||||
LL ~ x,
|
||||
LL ~ z: z,
|
||||
|
|
||||
|
||||
error: struct constructor field order is inconsistent with struct definition field order
|
||||
--> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:36:13
|
||||
|
|
||||
LL | / #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning
|
||||
LL | | expn_depth: if condition { 1 } else { 0 },
|
||||
LL | | macro_unsafe_blocks: Vec::new(),
|
||||
| |___________________________________________^
|
||||
|
|
||||
help: if the field evaluation order doesn't matter, try
|
||||
|
|
||||
LL ~ macro_unsafe_blocks: Vec::new(),
|
||||
LL + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning
|
||||
LL ~ expn_depth: if condition { 1 } else { 0 },
|
||||
|
|
||||
|
||||
error: struct constructor field order is inconsistent with struct definition field order
|
||||
--> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:55:13
|
||||
|
|
||||
LL | / d: 0,
|
||||
LL | | #[cfg(all())]
|
||||
LL | | c: 1,
|
||||
LL | | b: 2,
|
||||
LL | | a: 3,
|
||||
| |________________^
|
||||
|
|
||||
help: if the field evaluation order doesn't matter, try
|
||||
|
|
||||
LL ~ a: 3,
|
||||
LL + b: 2,
|
||||
LL + #[cfg(all())]
|
||||
LL + c: 1,
|
||||
LL ~ d: 0,
|
||||
|
|
||||
|
||||
error: struct constructor field order is inconsistent with struct definition field order
|
||||
--> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:72:13
|
||||
|
|
||||
LL | / d: 0,
|
||||
LL | | #[cfg(any())]
|
||||
LL | | c: 1,
|
||||
LL | | b: 2,
|
||||
LL | | a: 3,
|
||||
| |________________^
|
||||
|
|
||||
help: if the field evaluation order doesn't matter, try
|
||||
|
|
||||
LL ~ a: 3,
|
||||
LL + #[cfg(any())]
|
||||
LL + c: 1,
|
||||
LL + b: 2,
|
||||
LL ~ d: 0,
|
||||
|
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
|
@ -46,6 +46,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
|
|||
future-size-threshold
|
||||
ignore-interior-mutability
|
||||
large-error-threshold
|
||||
lint-inconsistent-struct-field-initializers
|
||||
literal-representation-threshold
|
||||
matches-for-let-else
|
||||
max-fn-params-bools
|
||||
|
@ -134,6 +135,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
|
|||
future-size-threshold
|
||||
ignore-interior-mutability
|
||||
large-error-threshold
|
||||
lint-inconsistent-struct-field-initializers
|
||||
literal-representation-threshold
|
||||
matches-for-let-else
|
||||
max-fn-params-bools
|
||||
|
@ -222,6 +224,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
|
|||
future-size-threshold
|
||||
ignore-interior-mutability
|
||||
large-error-threshold
|
||||
lint-inconsistent-struct-field-initializers
|
||||
literal-representation-threshold
|
||||
matches-for-let-else
|
||||
max-fn-params-bools
|
||||
|
|
|
@ -16,4 +16,12 @@ fn main() {
|
|||
|
||||
let mut val_mut = 1;
|
||||
let _p_mut = std::ptr::addr_of_mut!(val_mut);
|
||||
|
||||
let mut x: [i32; 2] = [42, 43];
|
||||
let _raw = std::ptr::addr_of_mut!(x[1]).wrapping_offset(-1);
|
||||
}
|
||||
|
||||
fn issue_13882() {
|
||||
let mut x: [i32; 2] = [42, 43];
|
||||
let _raw = (&raw mut x[1]).wrapping_offset(-1);
|
||||
}
|
||||
|
|
|
@ -16,4 +16,12 @@ fn main() {
|
|||
|
||||
let mut val_mut = 1;
|
||||
let _p_mut = &mut val_mut as *mut i32;
|
||||
|
||||
let mut x: [i32; 2] = [42, 43];
|
||||
let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1);
|
||||
}
|
||||
|
||||
fn issue_13882() {
|
||||
let mut x: [i32; 2] = [42, 43];
|
||||
let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1);
|
||||
}
|
||||
|
|
|
@ -13,5 +13,17 @@ error: borrow as raw pointer
|
|||
LL | let _p_mut = &mut val_mut as *mut i32;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(val_mut)`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: borrow as raw pointer
|
||||
--> tests/ui/borrow_as_ptr.rs:21:16
|
||||
|
|
||||
LL | let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(x[1])`
|
||||
|
||||
error: borrow as raw pointer
|
||||
--> tests/ui/borrow_as_ptr.rs:26:17
|
||||
|
|
||||
LL | let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `&raw mut x[1]`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
|
|
@ -47,6 +47,17 @@ impl<T> std::ops::Deref for StaticRef<T> {
|
|||
}
|
||||
}
|
||||
|
||||
// ICE regression test
|
||||
mod issue12979 {
|
||||
use std::cell::UnsafeCell;
|
||||
|
||||
const ATOMIC_TUPLE: (Vec<UnsafeCell<u8>>, ()) = (Vec::new(), ());
|
||||
|
||||
fn main() {
|
||||
let _x = &ATOMIC_TUPLE.0;
|
||||
}
|
||||
}
|
||||
|
||||
// use a tuple to make sure referencing a field behind a pointer isn't linted.
|
||||
const CELL_REF: StaticRef<(UnsafeCell<u32>,)> = unsafe { StaticRef::new(std::ptr::null()) };
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:54:5
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:65:5
|
||||
|
|
||||
LL | ATOMIC.store(1, Ordering::SeqCst);
|
||||
| ^^^^^^
|
||||
|
@ -12,7 +12,7 @@ LL | #![deny(clippy::borrow_interior_mutable_const)]
|
|||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:55:16
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:66:16
|
||||
|
|
||||
LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5);
|
||||
| ^^^^^^
|
||||
|
@ -20,7 +20,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5);
|
|||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:58:22
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:69:22
|
||||
|
|
||||
LL | let _once_ref = &ONCE_INIT;
|
||||
| ^^^^^^^^^
|
||||
|
@ -28,7 +28,7 @@ LL | let _once_ref = &ONCE_INIT;
|
|||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:59:25
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:70:25
|
||||
|
|
||||
LL | let _once_ref_2 = &&ONCE_INIT;
|
||||
| ^^^^^^^^^
|
||||
|
@ -36,7 +36,7 @@ LL | let _once_ref_2 = &&ONCE_INIT;
|
|||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:60:27
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:71:27
|
||||
|
|
||||
LL | let _once_ref_4 = &&&&ONCE_INIT;
|
||||
| ^^^^^^^^^
|
||||
|
@ -44,7 +44,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT;
|
|||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:61:26
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:72:26
|
||||
|
|
||||
LL | let _once_mut = &mut ONCE_INIT;
|
||||
| ^^^^^^^^^
|
||||
|
@ -52,7 +52,7 @@ LL | let _once_mut = &mut ONCE_INIT;
|
|||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:72:14
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:83:14
|
||||
|
|
||||
LL | let _ = &ATOMIC_TUPLE;
|
||||
| ^^^^^^^^^^^^
|
||||
|
@ -60,7 +60,7 @@ LL | let _ = &ATOMIC_TUPLE;
|
|||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:73:14
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:84:14
|
||||
|
|
||||
LL | let _ = &ATOMIC_TUPLE.0;
|
||||
| ^^^^^^^^^^^^
|
||||
|
@ -68,7 +68,7 @@ LL | let _ = &ATOMIC_TUPLE.0;
|
|||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:74:19
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:85:19
|
||||
|
|
||||
LL | let _ = &(&&&&ATOMIC_TUPLE).0;
|
||||
| ^^^^^^^^^^^^
|
||||
|
@ -76,7 +76,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0;
|
|||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:75:14
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:86:14
|
||||
|
|
||||
LL | let _ = &ATOMIC_TUPLE.0[0];
|
||||
| ^^^^^^^^^^^^
|
||||
|
@ -84,7 +84,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0];
|
|||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:76:13
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:87:13
|
||||
|
|
||||
LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst);
|
||||
| ^^^^^^^^^^^^
|
||||
|
@ -92,7 +92,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst);
|
|||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:81:13
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:92:13
|
||||
|
|
||||
LL | let _ = ATOMIC_TUPLE.0[0];
|
||||
| ^^^^^^^^^^^^
|
||||
|
@ -100,7 +100,7 @@ LL | let _ = ATOMIC_TUPLE.0[0];
|
|||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:86:5
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:97:5
|
||||
|
|
||||
LL | CELL.set(2);
|
||||
| ^^^^
|
||||
|
@ -108,7 +108,7 @@ LL | CELL.set(2);
|
|||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:87:16
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:98:16
|
||||
|
|
||||
LL | assert_eq!(CELL.get(), 6);
|
||||
| ^^^^
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue