1
Fork 0

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

This commit is contained in:
Philipp Krones 2025-01-09 18:00:37 +01:00
commit b5bf09e57a
No known key found for this signature in database
GPG key ID: 1CA0DF2AF59D68A5
156 changed files with 2118 additions and 522 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,7 @@
avoid-breaking-exported-api = false
lint-inconsistent-struct-field-initializers = true
[[disallowed-methods]]
path = "rustc_lint::context::LintContext::lint"
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"

View file

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

View file

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

View file

@ -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);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,7 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{SpanRangeExt, snippet_with_context};
use clippy_utils::sugg::{Sugg, has_enclosing_paren};
use clippy_utils::ty::implements_trait;
use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, is_trait_method, peel_ref_operators};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
@ -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)
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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(

View file

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

View file

@ -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)

View file

@ -1,6 +1,6 @@
use super::REDUNDANT_PATTERN_MATCHING;
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, walk_span_to_context};
use clippy_utils::source::walk_span_to_context;
use clippy_utils::sugg::{Sugg, make_unop};
use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop};
use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures};
@ -274,7 +274,9 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
ExprKind::AddrOf(_, _, borrowed) => borrowed,
_ => op,
};
let mut sugg = format!("{}.{good_method}", snippet(cx, result_expr.span, "_"));
let mut app = Applicability::MachineApplicable;
let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_par();
let mut sugg = format!("{receiver_sugg}.{good_method}");
if let Some(guard) = maybe_guard {
// wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying!
@ -307,7 +309,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
format!("redundant pattern matching, consider using `{good_method}`"),
"try",
sugg,
Applicability::MachineApplicable,
app,
);
}
}

View file

@ -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) => {

View file

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

View file

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

View file

@ -1,8 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_expr_untyped_identity_function, is_trait_method};
use clippy_utils::{is_expr_untyped_identity_function, is_trait_method, path_to_local};
use rustc_ast::BindingMode;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::{self as hir, Node, PatKind};
use rustc_lint::LateContext;
use rustc_span::{Span, sym};
@ -24,6 +25,16 @@ pub(super) fn check(
&& is_expr_untyped_identity_function(cx, map_arg)
&& let Some(sugg_span) = expr.span.trim_start(caller.span)
{
// If the result of `.map(identity)` is used as a mutable reference,
// the caller must not be an immutable binding.
if cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr()
&& let Some(hir_id) = path_to_local(caller)
&& let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
&& !matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
{
return;
}
span_lint_and_sugg(
cx,
MAP_IDENTITY,

View file

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

View file

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

View file

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

View file

@ -1,6 +1,7 @@
use std::ops::ControlFlow;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::paths::STDIN;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::for_each_local_use_after_expr;
@ -33,7 +34,7 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool {
pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) {
if let Some(recv_adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
&& match_def_path(cx, recv_adt.did(), &["std", "io", "stdio", "Stdin"])
&& match_def_path(cx, recv_adt.did(), &STDIN)
&& let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind
&& let Res::Local(local_id) = path.res
{

View file

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

View file

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

View file

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

View file

@ -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;

View file

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

View file

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

View file

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

View file

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

View file

@ -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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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})");

View file

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

View file

@ -8,7 +8,7 @@ use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::is_copy;
use clippy_utils::visitors::for_each_local_use_after_expr;
use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method};
use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method, span_contains_comment};
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass};
@ -132,9 +132,19 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
for (span, lint_opt) in &self.span_to_lint_map {
if let Some((hir_id, suggest_slice, snippet, applicability)) = lint_opt {
let help_msg = format!("you can use {} directly", suggest_slice.desc(),);
let help_msg = format!("you can use {} directly", suggest_slice.desc());
span_lint_hir_and_then(cx, USELESS_VEC, *hir_id, *span, "useless use of `vec!`", |diag| {
diag.span_suggestion(*span, help_msg, snippet, *applicability);
// If the `vec!` macro contains comment, better not make the suggestion machine
// applicable as it would remove them.
let applicability = if *applicability != Applicability::Unspecified
&& let source_map = cx.tcx.sess.source_map()
&& span_contains_comment(source_map, *span)
{
Applicability::Unspecified
} else {
*applicability
};
diag.span_suggestion(*span, help_msg, snippet, applicability);
});
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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(),
}
}

View file

@ -1243,9 +1243,9 @@ pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'
let mut v = V {
cx,
allow_closure: true,
loops: Vec::new(),
locals: HirIdSet::default(),
allow_closure: true,
captures: HirIdMap::default(),
};
v.visit_expr(expr);
@ -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;

View file

@ -32,8 +32,8 @@ impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> {
) -> Self {
Self {
possible_borrower: TransitiveRelation::default(),
cx,
body,
cx,
possible_origin,
}
}

View file

@ -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")

View file

@ -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"];

View file

@ -94,8 +94,8 @@ impl ClippyWarning {
Some(Self {
name,
diag,
url,
krate: krate.to_string(),
url,
})
}

View file

@ -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,
}
}

View file

@ -59,7 +59,7 @@ fn explore_directory(dir: &Path) -> Vec<String> {
missing_files.push(path.to_str().unwrap().to_string());
}
},
_ => continue,
_ => {},
};
}
}

View file

@ -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

View file

@ -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`

View file

@ -0,0 +1 @@
lint-inconsistent-struct-field-initializers = true

View file

@ -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,
};
}
}

View file

@ -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,
};
}
}

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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

View file

@ -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()) };

View file

@ -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