Auto merge of #108474 - Jarcho:clippyup, r=Manishearth

Update Clippy

Seems like `@flip1995` so this is a couple days late.

r? `@Manishearth`
This commit is contained in:
bors 2023-02-26 18:45:47 +00:00
commit 0966f59c78
131 changed files with 3025 additions and 630 deletions

View file

@ -4430,6 +4430,7 @@ Released 2018-09-13
[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
[`impl_trait_in_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_trait_in_params
[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
[`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
@ -4494,6 +4495,7 @@ Released 2018-09-13
[`let_underscore_future`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_future
[`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock
[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
[`let_underscore_untyped`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped
[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
@ -4620,6 +4622,7 @@ Released 2018-09-13
[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
[`no_effect_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_replace
[`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding
[`no_mangle_with_rust_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_mangle_with_rust_abi
[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty
@ -4675,6 +4678,7 @@ Released 2018-09-13
[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
[`question_mark_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark_used
[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
@ -4734,6 +4738,7 @@ Released 2018-09-13
[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee
[`significant_drop_tightening`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_tightening
[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names
@ -4764,6 +4769,7 @@ Released 2018-09-13
[`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops
[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
[`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting
[`suspicious_command_arg_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_command_arg_space
[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
@ -4790,6 +4796,7 @@ Released 2018-09-13
[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
[`transmute_int_to_non_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_non_zero
[`transmute_null_to_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_null_to_fn
[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr

View file

@ -19,21 +19,35 @@ You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the
| `clippy::complexity` | code that does something simple but in a complex way | **warn** |
| `clippy::perf` | code that can be written to run faster | **warn** |
| `clippy::pedantic` | lints which are rather strict or have occasional false positives | allow |
| `clippy::restriction` | lints which prevent the use of language and library features[^restrict] | allow |
| `clippy::nursery` | new lints that are still under development | allow |
| `clippy::cargo` | lints for the cargo manifest | allow |
More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also contains "restriction lints", which are
for things which are usually not considered "bad", but may be useful to turn on in specific cases. These should be used
very selectively, if at all.
The `restriction` category should, *emphatically*, not be enabled as a whole. The contained
lints may lint against perfectly reasonable code, may not have an alternative suggestion,
and may contradict any other lints (including other categories). Lints should be considered
on a case-by-case basis before enabling.
[^restrict]: Some use cases for `restriction` lints include:
- Strict coding styles (e.g. [`clippy::else_if_without_else`]).
- Additional restrictions on CI (e.g. [`clippy::todo`]).
- Preventing panicking in certain functions (e.g. [`clippy::unwrap_used`]).
- Running a lint only on a subset of code (e.g. `#[forbid(clippy::float_arithmetic)]` on a module).
[`clippy::else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
[`clippy::todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
[`clippy::unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
---
Table of contents:
* [Usage instructions](#usage)
* [Configuration](#configuration)
* [Contributing](#contributing)
* [License](#license)
* [Usage instructions](#usage)
* [Configuration](#configuration)
* [Contributing](#contributing)
* [License](#license)
## Usage
@ -64,6 +78,7 @@ Once you have rustup and the latest stable release (at least Rust 1.29) installe
```terminal
rustup component add clippy
```
If it says that it can't find the `clippy` component, please run `rustup self update`.
#### Step 3: Run Clippy
@ -143,16 +158,16 @@ line. (You can swap `clippy::all` with the specific lint category you are target
You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
* the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`).
* the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`).
Note that `rustc` has additional [lint groups](https://doc.rust-lang.org/rustc/lints/groups.html).
* all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`,
* all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`,
`#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
lints prone to false positives.
* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)
* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)
* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc.
* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc.
Note: `allow` means to suppress the lint for your code. With `warn` the lint
will only emit a warning, while with `deny` the lint will emit an error, when
@ -176,12 +191,14 @@ cargo clippy -- -W clippy::lint_name
This also works with lint groups. For example, you
can run Clippy with warnings for all lints enabled:
```terminal
cargo clippy -- -W clippy::pedantic
```
If you care only about a single lint, you can allow all others and then explicitly warn on
the lint(s) you are interested in:
```terminal
cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
```

View file

@ -28,6 +28,7 @@ repository. You can do this with:
```bash
# Assuming the current directory corresponds to the Rust repository
$ git checkout beta
# Make sure to change `your-github-name` to your github name in the following command
$ git subtree pull -p src/tools/clippy https://github.com/<your-github-name>/rust-clippy backport
$ ./x.py test src/tools/clippy
```

View file

@ -79,8 +79,7 @@ to be run inside the `rust` directory):
`rustup check`.
3. Sync the changes to the rust-copy of Clippy to your Clippy fork:
```bash
# Make sure to change `your-github-name` to your github name in the following command. Also be
# sure to either use a net-new branch, e.g. `sync-from-rust`, or delete the branch beforehand
# Be sure to either use a net-new branch, e.g. `sync-from-rust`, or delete the branch beforehand
# because changes cannot be fast forwarded and you have to run this command again.
git subtree push -P src/tools/clippy clippy-local sync-from-rust
```

View file

@ -53,6 +53,7 @@ Please use that command to update the file and do not edit it by hand.
| [ignore-interior-mutability](#ignore-interior-mutability) | `["bytes::Bytes"]` |
| [allow-mixed-uninlined-format-args](#allow-mixed-uninlined-format-args) | `true` |
| [suppress-restriction-lint-in-const](#suppress-restriction-lint-in-const) | `false` |
| [missing-docs-in-crate-items](#missing-docs-in-crate-items) | `false` |
### arithmetic-side-effects-allowed
Suppress checking of the passed type names in all types of operations.
@ -471,7 +472,7 @@ The maximum size of a file included via `include_bytes!()` or `include_str!()`,
### allow-expect-in-tests
Whether `expect` should be allowed within `#[cfg(test)]`
Whether `expect` should be allowed in test functions or `#[cfg(test)]`
**Default Value:** `false` (`bool`)
@ -479,7 +480,7 @@ Whether `expect` should be allowed within `#[cfg(test)]`
### allow-unwrap-in-tests
Whether `unwrap` should be allowed in test cfg
Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
**Default Value:** `false` (`bool`)
@ -487,7 +488,7 @@ Whether `unwrap` should be allowed in test cfg
### allow-dbg-in-tests
Whether `dbg!` should be allowed in test functions
Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
**Default Value:** `false` (`bool`)
@ -495,7 +496,7 @@ Whether `dbg!` should be allowed in test functions
### allow-print-in-tests
Whether print macros (ex. `println!`) should be allowed in test functions
Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]`
**Default Value:** `false` (`bool`)
@ -540,4 +541,13 @@ if no suggestion can be made.
* [indexing_slicing](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing)
### missing-docs-in-crate-items
Whether to **only** check for missing documentation in items visible within the current
crate. For example, `pub(crate)` items.
**Default Value:** `false` (`bool`)
* [missing_docs_in_private_items](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items)

View file

@ -1,5 +1,6 @@
use crate::clippy_project_root;
use indoc::{formatdoc, writedoc};
use std::fmt;
use std::fmt::Write as _;
use std::fs::{self, OpenOptions};
use std::io::prelude::*;
@ -256,7 +257,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
)
});
let _ = write!(result, "{}", get_lint_declaration(&name_upper, category));
let _: fmt::Result = write!(result, "{}", get_lint_declaration(&name_upper, category));
result.push_str(&if enable_msrv {
formatdoc!(
@ -353,7 +354,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
let mut lint_file_contents = String::new();
if enable_msrv {
let _ = writedoc!(
let _: fmt::Result = writedoc!(
lint_file_contents,
r#"
use clippy_utils::msrvs::{{self, Msrv}};
@ -373,7 +374,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
name_upper = name_upper,
);
} else {
let _ = writedoc!(
let _: fmt::Result = writedoc!(
lint_file_contents,
r#"
use rustc_lint::{{{context_import}, LintContext}};
@ -521,7 +522,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
.chain(std::iter::once(&*lint_name_upper))
.filter(|s| !s.is_empty())
{
let _ = write!(new_arr_content, "\n {ident},");
let _: fmt::Result = write!(new_arr_content, "\n {ident},");
}
new_arr_content.push('\n');

View file

@ -5,7 +5,7 @@ use itertools::Itertools;
use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
use std::collections::{HashMap, HashSet};
use std::ffi::OsStr;
use std::fmt::Write;
use std::fmt::{self, Write};
use std::fs::{self, OpenOptions};
use std::io::{self, Read, Seek, SeekFrom, Write as _};
use std::ops::Range;
@ -691,7 +691,7 @@ fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
let mut output = GENERATED_FILE_COMMENT.to_string();
output.push_str("{\n");
for lint in lints {
let _ = write!(
let _: fmt::Result = write!(
output,
concat!(
" store.register_removed(\n",
@ -726,7 +726,7 @@ fn gen_declared_lints<'a>(
if !is_public {
output.push_str(" #[cfg(feature = \"internal\")]\n");
}
let _ = writeln!(output, " crate::{module_name}::{lint_name}_INFO,");
let _: fmt::Result = writeln!(output, " crate::{module_name}::{lint_name}_INFO,");
}
output.push_str("];\n");

View file

@ -117,7 +117,8 @@ fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
) => {
if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id) &&
let Some(sig) = expr_sig(cx, path) &&
let Some(input) = sig.input(index)
let Some(input) = sig.input(index) &&
!cx.typeck_results().expr_ty_adjusted(expr).boxed_ty().is_trait()
{
input.no_bound_vars().is_some()
} else {

View file

@ -168,7 +168,7 @@ pub(super) fn check(
let suggestion = format!("{cast_to_snip}::try_from({name_of_cast_from})");
span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg, |diag| {
diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...");
diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...");
diag.span_suggestion_with_style(
expr.span,
"... or use `try_from` and handle the error accordingly",

View file

@ -179,6 +179,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO,
crate::from_str_radix_10::FROM_STR_RADIX_10_INFO,
crate::functions::DOUBLE_MUST_USE_INFO,
crate::functions::IMPL_TRAIT_IN_PARAMS_INFO,
crate::functions::MISNAMED_GETTERS_INFO,
crate::functions::MUST_USE_CANDIDATE_INFO,
crate::functions::MUST_USE_UNIT_INFO,
@ -224,6 +225,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::let_underscore::LET_UNDERSCORE_FUTURE_INFO,
crate::let_underscore::LET_UNDERSCORE_LOCK_INFO,
crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO,
crate::let_underscore::LET_UNDERSCORE_UNTYPED_INFO,
crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO,
crate::lifetimes::NEEDLESS_LIFETIMES_INFO,
crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO,
@ -378,6 +380,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::SKIP_WHILE_NEXT_INFO,
crate::methods::STABLE_SORT_PRIMITIVE_INFO,
crate::methods::STRING_EXTEND_CHARS_INFO,
crate::methods::SUSPICIOUS_COMMAND_ARG_SPACE_INFO,
crate::methods::SUSPICIOUS_MAP_INFO,
crate::methods::SUSPICIOUS_SPLITN_INFO,
crate::methods::SUSPICIOUS_TO_OWNED_INFO,
@ -447,6 +450,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::no_effect::NO_EFFECT_INFO,
crate::no_effect::NO_EFFECT_UNDERSCORE_BINDING_INFO,
crate::no_effect::UNNECESSARY_OPERATION_INFO,
crate::no_mangle_with_rust_abi::NO_MANGLE_WITH_RUST_ABI_INFO,
crate::non_copy_const::BORROW_INTERIOR_MUTABLE_CONST_INFO,
crate::non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST_INFO,
crate::non_expressive_names::JUST_UNDERSCORES_AND_DIGITS_INFO,
@ -506,6 +510,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO,
crate::pub_use::PUB_USE_INFO,
crate::question_mark::QUESTION_MARK_INFO,
crate::question_mark_used::QUESTION_MARK_USED_INFO,
crate::ranges::MANUAL_RANGE_CONTAINS_INFO,
crate::ranges::RANGE_MINUS_ONE_INFO,
crate::ranges::RANGE_PLUS_ONE_INFO,
@ -536,6 +541,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::shadow::SHADOW_REUSE_INFO,
crate::shadow::SHADOW_SAME_INFO,
crate::shadow::SHADOW_UNRELATED_INFO,
crate::significant_drop_tightening::SIGNIFICANT_DROP_TIGHTENING_INFO,
crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO,
crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO,
crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO,
@ -573,6 +579,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO,
crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO,
crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO,
crate::transmute::TRANSMUTE_INT_TO_NON_ZERO_INFO,
crate::transmute::TRANSMUTE_NULL_TO_FN_INFO,
crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO,
crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO,

View file

@ -3,7 +3,7 @@ use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exact
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
use clippy_utils::ty::{adt_and_variant_of_res, expr_sig, is_copy, peel_mid_ty_refs, ty_sig};
use clippy_utils::{
fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage,
};
@ -26,8 +26,8 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir::{Rvalue, StatementKind};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
use rustc_middle::ty::{
self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
ProjectionPredicate, Ty, TyCtxt, TypeVisitableExt, TypeckResults,
self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamEnv, ParamTy,
PredicateKind, ProjectionPredicate, Ty, TyCtxt, TypeVisitableExt, TypeckResults,
};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::sym, Span, Symbol};
@ -736,7 +736,7 @@ fn walk_parents<'tcx>(
..
}) if span.ctxt() == ctxt => {
let ty = cx.tcx.type_of(owner_id.def_id).subst_identity();
Some(ty_auto_deref_stability(cx, ty, precedence).position_for_result(cx))
Some(ty_auto_deref_stability(cx.tcx, cx.param_env, ty, precedence).position_for_result(cx))
},
Node::Item(&Item {
@ -760,7 +760,7 @@ fn walk_parents<'tcx>(
let output = cx
.tcx
.erase_late_bound_regions(cx.tcx.fn_sig(owner_id).subst_identity().output());
Some(ty_auto_deref_stability(cx, output, precedence).position_for_result(cx))
Some(ty_auto_deref_stability(cx.tcx, cx.param_env, output, precedence).position_for_result(cx))
},
Node::ExprField(field) if field.span.ctxt() == ctxt => match get_parent_expr_for_hir(cx, field.hir_id) {
@ -768,10 +768,23 @@ fn walk_parents<'tcx>(
hir_id,
kind: ExprKind::Struct(path, ..),
..
}) => variant_of_res(cx, cx.qpath_res(path, *hir_id))
.and_then(|variant| variant.fields.iter().find(|f| f.name == field.ident.name))
.map(|field_def| {
ty_auto_deref_stability(cx, cx.tcx.type_of(field_def.did).subst_identity(), precedence).position_for_arg()
}) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
.and_then(|(adt, variant)| {
variant
.fields
.iter()
.find(|f| f.name == field.ident.name)
.map(|f| (adt, f))
})
.map(|(adt, field_def)| {
ty_auto_deref_stability(
cx.tcx,
// Use the param_env of the target type.
cx.tcx.param_env(adt.did()),
cx.tcx.type_of(field_def.did).subst_identity(),
precedence,
)
.position_for_arg()
}),
_ => None,
},
@ -792,7 +805,7 @@ fn walk_parents<'tcx>(
let output = cx
.tcx
.erase_late_bound_regions(cx.tcx.fn_sig(owner_id).subst_identity().output());
ty_auto_deref_stability(cx, output, precedence).position_for_result(cx)
ty_auto_deref_stability(cx.tcx, cx.param_env, output, precedence).position_for_result(cx)
},
)
},
@ -835,15 +848,20 @@ fn walk_parents<'tcx>(
msrv,
)
} else {
ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
.position_for_arg()
ty_auto_deref_stability(
cx.tcx,
// Use the param_env of the target function.
sig.predicates_id().map_or(ParamEnv::empty(), |id| cx.tcx.param_env(id)),
cx.tcx.erase_late_bound_regions(ty),
precedence
).position_for_arg()
}
},
}
})
}),
ExprKind::MethodCall(method, receiver, args, _) => {
let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
let fn_id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
if receiver.hir_id == child_id {
// Check for calls to trait methods where the trait is implemented on a reference.
// Two cases need to be handled:
@ -852,13 +870,17 @@ fn walk_parents<'tcx>(
// priority.
if e.hir_id != child_id {
return Some(Position::ReborrowStable(precedence))
} else if let Some(trait_id) = cx.tcx.trait_of_item(id)
} else if let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
&& let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
&& let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
&& let subs = cx
.typeck_results()
.node_substs_opt(parent.hir_id).map(|subs| &subs[1..]).unwrap_or_default()
&& let impl_ty = if cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[0].is_ref() {
&& let impl_ty = if cx.tcx.fn_sig(fn_id)
.subst_identity()
.skip_binder()
.inputs()[0].is_ref()
{
// Trait methods taking `&self`
sub_ty
} else {
@ -879,10 +901,13 @@ fn walk_parents<'tcx>(
return Some(Position::MethodReceiver);
}
args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
let ty = cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i + 1];
let ty = cx.tcx.fn_sig(fn_id).subst_identity().input(i + 1);
// `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739
// `method.args.is_none()` for https://github.com/rust-lang/rust-clippy/issues/9782
if e.hir_id == child_id && method.args.is_none() && let ty::Param(param_ty) = ty.kind() {
if e.hir_id == child_id
&& method.args.is_none()
&& let ty::Param(param_ty) = ty.skip_binder().kind()
{
needless_borrow_impl_arg_position(
cx,
possible_borrowers,
@ -895,8 +920,10 @@ fn walk_parents<'tcx>(
)
} else {
ty_auto_deref_stability(
cx,
cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).subst_identity().input(i + 1)),
cx.tcx,
// Use the param_env of the target function.
cx.tcx.param_env(fn_id),
cx.tcx.erase_late_bound_regions(ty),
precedence,
)
.position_for_arg()
@ -1378,11 +1405,18 @@ impl<'tcx> TyPosition<'tcx> {
}
// Checks whether a type is stable when switching to auto dereferencing,
fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedence: i8) -> TyPosition<'tcx> {
fn ty_auto_deref_stability<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
ty: Ty<'tcx>,
precedence: i8,
) -> TyPosition<'tcx> {
let ty::Ref(_, mut ty, _) = *ty.kind() else {
return Position::Other(precedence).into();
};
ty = tcx.try_normalize_erasing_regions(param_env, ty).unwrap_or(ty);
loop {
break match *ty.kind() {
ty::Ref(_, ref_ty, _) => {
@ -1423,9 +1457,7 @@ fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedenc
| ty::Closure(..)
| ty::Never
| ty::Tuple(_)
| ty::Alias(ty::Projection, _) => {
Position::DerefStable(precedence, ty.is_sized(cx.tcx, cx.param_env.without_caller_bounds())).into()
},
| ty::Alias(ty::Projection, _) => Position::DerefStable(precedence, ty.is_sized(tcx, param_env)).into(),
};
}
}

View file

@ -6,6 +6,11 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty};
use if_chain::if_chain;
use itertools::Itertools;
use pulldown_cmark::Event::{
Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
};
use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options};
use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind};
use rustc_ast::token::CommentKind;
use rustc_data_structures::fx::FxHashSet;
@ -497,7 +502,6 @@ struct DocHeaders {
}
fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[Attribute]) -> Option<DocHeaders> {
use pulldown_cmark::{BrokenLink, CowStr, Options};
/// We don't want the parser to choke on intra doc links. Since we don't
/// actually care about rendering them, just pretend that all broken links are
/// point to a fake address.
@ -538,8 +542,6 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
pulldown_cmark::Parser::new_with_broken_link_callback(&doc, Options::empty(), Some(&mut cb)).into_offset_iter();
// Iterate over all `Events` and combine consecutive events into one
let events = parser.coalesce(|previous, current| {
use pulldown_cmark::Event::Text;
let previous_range = previous.1;
let current_range = current.1;
@ -564,12 +566,6 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
spans: &[(usize, Span)],
) -> DocHeaders {
// true if a safety header was found
use pulldown_cmark::Event::{
Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
};
use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
use pulldown_cmark::{CodeBlockKind, CowStr};
let mut headers = DocHeaders::default();
let mut in_code = false;
let mut in_link = None;
@ -660,6 +656,12 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
check_link_quotes(cx, in_link.is_some(), trimmed_text, span, &range, begin, text.len());
// Adjust for the beginning of the current `Event`
let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
if let Some(link) = in_link.as_ref()
&& let Ok(url) = Url::parse(link)
&& (url.scheme() == "https" || url.scheme() == "http") {
// Don't check the text associated with external URLs
continue;
}
text_to_check.push((text, span));
}
},
@ -704,10 +706,8 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
let filename = FileName::anon_source_code(&code);
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
let fallback_bundle = rustc_errors::fallback_fluent_bundle(
rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
false
);
let fallback_bundle =
rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
let emitter = EmitterWriter::new(
Box::new(io::sink()),
None,

View file

@ -6,7 +6,7 @@ use clippy_utils::{
source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context},
SpanlessEq,
};
use core::fmt::Write;
use core::fmt::{self, Write};
use rustc_errors::Applicability;
use rustc_hir::{
hir_id::HirIdSet,
@ -65,6 +65,10 @@ declare_lint_pass!(HashMapPass => [MAP_ENTRY]);
impl<'tcx> LateLintPass<'tcx> for HashMapPass {
#[expect(clippy::too_many_lines)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if expr.span.from_expansion() {
return;
}
let Some(higher::If { cond: cond_expr, then: then_expr, r#else: else_expr }) = higher::If::hir(expr) else {
return
};
@ -532,7 +536,7 @@ impl<'tcx> InsertSearchResults<'tcx> {
if is_expr_used_or_unified(cx.tcx, insertion.call) {
write_wrapped(&mut res, insertion, ctxt, app);
} else {
let _ = write!(
let _: fmt::Result = write!(
res,
"e.insert({})",
snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0
@ -548,7 +552,7 @@ impl<'tcx> InsertSearchResults<'tcx> {
(
self.snippet(cx, span, app, |res, insertion, ctxt, app| {
// Insertion into a map would return `Some(&mut value)`, but the entry returns `&mut value`
let _ = write!(
let _: fmt::Result = write!(
res,
"Some(e.insert({}))",
snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0
@ -562,7 +566,7 @@ impl<'tcx> InsertSearchResults<'tcx> {
(
self.snippet(cx, span, app, |res, insertion, ctxt, app| {
// Insertion into a map would return `None`, but the entry returns a mutable reference.
let _ = if is_expr_final_block_expr(cx.tcx, insertion.call) {
let _: fmt::Result = if is_expr_final_block_expr(cx.tcx, insertion.call) {
write!(
res,
"e.insert({});\n{}None",

View file

@ -4,12 +4,17 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_errors::MultiSpan;
use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor};
use rustc_hir::{
GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind, PredicateOrigin, Ty, TyKind, WherePredicate,
BodyId, ExprKind, GenericBound, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind,
PredicateOrigin, Ty, TyKind, WherePredicate,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::nested_filter;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{def_id::DefId, Span};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{
def_id::{DefId, LocalDefId},
Span,
};
declare_clippy_lint! {
/// ### What it does
@ -21,7 +26,6 @@ declare_clippy_lint! {
///
/// ### Example
/// ```rust
/// // unused type parameters
/// fn unused_ty<T>(x: u8) {
/// // ..
/// }
@ -37,13 +41,35 @@ declare_clippy_lint! {
complexity,
"unused type parameters in function definitions"
}
declare_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]);
pub struct ExtraUnusedTypeParameters {
avoid_breaking_exported_api: bool,
}
impl ExtraUnusedTypeParameters {
pub fn new(avoid_breaking_exported_api: bool) -> Self {
Self {
avoid_breaking_exported_api,
}
}
/// Don't lint external macros or functions with empty bodies. Also, don't lint public items if
/// the `avoid_breaking_exported_api` config option is set.
fn check_false_positive(&self, cx: &LateContext<'_>, span: Span, def_id: LocalDefId, body_id: BodyId) -> bool {
let body = cx.tcx.hir().body(body_id).value;
let fn_empty = matches!(&body.kind, ExprKind::Block(blk, None) if blk.stmts.is_empty() && blk.expr.is_none());
let is_exported = cx.effective_visibilities.is_exported(def_id);
in_external_macro(cx.sess(), span) || (self.avoid_breaking_exported_api && is_exported) || fn_empty
}
}
impl_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]);
/// A visitor struct that walks a given function and gathers generic type parameters, plus any
/// trait bounds those parameters have.
struct TypeWalker<'cx, 'tcx> {
cx: &'cx LateContext<'tcx>,
/// Collection of all the type parameters and their spans.
/// Collection of all the function's type parameters.
ty_params: FxHashMap<DefId, Span>,
/// Collection of any (inline) trait bounds corresponding to each type parameter.
bounds: FxHashMap<DefId, Span>,
@ -64,8 +90,8 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
.params
.iter()
.filter_map(|param| {
if let GenericParamKind::Type { .. } = param.kind {
Some((param.def_id.into(), param.span))
if let GenericParamKind::Type { synthetic, .. } = param.kind {
(!synthetic).then_some((param.def_id.into(), param.span))
} else {
if !param.is_elided_lifetime() {
all_params_unused = false;
@ -74,6 +100,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
}
})
.collect();
Self {
cx,
ty_params,
@ -83,6 +110,12 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
}
}
fn mark_param_used(&mut self, def_id: DefId) {
if self.ty_params.remove(&def_id).is_some() {
self.all_params_unused = false;
}
}
fn emit_lint(&self) {
let (msg, help) = match self.ty_params.len() {
0 => return,
@ -96,7 +129,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
),
};
let source_map = self.cx.tcx.sess.source_map();
let source_map = self.cx.sess().source_map();
let span = if self.all_params_unused {
self.generics.span.into() // Remove the entire list of generics
} else {
@ -118,14 +151,18 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
}
}
/// Given a generic bound, if the bound is for a trait that's not a `LangItem`, return the
/// `LocalDefId` for that trait.
fn bound_to_trait_def_id(bound: &GenericBound<'_>) -> Option<LocalDefId> {
bound.trait_ref()?.trait_def_id()?.as_local()
}
impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) {
if let Some((def_id, _)) = t.peel_refs().as_generic_param() {
if self.ty_params.remove(&def_id).is_some() {
self.all_params_unused = false;
}
self.mark_param_used(def_id);
} else if let TyKind::OpaqueDef(id, _, _) = t.kind {
// Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls
// `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're
@ -139,12 +176,21 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) {
if let WherePredicate::BoundPredicate(predicate) = predicate {
// Collect spans for bounds that appear in the list of generics (not in a where-clause)
// for use in forming the help message
if let Some((def_id, _)) = predicate.bounded_ty.peel_refs().as_generic_param()
&& let PredicateOrigin::GenericParam = predicate.origin
{
self.bounds.insert(def_id, predicate.span);
// Collect spans for any bounds on type parameters. We only keep bounds that appear in
// the list of generics (not in a where-clause).
if let Some((def_id, _)) = predicate.bounded_ty.peel_refs().as_generic_param() {
// If the bound contains non-public traits, err on the safe side and don't lint the
// corresponding parameter.
if !predicate
.bounds
.iter()
.filter_map(bound_to_trait_def_id)
.all(|id| self.cx.effective_visibilities.is_exported(id))
{
self.mark_param_used(def_id);
} else if let PredicateOrigin::GenericParam = predicate.origin {
self.bounds.insert(def_id, predicate.span);
}
}
// Only walk the right-hand side of where-bounds
for bound in predicate.bounds {
@ -160,7 +206,9 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::Fn(_, generics, _) = item.kind {
if let ItemKind::Fn(_, generics, body_id) = item.kind
&& !self.check_false_positive(cx, item.span, item.owner_id.def_id, body_id)
{
let mut walker = TypeWalker::new(cx, generics);
walk_item(&mut walker, item);
walker.emit_lint();
@ -169,7 +217,10 @@ impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters {
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) {
// Only lint on inherent methods, not trait methods.
if let ImplItemKind::Fn(..) = item.kind && trait_ref_of_method(cx, item.owner_id.def_id).is_none() {
if let ImplItemKind::Fn(.., body_id) = item.kind
&& trait_ref_of_method(cx, item.owner_id.def_id).is_none()
&& !self.check_false_positive(cx, item.span, item.owner_id.def_id, body_id)
{
let mut walker = TypeWalker::new(cx, item.generics);
walk_impl_item(&mut walker, item);
walker.emit_lint();

View file

@ -340,6 +340,7 @@ fn check_one_arg(
if matches!(param.kind, Implicit | Starred | Named(_) | Numbered)
&& let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind
&& let [segment] = path.segments
&& segment.args.is_none()
&& let Some(arg_span) = args.value_with_prev_comma_span(param.value.hir_id)
{
let replacement = match param.usage {

View file

@ -0,0 +1,50 @@
use clippy_utils::{diagnostics::span_lint_and_then, is_in_test_function};
use rustc_hir::{intravisit::FnKind, Body, HirId};
use rustc_lint::LateContext;
use rustc_span::Span;
use super::IMPL_TRAIT_IN_PARAMS;
pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: &'tcx Body<'_>, hir_id: HirId) {
if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public() && !is_in_test_function(cx.tcx, hir_id)
{
if let FnKind::ItemFn(ident, generics, _) = kind {
for param in generics.params {
if param.is_impl_trait() {
// No generics with nested generics, and no generics like FnMut(x)
span_lint_and_then(
cx,
IMPL_TRAIT_IN_PARAMS,
param.span,
"'`impl Trait` used as a function parameter'",
|diag| {
if let Some(gen_span) = generics.span_for_param_suggestion() {
diag.span_suggestion_with_style(
gen_span,
"add a type paremeter",
format!(", {{ /* Generic name */ }}: {}", &param.name.ident().as_str()[5..]),
rustc_errors::Applicability::HasPlaceholders,
rustc_errors::SuggestionStyle::ShowAlways,
);
} else {
diag.span_suggestion_with_style(
Span::new(
body.params[0].span.lo() - rustc_span::BytePos(1),
ident.span.hi(),
ident.span.ctxt(),
ident.span.parent(),
),
"add a type paremeter",
format!("<{{ /* Generic name */ }}: {}>", &param.name.ident().as_str()[5..]),
rustc_errors::Applicability::HasPlaceholders,
rustc_errors::SuggestionStyle::ShowAlways,
);
}
},
);
}
}
}
}
}

View file

@ -1,3 +1,4 @@
mod impl_trait_in_params;
mod misnamed_getters;
mod must_use;
mod not_unsafe_ptr_arg_deref;
@ -327,6 +328,32 @@ declare_clippy_lint! {
"getter method returning the wrong field"
}
declare_clippy_lint! {
/// ### What it does
/// Lints when `impl Trait` is being used in a function's paremeters.
/// ### Why is this bad?
/// Turbofish syntax (`::<>`) cannot be used when `impl Trait` is being used, making `impl Trait` less powerful. Readability may also be a factor.
///
/// ### Example
/// ```rust
/// trait MyTrait {}
/// fn foo(a: impl MyTrait) {
/// // [...]
/// }
/// ```
/// Use instead:
/// ```rust
/// trait MyTrait {}
/// fn foo<T: MyTrait>(a: T) {
/// // [...]
/// }
/// ```
#[clippy::version = "1.68.0"]
pub IMPL_TRAIT_IN_PARAMS,
restriction,
"`impl Trait` is used in the function's parameters"
}
#[derive(Copy, Clone)]
pub struct Functions {
too_many_arguments_threshold: u64,
@ -354,6 +381,7 @@ impl_lint_pass!(Functions => [
RESULT_UNIT_ERR,
RESULT_LARGE_ERR,
MISNAMED_GETTERS,
IMPL_TRAIT_IN_PARAMS,
]);
impl<'tcx> LateLintPass<'tcx> for Functions {
@ -371,6 +399,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
too_many_lines::check_fn(cx, kind, span, body, self.too_many_lines_threshold);
not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, def_id);
misnamed_getters::check_fn(cx, kind, decl, body, span);
impl_trait_in_params::check_fn(cx, &kind, body, hir_id);
}
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {

View file

@ -7,7 +7,7 @@ use rustc_hir::{self as hir, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Symbol;
use std::fmt::Write as _;
use std::fmt::{self, Write as _};
declare_clippy_lint! {
/// ### What it does
@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
let mut fields_snippet = String::new();
let (last_ident, idents) = ordered_fields.split_last().unwrap();
for ident in idents {
let _ = write!(fields_snippet, "{ident}, ");
let _: fmt::Result = write!(fields_snippet, "{ident}, ");
}
fields_snippet.push_str(&last_ident.to_string());

View file

@ -90,7 +90,45 @@ declare_clippy_lint! {
"non-binding `let` on a future"
}
declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE]);
declare_clippy_lint! {
/// ### What it does
/// Checks for `let _ = <expr>` without a type annotation, and suggests to either provide one,
/// or remove the `let` keyword altogether.
///
/// ### Why is this bad?
/// The `let _ = <expr>` expression ignores the value of `<expr>` but will remain doing so even
/// if the type were to change, thus potentially introducing subtle bugs. By supplying a type
/// annotation, one will be forced to re-visit the decision to ignore the value in such cases.
///
/// ### Known problems
/// The `_ = <expr>` is not properly supported by some tools (e.g. IntelliJ) and may seem odd
/// to many developers. This lint also partially overlaps with the other `let_underscore_*`
/// lints.
///
/// ### Example
/// ```rust
/// fn foo() -> Result<u32, ()> {
/// Ok(123)
/// }
/// let _ = foo();
/// ```
/// Use instead:
/// ```rust
/// fn foo() -> Result<u32, ()> {
/// Ok(123)
/// }
/// // Either provide a type annotation:
/// let _: Result<u32, ()> = foo();
/// // …or drop the let keyword:
/// _ = foo();
/// ```
#[clippy::version = "1.69.0"]
pub LET_UNDERSCORE_UNTYPED,
pedantic,
"non-binding `let` without a type annotation"
}
declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE, LET_UNDERSCORE_UNTYPED]);
const SYNC_GUARD_PATHS: [&[&str]; 3] = [
&paths::PARKING_LOT_MUTEX_GUARD,
@ -148,6 +186,18 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
"consider explicitly using function result",
);
}
if local.pat.default_binding_modes && local.ty.is_none() {
// When `default_binding_modes` is true, the `let` keyword is present.
span_lint_and_help(
cx,
LET_UNDERSCORE_UNTYPED,
local.span,
"non-binding `let` without a type annotation",
None,
"consider adding a type annotation or removing the `let` keyword",
);
}
}
}
}

View file

@ -2,6 +2,7 @@
#![feature(binary_heap_into_iter_sorted)]
#![feature(box_patterns)]
#![feature(drain_filter)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![feature(let_chains)]
#![feature(lint_reasons)]
@ -219,6 +220,7 @@ mod neg_cmp_op_on_partial_ord;
mod neg_multiply;
mod new_without_default;
mod no_effect;
mod no_mangle_with_rust_abi;
mod non_copy_const;
mod non_expressive_names;
mod non_octal_unix_permissions;
@ -243,6 +245,7 @@ mod ptr;
mod ptr_offset_with_cast;
mod pub_use;
mod question_mark;
mod question_mark_used;
mod ranges;
mod rc_clone_in_vec_init;
mod read_zero_byte_vec;
@ -264,6 +267,7 @@ mod semicolon_block;
mod semicolon_if_nothing_returned;
mod serde_api;
mod shadow;
mod significant_drop_tightening;
mod single_char_lifetime_names;
mod single_component_path_imports;
mod size_of_in_element_count;
@ -559,6 +563,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction));
store.register_late_pass(|_| Box::new(mut_mut::MutMut));
store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed));
store.register_late_pass(|_| Box::<significant_drop_tightening::SignificantDropTightening<'_>>::default());
store.register_late_pass(|_| Box::new(len_zero::LenZero));
store.register_late_pass(|_| Box::new(attrs::Attributes));
store.register_late_pass(|_| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
@ -665,12 +670,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
))
});
let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
let missing_docs_in_crate_items = conf.missing_docs_in_crate_items;
store.register_late_pass(move |_| Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply));
store.register_late_pass(|_| Box::new(mem_forget::MemForget));
store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq));
store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
store.register_late_pass(|_| Box::new(missing_doc::MissingDoc::new()));
store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(missing_docs_in_crate_items)));
store.register_late_pass(|_| Box::new(missing_inline::MissingInline));
store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems));
store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk));
@ -694,6 +700,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher));
store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom));
store.register_late_pass(|_| Box::new(question_mark::QuestionMark));
store.register_late_pass(|_| Box::new(question_mark_used::QuestionMarkUsed));
store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl));
store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit));
@ -911,7 +918,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse));
store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef));
store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock));
store.register_late_pass(|_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters));
store.register_late_pass(move |_| {
Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters::new(
avoid_breaking_exported_api,
))
});
store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View file

@ -144,6 +144,10 @@ fn check_fn_inner<'tcx>(
.filter(|param| matches!(param.kind, GenericParamKind::Type { .. }));
for typ in types {
if !typ.span.eq_ctxt(span) {
return;
}
for pred in generics.bounds_for_param(typ.def_id) {
if pred.origin == PredicateOrigin::WhereClause {
// has_where_lifetimes checked that this predicate contains no lifetime.
@ -181,6 +185,10 @@ fn check_fn_inner<'tcx>(
}
if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params) {
if usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span)) {
return;
}
let lts = elidable_lts
.iter()
// In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a

View file

@ -210,7 +210,7 @@ impl WarningType {
cx,
UNUSUAL_BYTE_GROUPINGS,
span,
"digits of hex or binary literal not grouped by four",
"digits of hex, binary or octal literal not in groups of equal size",
"consider",
suggested_format,
Applicability::MachineApplicable,
@ -427,8 +427,12 @@ impl LiteralDigitGrouping {
let first = groups.next().expect("At least one group");
if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i != 4 && i != 2) {
return Err(WarningType::UnusualByteGroupings);
if radix == Radix::Binary || radix == Radix::Octal || radix == Radix::Hexadecimal {
if let Some(second_size) = groups.next() {
if !groups.all(|i| i == second_size) || first > second_size {
return Err(WarningType::UnusualByteGroupings);
}
}
}
if let Some(second) = groups.next() {
@ -484,7 +488,7 @@ impl DecimalLiteralRepresentation {
then {
let hex = format!("{val:#X}");
let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false);
let _ = Self::do_lint(num_lit.integer).map_err(|warning_type| {
let _: Result<(), ()> = Self::do_lint(num_lit.integer).map_err(|warning_type| {
warning_type.display(num_lit.format(), cx, span);
});
}

View file

@ -39,6 +39,7 @@ pub(super) fn check(
});
},
NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
NeverLoopResult::IgnoreUntilEnd(_) => unreachable!(),
}
}
@ -48,6 +49,8 @@ enum NeverLoopResult {
AlwaysBreak,
// A continue may occur for the main loop.
MayContinueMainLoop,
// Ignore everything until the end of the block with this id
IgnoreUntilEnd(HirId),
Otherwise,
}
@ -56,6 +59,7 @@ fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult {
match arg {
NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise,
NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop,
NeverLoopResult::IgnoreUntilEnd(id) => NeverLoopResult::IgnoreUntilEnd(id),
}
}
@ -63,27 +67,26 @@ fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult {
#[must_use]
fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult {
match first {
NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop => first,
NeverLoopResult::Otherwise => second,
}
}
// Combine two results where both parts are called but not necessarily in order.
#[must_use]
fn combine_both(left: NeverLoopResult, right: NeverLoopResult) -> NeverLoopResult {
match (left, right) {
(NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
NeverLoopResult::MayContinueMainLoop
NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop | NeverLoopResult::IgnoreUntilEnd(_) => {
first
},
(NeverLoopResult::AlwaysBreak, _) | (_, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
(NeverLoopResult::Otherwise, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
NeverLoopResult::Otherwise => second,
}
}
// Combine two results where only one of the part may have been executed.
#[must_use]
fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult {
fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult, ignore_ids: &[HirId]) -> NeverLoopResult {
match (b1, b2) {
(NeverLoopResult::IgnoreUntilEnd(a), NeverLoopResult::IgnoreUntilEnd(b)) => {
if ignore_ids.iter().find(|&e| e == &a || e == &b).unwrap() == &a {
NeverLoopResult::IgnoreUntilEnd(b)
} else {
NeverLoopResult::IgnoreUntilEnd(a)
}
},
(i @ NeverLoopResult::IgnoreUntilEnd(_), NeverLoopResult::AlwaysBreak)
| (NeverLoopResult::AlwaysBreak, i @ NeverLoopResult::IgnoreUntilEnd(_)) => i,
(NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
(NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
NeverLoopResult::MayContinueMainLoop
@ -103,7 +106,7 @@ fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id
let e = never_loop_expr(e, ignore_ids, main_loop_id);
// els is an else block in a let...else binding
els.map_or(e, |els| {
combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id))
combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id), ignore_ids)
})
})
.fold(NeverLoopResult::Otherwise, combine_seq)
@ -139,7 +142,7 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
ExprKind::Struct(_, fields, base) => {
let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id);
if let Some(base) = base {
combine_both(fields, never_loop_expr(base, ignore_ids, main_loop_id))
combine_seq(fields, never_loop_expr(base, ignore_ids, main_loop_id))
} else {
fields
}
@ -159,7 +162,7 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| {
never_loop_expr(e, ignore_ids, main_loop_id)
});
combine_seq(e1, combine_branches(e2, e3))
combine_seq(e1, combine_branches(e2, e3, ignore_ids))
},
ExprKind::Match(e, arms, _) => {
let e = never_loop_expr(e, ignore_ids, main_loop_id);
@ -175,8 +178,13 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
ignore_ids.push(b.hir_id);
}
let ret = never_loop_block(b, ignore_ids, main_loop_id);
ignore_ids.pop();
ret
if l.is_some() {
ignore_ids.pop();
}
match ret {
NeverLoopResult::IgnoreUntilEnd(a) if a == b.hir_id => NeverLoopResult::Otherwise,
_ => ret,
}
},
ExprKind::Continue(d) => {
let id = d
@ -190,8 +198,8 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
},
// checks if break targets a block instead of a loop
ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e
.map_or(NeverLoopResult::Otherwise, |e| {
combine_seq(never_loop_expr(e, ignore_ids, main_loop_id), NeverLoopResult::Otherwise)
.map_or(NeverLoopResult::IgnoreUntilEnd(t), |e| {
never_loop_expr(e, ignore_ids, main_loop_id)
}),
ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
combine_seq(
@ -218,7 +226,7 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
| InlineAsmOperand::SymFn { .. }
| InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise,
})
.fold(NeverLoopResult::Otherwise, combine_both),
.fold(NeverLoopResult::Otherwise, combine_seq),
ExprKind::Yield(_, _)
| ExprKind::Closure { .. }
| ExprKind::Path(_)
@ -234,7 +242,7 @@ fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(
main_loop_id: HirId,
) -> NeverLoopResult {
es.map(|e| never_loop_expr(e, ignore_ids, main_loop_id))
.fold(NeverLoopResult::Otherwise, combine_both)
.fold(NeverLoopResult::Otherwise, combine_seq)
}
fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(
@ -242,8 +250,9 @@ fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(
ignore_ids: &mut Vec<HirId>,
main_loop_id: HirId,
) -> NeverLoopResult {
e.map(|e| never_loop_expr(e, ignore_ids, main_loop_id))
.fold(NeverLoopResult::AlwaysBreak, combine_branches)
e.fold(NeverLoopResult::AlwaysBreak, |a, b| {
combine_branches(a, never_loop_expr(b, ignore_ids, main_loop_id), ignore_ids)
})
}
fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) -> String {

View file

@ -4,11 +4,12 @@ use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::peel_blocks;
use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::{for_each_expr, Descend};
use clippy_utils::visitors::{Descend, Visitable};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind};
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{Expr, ExprKind, HirId, ItemId, Local, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind, Ty};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
@ -115,6 +116,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
.enumerate()
.find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types));
let Some((idx, diverging_arm)) = diverging_arm_opt else { return; };
// If the non-diverging arm is the first one, its pattern can be reused in a let/else statement.
// However, if it arrives in second position, its pattern may cover some cases already covered
// by the diverging one.
// TODO: accept the non-diverging arm as a second position if patterns are disjointed.
if idx == 0 {
return;
}
let pat_arm = &arms[1 - idx];
if !expr_is_simple_identity(pat_arm.pat, pat_arm.body) {
return;
@ -162,61 +170,102 @@ fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat:
);
}
fn expr_diverges(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
fn is_never(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
if let Some(ty) = cx.typeck_results().expr_ty_opt(expr) {
return ty.is_never();
}
false
/// Check whether an expression is divergent. May give false negatives.
fn expr_diverges(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
struct V<'cx, 'tcx> {
cx: &'cx LateContext<'tcx>,
res: ControlFlow<(), Descend>,
}
// We can't just call is_never on expr and be done, because the type system
// sometimes coerces the ! type to something different before we can get
// our hands on it. So instead, we do a manual search. We do fall back to
// is_never in some places when there is no better alternative.
for_each_expr(expr, |ex| {
match ex.kind {
ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => ControlFlow::Break(()),
ExprKind::Call(call, _) => {
if is_never(cx, ex) || is_never(cx, call) {
return ControlFlow::Break(());
impl<'tcx> Visitor<'tcx> for V<'_, '_> {
fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
fn is_never(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
if let Some(ty) = cx.typeck_results().expr_ty_opt(expr) {
return ty.is_never();
}
ControlFlow::Continue(Descend::Yes)
},
ExprKind::MethodCall(..) => {
if is_never(cx, ex) {
return ControlFlow::Break(());
}
ControlFlow::Continue(Descend::Yes)
},
ExprKind::If(if_expr, if_then, if_else) => {
let else_diverges = if_else.map_or(false, |ex| expr_diverges(cx, ex));
let diverges = expr_diverges(cx, if_expr) || (else_diverges && expr_diverges(cx, if_then));
if diverges {
return ControlFlow::Break(());
}
ControlFlow::Continue(Descend::No)
},
ExprKind::Match(match_expr, match_arms, _) => {
let diverges = expr_diverges(cx, match_expr)
|| match_arms.iter().all(|arm| {
let guard_diverges = arm.guard.as_ref().map_or(false, |g| expr_diverges(cx, g.body()));
guard_diverges || expr_diverges(cx, arm.body)
});
if diverges {
return ControlFlow::Break(());
}
ControlFlow::Continue(Descend::No)
},
false
}
// Don't continue into loops or labeled blocks, as they are breakable,
// and we'd have to start checking labels.
ExprKind::Block(_, Some(_)) | ExprKind::Loop(..) => ControlFlow::Continue(Descend::No),
if self.res.is_break() {
return;
}
// Default: descend
_ => ControlFlow::Continue(Descend::Yes),
// We can't just call is_never on expr and be done, because the type system
// sometimes coerces the ! type to something different before we can get
// our hands on it. So instead, we do a manual search. We do fall back to
// is_never in some places when there is no better alternative.
self.res = match e.kind {
ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => ControlFlow::Break(()),
ExprKind::Call(call, _) => {
if is_never(self.cx, e) || is_never(self.cx, call) {
ControlFlow::Break(())
} else {
ControlFlow::Continue(Descend::Yes)
}
},
ExprKind::MethodCall(..) => {
if is_never(self.cx, e) {
ControlFlow::Break(())
} else {
ControlFlow::Continue(Descend::Yes)
}
},
ExprKind::If(if_expr, if_then, if_else) => {
let else_diverges = if_else.map_or(false, |ex| expr_diverges(self.cx, ex));
let diverges =
expr_diverges(self.cx, if_expr) || (else_diverges && expr_diverges(self.cx, if_then));
if diverges {
ControlFlow::Break(())
} else {
ControlFlow::Continue(Descend::No)
}
},
ExprKind::Match(match_expr, match_arms, _) => {
let diverges = expr_diverges(self.cx, match_expr)
|| match_arms.iter().all(|arm| {
let guard_diverges = arm.guard.as_ref().map_or(false, |g| expr_diverges(self.cx, g.body()));
guard_diverges || expr_diverges(self.cx, arm.body)
});
if diverges {
ControlFlow::Break(())
} else {
ControlFlow::Continue(Descend::No)
}
},
// Don't continue into loops or labeled blocks, as they are breakable,
// and we'd have to start checking labels.
ExprKind::Block(_, Some(_)) | ExprKind::Loop(..) => ControlFlow::Continue(Descend::No),
// Default: descend
_ => ControlFlow::Continue(Descend::Yes),
};
if let ControlFlow::Continue(Descend::Yes) = self.res {
walk_expr(self, e);
}
}
})
.is_some()
fn visit_local(&mut self, local: &'tcx Local<'_>) {
// Don't visit the else block of a let/else statement as it will not make
// the statement divergent even though the else block is divergent.
if let Some(init) = local.init {
self.visit_expr(init);
}
}
// Avoid unnecessary `walk_*` calls.
fn visit_ty(&mut self, _: &'tcx Ty<'tcx>) {}
fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {}
fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {}
// Avoid monomorphising all `visit_*` functions.
fn visit_nested_item(&mut self, _: ItemId) {}
}
let mut v = V {
cx,
res: ControlFlow::Continue(Descend::Yes),
};
expr.visit(&mut v);
v.res.is_break()
}
fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: bool) -> bool {

View file

@ -5,6 +5,8 @@ use rustc_errors::Applicability;
use rustc_hir::{Expr, LangItem};
use rustc_lint::LateContext;
use crate::methods::method_call;
use super::BYTES_NTH;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, n_arg: &'tcx Expr<'tcx>) {
@ -16,18 +18,32 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E
} else {
return;
};
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
BYTES_NTH,
expr.span,
&format!("called `.bytes().nth()` on a `{caller_type}`"),
"try",
format!(
"{}.as_bytes().get({})",
snippet_with_applicability(cx, recv.span, "..", &mut applicability),
snippet_with_applicability(cx, n_arg.span, "..", &mut applicability)
),
applicability,
);
let receiver = snippet_with_applicability(cx, recv.span, "..", &mut applicability);
let n = snippet_with_applicability(cx, n_arg.span, "..", &mut applicability);
if let Some(parent) = clippy_utils::get_parent_expr(cx, expr)
&& let Some((name, _, _, _, _)) = method_call(parent)
&& name == "unwrap" {
span_lint_and_sugg(
cx,
BYTES_NTH,
parent.span,
&format!("called `.bytes().nth().unwrap()` on a `{caller_type}`"),
"try",
format!("{receiver}.as_bytes()[{n}]",),
applicability
);
} else {
span_lint_and_sugg(
cx,
BYTES_NTH,
expr.span,
&format!("called `.bytes().nth()` on a `{caller_type}`"),
"try",
format!("{receiver}.as_bytes().get({n}).copied()"),
applicability
);
};
}

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_in_cfg_test;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_in_cfg_test, is_in_test_function};
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::sym;
@ -27,7 +27,7 @@ pub(super) fn check(
let method = if is_err { "expect_err" } else { "expect" };
if allow_expect_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) {
if allow_expect_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) {
return;
}

View file

@ -53,7 +53,9 @@ pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir
"to_vec" => cx
.tcx
.impl_of_method(method_def_id)
.filter(|&impl_did| cx.tcx.type_of(impl_did).subst_identity().is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none())
.filter(|&impl_did| {
cx.tcx.type_of(impl_did).subst_identity().is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none()
})
.is_some(),
_ => false,
}

View file

@ -80,6 +80,7 @@ mod skip_while_next;
mod stable_sort_primitive;
mod str_splitn;
mod string_extend_chars;
mod suspicious_command_arg_space;
mod suspicious_map;
mod suspicious_splitn;
mod suspicious_to_owned;
@ -3162,6 +3163,32 @@ declare_clippy_lint! {
"collecting an iterator when collect is not needed"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for `Command::arg()` invocations that look like they
/// should be multiple arguments instead, such as `arg("-t ext2")`.
///
/// ### Why is this bad?
///
/// `Command::arg()` does not split arguments by space. An argument like `arg("-t ext2")`
/// will be passed as a single argument to the command,
/// which is likely not what was intended.
///
/// ### Example
/// ```rust
/// std::process::Command::new("echo").arg("-n hello").spawn().unwrap();
/// ```
/// Use instead:
/// ```rust
/// std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap();
/// ```
#[clippy::version = "1.67.0"]
pub SUSPICIOUS_COMMAND_ARG_SPACE,
suspicious,
"single command line argument that looks like it should be multiple arguments"
}
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
@ -3289,6 +3316,7 @@ impl_lint_pass!(Methods => [
SEEK_FROM_CURRENT,
SEEK_TO_START_INSTEAD_OF_REWIND,
NEEDLESS_COLLECT,
SUSPICIOUS_COMMAND_ARG_SPACE,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@ -3496,6 +3524,9 @@ impl Methods {
unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
}
},
("arg", [arg]) => {
suspicious_command_arg_space::check(cx, recv, arg, span);
}
("as_deref" | "as_deref_mut", []) => {
needless_option_as_deref::check(cx, expr, recv, name);
},

View file

@ -0,0 +1,39 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::paths;
use clippy_utils::ty::match_type;
use rustc_ast as ast;
use rustc_errors::{Applicability, Diagnostic};
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::Span;
use super::SUSPICIOUS_COMMAND_ARG_SPACE;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) {
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
if match_type(cx, ty, &paths::STD_PROCESS_COMMAND)
&& let hir::ExprKind::Lit(lit) = &arg.kind
&& let ast::LitKind::Str(s, _) = &lit.node
&& let Some((arg1, arg2)) = s.as_str().split_once(' ')
&& arg1.starts_with('-')
&& arg1.chars().all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
{
span_lint_and_then(
cx,
SUSPICIOUS_COMMAND_ARG_SPACE,
arg.span,
"single argument that looks like it should be multiple arguments",
|diag: &mut Diagnostic| {
diag.multipart_suggestion_verbose(
"consider splitting the argument",
vec![
(span, "args".to_string()),
(arg.span, format!("[{arg1:?}, {arg2:?}]")),
],
Applicability::MaybeIncorrect,
);
}
);
}
}

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_in_cfg_test, is_lint_allowed};
use clippy_utils::{is_in_cfg_test, is_in_test_function, is_lint_allowed};
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::sym;
@ -27,7 +27,7 @@ pub(super) fn check(
let method_suffix = if is_err { "_err" } else { "" };
if allow_unwrap_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) {
if allow_unwrap_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) {
return;
}

View file

@ -8,10 +8,12 @@
use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_from_proc_macro;
use hir::def_id::LocalDefId;
use if_chain::if_chain;
use rustc_ast::ast::{self, MetaItem, MetaItemKind};
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::DefIdTree;
use rustc_middle::ty::{DefIdTree, Visibility};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::source_map::Span;
@ -34,6 +36,9 @@ declare_clippy_lint! {
}
pub struct MissingDoc {
/// Whether to **only** check for missing documentation in items visible within the current
/// crate. For example, `pub(crate)` items.
crate_items_only: bool,
/// Stack of whether #[doc(hidden)] is set
/// at each level which has lint attributes.
doc_hidden_stack: Vec<bool>,
@ -42,14 +47,15 @@ pub struct MissingDoc {
impl Default for MissingDoc {
#[must_use]
fn default() -> Self {
Self::new()
Self::new(false)
}
}
impl MissingDoc {
#[must_use]
pub fn new() -> Self {
pub fn new(crate_items_only: bool) -> Self {
Self {
crate_items_only,
doc_hidden_stack: vec![false],
}
}
@ -75,6 +81,7 @@ impl MissingDoc {
fn check_missing_docs_attrs(
&self,
cx: &LateContext<'_>,
def_id: LocalDefId,
attrs: &[ast::Attribute],
sp: Span,
article: &'static str,
@ -95,6 +102,13 @@ impl MissingDoc {
return;
}
if self.crate_items_only && def_id != CRATE_DEF_ID {
let vis = cx.tcx.visibility(def_id);
if vis == Visibility::Public || vis != Visibility::Restricted(CRATE_DEF_ID.into()) {
return;
}
}
let has_doc = attrs
.iter()
.any(|a| a.doc_str().is_some() || Self::has_include(a.meta()));
@ -123,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID);
self.check_missing_docs_attrs(cx, attrs, cx.tcx.def_span(CRATE_DEF_ID), "the", "crate");
self.check_missing_docs_attrs(cx, CRATE_DEF_ID, attrs, cx.tcx.def_span(CRATE_DEF_ID), "the", "crate");
}
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) {
@ -159,7 +173,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
let attrs = cx.tcx.hir().attrs(it.hir_id());
if !is_from_proc_macro(cx, it) {
self.check_missing_docs_attrs(cx, attrs, it.span, article, desc);
self.check_missing_docs_attrs(cx, it.owner_id.def_id, attrs, it.span, article, desc);
}
}
@ -168,7 +182,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
let attrs = cx.tcx.hir().attrs(trait_item.hir_id());
if !is_from_proc_macro(cx, trait_item) {
self.check_missing_docs_attrs(cx, attrs, trait_item.span, article, desc);
self.check_missing_docs_attrs(cx, trait_item.owner_id.def_id, attrs, trait_item.span, article, desc);
}
}
@ -185,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id());
let attrs = cx.tcx.hir().attrs(impl_item.hir_id());
if !is_from_proc_macro(cx, impl_item) {
self.check_missing_docs_attrs(cx, attrs, impl_item.span, article, desc);
self.check_missing_docs_attrs(cx, impl_item.owner_id.def_id, attrs, impl_item.span, article, desc);
}
}
@ -193,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
if !sf.is_positional() {
let attrs = cx.tcx.hir().attrs(sf.hir_id);
if !is_from_proc_macro(cx, sf) {
self.check_missing_docs_attrs(cx, attrs, sf.span, "a", "struct field");
self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field");
}
}
}
@ -201,7 +215,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) {
let attrs = cx.tcx.hir().attrs(v.hir_id);
if !is_from_proc_macro(cx, v) {
self.check_missing_docs_attrs(cx, attrs, v.span, "a", "variant");
self.check_missing_docs_attrs(cx, v.def_id, attrs, v.span, "a", "variant");
}
}
}

View file

@ -134,7 +134,7 @@ fn process_paths_for_mod_files<'a>(
mod_folders: &mut FxHashSet<&'a OsStr>,
) {
let mut comp = path.components().rev().peekable();
let _ = comp.next();
let _: Option<_> = comp.next();
if path.ends_with("mod.rs") {
mod_folders.insert(comp.peek().map(|c| c.as_os_str()).unwrap_or_default());
}

View file

@ -166,7 +166,8 @@ impl MutableKeyType {
Ref(_, inner_ty, mutbl) => mutbl == hir::Mutability::Mut || self.is_interior_mutable_type(cx, inner_ty),
Slice(inner_ty) => self.is_interior_mutable_type(cx, inner_ty),
Array(inner_ty, size) => {
size.try_eval_target_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0)
size.try_eval_target_usize(cx.tcx, cx.param_env)
.map_or(true, |u| u != 0)
&& self.is_interior_mutable_type(cx, inner_ty)
},
Tuple(fields) => fields.iter().any(|ty| self.is_interior_mutable_type(cx, ty)),

View file

@ -149,7 +149,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
};
let fn_sig = cx.tcx.fn_sig(fn_def_id).subst_identity();
let fn_sig = cx.tcx.erase_late_bound_regions(fn_sig);
let fn_sig = cx.tcx.liberate_late_bound_regions(fn_def_id.to_def_id(), fn_sig);
for (idx, ((input, &ty), arg)) in decl.inputs.iter().zip(fn_sig.inputs()).zip(body.params).enumerate() {
// All spans generated from a proc-macro invocation are the same...

View file

@ -0,0 +1,65 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_target::spec::abi::Abi;
declare_clippy_lint! {
/// ### What it does
/// Checks for Rust ABI functions with the `#[no_mangle]` attribute.
///
/// ### Why is this bad?
/// The Rust ABI is not stable, but in many simple cases matches
/// enough with the C ABI that it is possible to forget to add
/// `extern "C"` to a function called from C. Changes to the
/// Rust ABI can break this at any point.
///
/// ### Example
/// ```rust
/// #[no_mangle]
/// fn example(arg_one: u32, arg_two: usize) {}
/// ```
///
/// Use instead:
/// ```rust
/// #[no_mangle]
/// extern "C" fn example(arg_one: u32, arg_two: usize) {}
/// ```
#[clippy::version = "1.69.0"]
pub NO_MANGLE_WITH_RUST_ABI,
pedantic,
"convert Rust ABI functions to C ABI"
}
declare_lint_pass!(NoMangleWithRustAbi => [NO_MANGLE_WITH_RUST_ABI]);
impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::Fn(fn_sig, _, _) = &item.kind {
let attrs = cx.tcx.hir().attrs(item.hir_id());
let mut applicability = Applicability::MachineApplicable;
let snippet = snippet_with_applicability(cx, fn_sig.span, "..", &mut applicability);
for attr in attrs {
if let Some(ident) = attr.ident()
&& ident.name == rustc_span::sym::no_mangle
&& fn_sig.header.abi == Abi::Rust
&& !snippet.contains("extern") {
let suggestion = snippet.split_once("fn")
.map_or(String::new(), |(first, second)| format!(r#"{first}extern "C" fn{second}"#));
span_lint_and_sugg(
cx,
NO_MANGLE_WITH_RUST_ABI,
fn_sig.span,
"attribute #[no_mangle] set on a Rust ABI function",
"try",
suggestion,
applicability
);
}
}
}
}
}

View file

@ -1,8 +1,8 @@
use super::ARITHMETIC_SIDE_EFFECTS;
use clippy_utils::{
consts::{constant, constant_simple},
consts::{constant, constant_simple, Constant},
diagnostics::span_lint,
peel_hir_expr_refs, peel_hir_expr_unary,
is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary,
};
use rustc_ast as ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@ -97,17 +97,19 @@ impl ArithmeticSideEffects {
self.expr_span = Some(expr.span);
}
/// If `expr` is not a literal integer like `1`, returns `None`.
/// Returns the numeric value of a literal integer originated from `expr`, if any.
///
/// Returns the absolute value of the expression, if this is an integer literal.
fn literal_integer(expr: &hir::Expr<'_>) -> Option<u128> {
/// Literal integers can be originated from adhoc declarations like `1`, associated constants
/// like `i32::MAX` or constant references like `N` from `const N: i32 = 1;`,
fn literal_integer(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<u128> {
let actual = peel_hir_expr_unary(expr).0;
if let hir::ExprKind::Lit(ref lit) = actual.kind && let ast::LitKind::Int(n, _) = lit.node {
Some(n)
return Some(n)
}
else {
None
if let Some((Constant::Int(n), _)) = constant(cx, cx.typeck_results(), expr) {
return Some(n);
}
None
}
/// Manages when the lint should be triggered. Operations in constant environments, hard coded
@ -143,7 +145,10 @@ impl ArithmeticSideEffects {
let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) {
let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs);
let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs);
match (Self::literal_integer(actual_lhs), Self::literal_integer(actual_rhs)) {
match (
Self::literal_integer(cx, actual_lhs),
Self::literal_integer(cx, actual_rhs),
) {
(None, None) => false,
(None, Some(n)) | (Some(n), None) => match (&op.node, n) {
(hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false,
@ -180,20 +185,22 @@ impl ArithmeticSideEffects {
return;
}
let actual_un_expr = peel_hir_expr_refs(un_expr).0;
if Self::literal_integer(actual_un_expr).is_some() {
if Self::literal_integer(cx, actual_un_expr).is_some() {
return;
}
self.issue_lint(cx, expr);
}
fn should_skip_expr(&mut self, expr: &hir::Expr<'_>) -> bool {
self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span))
fn should_skip_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
is_lint_allowed(cx, ARITHMETIC_SIDE_EFFECTS, expr.hir_id)
|| self.expr_span.is_some()
|| self.const_span.map_or(false, |sp| sp.contains(expr.span))
}
}
impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) {
if self.should_skip_expr(expr) {
if self.should_skip_expr(cx, expr) {
return;
}
match &expr.kind {

View file

@ -0,0 +1,52 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::macros::span_is_local;
use rustc_hir::{Expr, ExprKind, MatchSource};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Checks for expressions that use the question mark operator and rejects them.
///
/// ### Why is this bad?
/// Sometimes code wants to avoid the question mark operator because for instance a local
/// block requires a macro to re-throw errors to attach additional information to the
/// error.
///
/// ### Example
/// ```ignore
/// let result = expr?;
/// ```
///
/// Could be written:
///
/// ```ignore
/// utility_macro!(expr);
/// ```
#[clippy::version = "pre 1.29.0"]
pub QUESTION_MARK_USED,
restriction,
"complains if the question mark operator is used"
}
declare_lint_pass!(QuestionMarkUsed => [QUESTION_MARK_USED]);
impl<'tcx> LateLintPass<'tcx> for QuestionMarkUsed {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Match(_, _, MatchSource::TryDesugar) = expr.kind {
if !span_is_local(expr.span) {
return;
}
span_lint_and_help(
cx,
QUESTION_MARK_USED,
expr.span,
"question mark operator was used",
None,
"consider using a custom macro or match expression",
);
}
}
}

View file

@ -14,6 +14,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
use rustc_span::{BytePos, Pos};
use std::borrow::Cow;
declare_clippy_lint! {
/// ### What it does
@ -69,31 +70,41 @@ declare_clippy_lint! {
"using a return statement like `return expr;` where an expression would suffice"
}
#[derive(PartialEq, Eq, Copy, Clone)]
enum RetReplacement {
#[derive(PartialEq, Eq, Clone)]
enum RetReplacement<'tcx> {
Empty,
Block,
Unit,
IfSequence(Cow<'tcx, str>, Applicability),
Expr(Cow<'tcx, str>, Applicability),
}
impl RetReplacement {
impl<'tcx> RetReplacement<'tcx> {
fn sugg_help(self) -> &'static str {
match self {
Self::Empty => "remove `return`",
Self::Empty | Self::Expr(..) => "remove `return`",
Self::Block => "replace `return` with an empty block",
Self::Unit => "replace `return` with a unit value",
Self::IfSequence(..) => "remove `return` and wrap the sequence with parentheses",
}
}
fn applicability(&self) -> Option<Applicability> {
match self {
Self::Expr(_, ap) | Self::IfSequence(_, ap) => Some(*ap),
_ => None,
}
}
}
impl ToString for RetReplacement {
impl<'tcx> ToString for RetReplacement<'tcx> {
fn to_string(&self) -> String {
match *self {
Self::Empty => "",
Self::Block => "{}",
Self::Unit => "()",
match self {
Self::Empty => String::new(),
Self::Block => "{}".to_string(),
Self::Unit => "()".to_string(),
Self::IfSequence(inner, _) => format!("({inner})"),
Self::Expr(inner, _) => inner.to_string(),
}
.to_string()
}
}
@ -204,26 +215,12 @@ fn check_final_expr<'tcx>(
expr: &'tcx Expr<'tcx>,
semi_spans: Vec<Span>, /* containing all the places where we would need to remove semicolons if finding an
* needless return */
replacement: RetReplacement,
replacement: RetReplacement<'tcx>,
) {
let peeled_drop_expr = expr.peel_drop_temps();
match &peeled_drop_expr.kind {
// simple return is always "bad"
ExprKind::Ret(ref inner) => {
// if desugar of `do yeet`, don't lint
if let Some(inner_expr) = inner
&& let ExprKind::Call(path_expr, _) = inner_expr.kind
&& let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind
{
return;
}
if !cx.tcx.hir().attrs(expr.hir_id).is_empty() {
return;
}
let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
if borrows {
return;
}
// check if expr return nothing
let ret_span = if inner.is_none() && replacement == RetReplacement::Empty {
extend_span_to_previous_non_ws(cx, peeled_drop_expr.span)
@ -231,7 +228,34 @@ fn check_final_expr<'tcx>(
peeled_drop_expr.span
};
emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement);
let replacement = if let Some(inner_expr) = inner {
// if desugar of `do yeet`, don't lint
if let ExprKind::Call(path_expr, _) = inner_expr.kind
&& let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind
{
return;
}
let mut applicability = Applicability::MachineApplicable;
let (snippet, _) = snippet_with_context(cx, inner_expr.span, ret_span.ctxt(), "..", &mut applicability);
if expr_contains_conjunctive_ifs(inner_expr) {
RetReplacement::IfSequence(snippet, applicability)
} else {
RetReplacement::Expr(snippet, applicability)
}
} else {
replacement
};
if !cx.tcx.hir().attrs(expr.hir_id).is_empty() {
return;
}
let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
if borrows {
return;
}
emit_return_lint(cx, ret_span, semi_spans, replacement);
},
ExprKind::If(_, then, else_clause_opt) => {
check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone());
@ -253,29 +277,25 @@ fn check_final_expr<'tcx>(
}
}
fn emit_return_lint(
cx: &LateContext<'_>,
ret_span: Span,
semi_spans: Vec<Span>,
inner_span: Option<Span>,
replacement: RetReplacement,
) {
fn expr_contains_conjunctive_ifs<'tcx>(expr: &'tcx Expr<'tcx>) -> bool {
fn contains_if(expr: &Expr<'_>, on_if: bool) -> bool {
match expr.kind {
ExprKind::If(..) => on_if,
ExprKind::Binary(_, left, right) => contains_if(left, true) || contains_if(right, true),
_ => false,
}
}
contains_if(expr, false)
}
fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec<Span>, replacement: RetReplacement<'_>) {
if ret_span.from_expansion() {
return;
}
let mut applicability = Applicability::MachineApplicable;
let return_replacement = inner_span.map_or_else(
|| replacement.to_string(),
|inner_span| {
let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability);
snippet.to_string()
},
);
let sugg_help = if inner_span.is_some() {
"remove `return`"
} else {
replacement.sugg_help()
};
let applicability = replacement.applicability().unwrap_or(Applicability::MachineApplicable);
let return_replacement = replacement.to_string();
let sugg_help = replacement.sugg_help();
span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
diag.span_suggestion_hidden(ret_span, sugg_help, return_replacement, applicability);
// for each parent statement, we need to remove the semicolon

View file

@ -0,0 +1,399 @@
use crate::FxHashSet;
use clippy_utils::{
diagnostics::span_lint_and_then,
get_attr,
source::{indent_of, snippet},
};
use rustc_errors::{Applicability, Diagnostic};
use rustc_hir::{
self as hir,
intravisit::{walk_expr, Visitor},
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::{subst::GenericArgKind, Ty, TypeAndMut};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::Ident, Span, DUMMY_SP};
declare_clippy_lint! {
/// ### What it does
///
/// Searches for elements marked with `#[clippy::significant_drop]` that could be early
/// dropped but are in fact dropped at the end of their scopes. In other words, enforces the
/// "tightening" of their possible lifetimes.
///
/// ### Why is this bad?
///
/// Elements marked with `#[clippy::has_significant_drop]` are generally synchronizing
/// primitives that manage shared resources, as such, it is desired to release them as soon as
/// possible to avoid unnecessary resource contention.
///
/// ### Example
///
/// ```rust,ignore
/// fn main() {
/// let lock = some_sync_resource.lock();
/// let owned_rslt = lock.do_stuff_with_resource();
/// // Only `owned_rslt` is needed but `lock` is still held.
/// do_heavy_computation_that_takes_time(owned_rslt);
/// }
/// ```
///
/// Use instead:
///
/// ```rust,ignore
/// fn main() {
/// let owned_rslt = some_sync_resource.lock().do_stuff_with_resource();
/// do_heavy_computation_that_takes_time(owned_rslt);
/// }
/// ```
#[clippy::version = "1.67.0"]
pub SIGNIFICANT_DROP_TIGHTENING,
nursery,
"Searches for elements marked with `#[clippy::has_significant_drop]` that could be early dropped but are in fact dropped at the end of their scopes"
}
impl_lint_pass!(SignificantDropTightening<'_> => [SIGNIFICANT_DROP_TIGHTENING]);
#[derive(Default)]
pub struct SignificantDropTightening<'tcx> {
/// Auxiliary structure used to avoid having to verify the same type multiple times.
seen_types: FxHashSet<Ty<'tcx>>,
}
impl<'tcx> SignificantDropTightening<'tcx> {
/// Unifies the statements of a block with its return expression.
fn all_block_stmts<'ret, 'rslt, 'stmts>(
block_stmts: &'stmts [hir::Stmt<'tcx>],
dummy_ret_stmt: Option<&'ret hir::Stmt<'tcx>>,
) -> impl Iterator<Item = &'rslt hir::Stmt<'tcx>>
where
'ret: 'rslt,
'stmts: 'rslt,
{
block_stmts.iter().chain(dummy_ret_stmt)
}
/// Searches for at least one statement that could slow down the release of a significant drop.
fn at_least_one_stmt_is_expensive<'stmt>(stmts: impl Iterator<Item = &'stmt hir::Stmt<'tcx>>) -> bool
where
'tcx: 'stmt,
{
for stmt in stmts {
match stmt.kind {
hir::StmtKind::Expr(expr) if let hir::ExprKind::Path(_) = expr.kind => {}
hir::StmtKind::Local(local) if let Some(expr) = local.init
&& let hir::ExprKind::Path(_) = expr.kind => {},
_ => return true
};
}
false
}
/// Verifies if the expression is of type `drop(some_lock_path)` to assert that the temporary
/// is already being dropped before the end of its scope.
fn has_drop(expr: &'tcx hir::Expr<'_>, init_bind_ident: Ident) -> bool {
if let hir::ExprKind::Call(fun, args) = expr.kind
&& let hir::ExprKind::Path(hir::QPath::Resolved(_, fun_path)) = &fun.kind
&& let [fun_ident, ..] = fun_path.segments
&& fun_ident.ident.name == rustc_span::sym::drop
&& let [first_arg, ..] = args
&& let hir::ExprKind::Path(hir::QPath::Resolved(_, arg_path)) = &first_arg.kind
&& let [first_arg_ps, .. ] = arg_path.segments
{
first_arg_ps.ident == init_bind_ident
}
else {
false
}
}
/// Tries to find types marked with `#[has_significant_drop]` of an expression `expr` that is
/// originated from `stmt` and then performs common logic on `sdap`.
fn modify_sdap_if_sig_drop_exists(
&mut self,
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
idx: usize,
sdap: &mut SigDropAuxParams,
stmt: &hir::Stmt<'_>,
cb: impl Fn(&mut SigDropAuxParams),
) {
let mut sig_drop_finder = SigDropFinder::new(cx, &mut self.seen_types);
sig_drop_finder.visit_expr(expr);
if sig_drop_finder.has_sig_drop {
cb(sdap);
if sdap.number_of_stmts > 0 {
sdap.last_use_stmt_idx = idx;
sdap.last_use_stmt_span = stmt.span;
if let hir::ExprKind::MethodCall(_, _, _, span) = expr.kind {
sdap.last_use_method_span = span;
}
}
sdap.number_of_stmts = sdap.number_of_stmts.wrapping_add(1);
}
}
/// Shows generic overall messages as well as specialized messages depending on the usage.
fn set_suggestions(cx: &LateContext<'tcx>, block_span: Span, diag: &mut Diagnostic, sdap: &SigDropAuxParams) {
match sdap.number_of_stmts {
0 | 1 => {},
2 => {
let indent = " ".repeat(indent_of(cx, sdap.last_use_stmt_span).unwrap_or(0));
let init_method = snippet(cx, sdap.init_method_span, "..");
let usage_method = snippet(cx, sdap.last_use_method_span, "..");
let stmt = if let Some(last_use_bind_span) = sdap.last_use_bind_span {
format!(
"\n{indent}let {} = {init_method}.{usage_method};",
snippet(cx, last_use_bind_span, ".."),
)
} else {
format!("\n{indent}{init_method}.{usage_method};")
};
diag.span_suggestion_verbose(
sdap.init_stmt_span,
"merge the temporary construction with its single usage",
stmt,
Applicability::MaybeIncorrect,
);
diag.span_suggestion(
sdap.last_use_stmt_span,
"remove separated single usage",
"",
Applicability::MaybeIncorrect,
);
},
_ => {
diag.span_suggestion(
sdap.last_use_stmt_span.shrink_to_hi(),
"drop the temporary after the end of its last usage",
format!(
"\n{}drop({});",
" ".repeat(indent_of(cx, sdap.last_use_stmt_span).unwrap_or(0)),
sdap.init_bind_ident
),
Applicability::MaybeIncorrect,
);
},
}
diag.note("this might lead to unnecessary resource contention");
diag.span_label(
block_span,
format!(
"temporary `{}` is currently being dropped at the end of its contained scope",
sdap.init_bind_ident
),
);
}
}
impl<'tcx> LateLintPass<'tcx> for SignificantDropTightening<'tcx> {
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
let dummy_ret_stmt = block.expr.map(|expr| hir::Stmt {
hir_id: hir::HirId::INVALID,
kind: hir::StmtKind::Expr(expr),
span: DUMMY_SP,
});
let mut sdap = SigDropAuxParams::default();
for (idx, stmt) in Self::all_block_stmts(block.stmts, dummy_ret_stmt.as_ref()).enumerate() {
match stmt.kind {
hir::StmtKind::Expr(expr) => self.modify_sdap_if_sig_drop_exists(
cx,
expr,
idx,
&mut sdap,
stmt,
|_| {}
),
hir::StmtKind::Local(local) if let Some(expr) = local.init => self.modify_sdap_if_sig_drop_exists(
cx,
expr,
idx,
&mut sdap,
stmt,
|local_sdap| {
if local_sdap.number_of_stmts == 0 {
if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind {
local_sdap.init_bind_ident = ident;
}
if let hir::ExprKind::MethodCall(_, local_expr, _, span) = expr.kind {
local_sdap.init_method_span = local_expr.span.to(span);
}
local_sdap.init_stmt_span = stmt.span;
}
else if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind {
local_sdap.last_use_bind_span = Some(ident.span);
}
}
),
hir::StmtKind::Semi(expr) => {
if Self::has_drop(expr, sdap.init_bind_ident) {
return;
}
self.modify_sdap_if_sig_drop_exists(cx, expr, idx, &mut sdap, stmt, |_| {});
},
_ => {}
};
}
let idx = sdap.last_use_stmt_idx.wrapping_add(1);
let stmts_after_last_use = Self::all_block_stmts(block.stmts, dummy_ret_stmt.as_ref()).skip(idx);
if sdap.number_of_stmts > 1 && Self::at_least_one_stmt_is_expensive(stmts_after_last_use) {
span_lint_and_then(
cx,
SIGNIFICANT_DROP_TIGHTENING,
sdap.init_bind_ident.span,
"temporary with significant `Drop` can be early dropped",
|diag| {
Self::set_suggestions(cx, block.span, diag, &sdap);
},
);
}
}
}
/// Auxiliary parameters used on each block check.
struct SigDropAuxParams {
/// The binding or variable that references the initial construction of the type marked with
/// `#[has_significant_drop]`.
init_bind_ident: Ident,
/// Similar to `init_bind_ident` but encompasses the right-hand method call.
init_method_span: Span,
/// Similar to `init_bind_ident` but encompasses the whole contained statement.
init_stmt_span: Span,
/// The last visited binding or variable span within a block that had any referenced inner type
/// marked with `#[has_significant_drop]`.
last_use_bind_span: Option<Span>,
/// Index of the last visited statement within a block that had any referenced inner type
/// marked with `#[has_significant_drop]`.
last_use_stmt_idx: usize,
/// Similar to `last_use_bind_span` but encompasses the whole contained statement.
last_use_stmt_span: Span,
/// Similar to `last_use_bind_span` but encompasses the right-hand method call.
last_use_method_span: Span,
/// Total number of statements within a block that have any referenced inner type marked with
/// `#[has_significant_drop]`.
number_of_stmts: usize,
}
impl Default for SigDropAuxParams {
fn default() -> Self {
Self {
init_bind_ident: Ident::empty(),
init_method_span: DUMMY_SP,
init_stmt_span: DUMMY_SP,
last_use_bind_span: None,
last_use_method_span: DUMMY_SP,
last_use_stmt_idx: 0,
last_use_stmt_span: DUMMY_SP,
number_of_stmts: 0,
}
}
}
/// Checks the existence of the `#[has_significant_drop]` attribute
struct SigDropChecker<'cx, 'sdt, 'tcx> {
cx: &'cx LateContext<'tcx>,
seen_types: &'sdt mut FxHashSet<Ty<'tcx>>,
}
impl<'cx, 'sdt, 'tcx> SigDropChecker<'cx, 'sdt, 'tcx> {
pub(crate) fn new(cx: &'cx LateContext<'tcx>, seen_types: &'sdt mut FxHashSet<Ty<'tcx>>) -> Self {
seen_types.clear();
Self { cx, seen_types }
}
pub(crate) fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool {
if let Some(adt) = ty.ty_adt_def() {
let mut iter = get_attr(
self.cx.sess(),
self.cx.tcx.get_attrs_unchecked(adt.did()),
"has_significant_drop",
);
if iter.next().is_some() {
return true;
}
}
match ty.kind() {
rustc_middle::ty::Adt(a, b) => {
for f in a.all_fields() {
let ty = f.ty(self.cx.tcx, b);
if !self.has_seen_ty(ty) && self.has_sig_drop_attr(ty) {
return true;
}
}
for generic_arg in b.iter() {
if let GenericArgKind::Type(ty) = generic_arg.unpack() {
if self.has_sig_drop_attr(ty) {
return true;
}
}
}
false
},
rustc_middle::ty::Array(ty, _)
| rustc_middle::ty::RawPtr(TypeAndMut { ty, .. })
| rustc_middle::ty::Ref(_, ty, _)
| rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(*ty),
_ => false,
}
}
fn has_seen_ty(&mut self, ty: Ty<'tcx>) -> bool {
!self.seen_types.insert(ty)
}
}
/// Performs recursive calls to find any inner type marked with `#[has_significant_drop]`.
struct SigDropFinder<'cx, 'sdt, 'tcx> {
cx: &'cx LateContext<'tcx>,
has_sig_drop: bool,
sig_drop_checker: SigDropChecker<'cx, 'sdt, 'tcx>,
}
impl<'cx, 'sdt, 'tcx> SigDropFinder<'cx, 'sdt, 'tcx> {
fn new(cx: &'cx LateContext<'tcx>, seen_types: &'sdt mut FxHashSet<Ty<'tcx>>) -> Self {
Self {
cx,
has_sig_drop: false,
sig_drop_checker: SigDropChecker::new(cx, seen_types),
}
}
}
impl<'cx, 'sdt, 'tcx> Visitor<'tcx> for SigDropFinder<'cx, 'sdt, 'tcx> {
fn visit_expr(&mut self, ex: &'tcx hir::Expr<'_>) {
if self
.sig_drop_checker
.has_sig_drop_attr(self.cx.typeck_results().expr_ty(ex))
{
self.has_sig_drop = true;
return;
}
match ex.kind {
hir::ExprKind::MethodCall(_, expr, ..) => {
self.visit_expr(expr);
},
hir::ExprKind::Array(..)
| hir::ExprKind::Assign(..)
| hir::ExprKind::AssignOp(..)
| hir::ExprKind::Binary(..)
| hir::ExprKind::Box(..)
| hir::ExprKind::Call(..)
| hir::ExprKind::Field(..)
| hir::ExprKind::If(..)
| hir::ExprKind::Index(..)
| hir::ExprKind::Match(..)
| hir::ExprKind::Repeat(..)
| hir::ExprKind::Ret(..)
| hir::ExprKind::Tup(..)
| hir::ExprKind::Unary(..)
| hir::ExprKind::Yield(..) => {
walk_expr(self, ex);
},
_ => {},
}
}
}

View file

@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
use rustc_span::{sym, Span};
use rustc_span::{sym, symbol::Ident, Span};
declare_clippy_lint! {
/// ### What it does
@ -174,55 +174,76 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
/// Implementation of the `ALMOST_SWAPPED` lint.
fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
for w in block.stmts.windows(2) {
if_chain! {
if let StmtKind::Semi(first) = w[0].kind;
if let StmtKind::Semi(second) = w[1].kind;
if first.span.ctxt() == second.span.ctxt();
if let ExprKind::Assign(lhs0, rhs0, _) = first.kind;
if let ExprKind::Assign(lhs1, rhs1, _) = second.kind;
if eq_expr_value(cx, lhs0, rhs1);
if eq_expr_value(cx, lhs1, rhs0);
then {
let lhs0 = Sugg::hir_opt(cx, lhs0);
let rhs0 = Sugg::hir_opt(cx, rhs0);
let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) {
(
format!(" `{first}` and `{second}`"),
first.mut_addr().to_string(),
second.mut_addr().to_string(),
)
} else {
(String::new(), String::new(), String::new())
};
for [first, second] in block.stmts.array_windows() {
if let Some((lhs0, rhs0)) = parse(first)
&& let Some((lhs1, rhs1)) = parse(second)
&& first.span.eq_ctxt(second.span)
&& is_same(cx, lhs0, rhs1)
&& is_same(cx, lhs1, rhs0)
&& let Some(lhs_sugg) = match &lhs0 {
ExprOrIdent::Expr(expr) => Sugg::hir_opt(cx, expr),
ExprOrIdent::Ident(ident) => Some(Sugg::NonParen(ident.as_str().into())),
}
&& let Some(rhs_sugg) = Sugg::hir_opt(cx, rhs0)
{
let span = first.span.to(rhs1.span);
let Some(sugg) = std_or_core(cx) else { return };
span_lint_and_then(
cx,
ALMOST_SWAPPED,
span,
&format!("this looks like you are trying to swap `{lhs_sugg}` and `{rhs_sugg}`"),
|diag| {
diag.span_suggestion(
span,
"try",
format!("{sugg}::mem::swap({}, {})", lhs_sugg.mut_addr(), rhs_sugg.mut_addr()),
Applicability::MaybeIncorrect,
);
diag.note(format!("or maybe you should use `{sugg}::mem::replace`?"));
},
);
}
}
}
let span = first.span.to(second.span);
let Some(sugg) = std_or_core(cx) else { return };
span_lint_and_then(cx,
ALMOST_SWAPPED,
span,
&format!("this looks like you are trying to swap{what}"),
|diag| {
if !what.is_empty() {
diag.span_suggestion(
span,
"try",
format!(
"{sugg}::mem::swap({lhs}, {rhs})",
),
Applicability::MaybeIncorrect,
);
diag.note(
format!("or maybe you should use `{sugg}::mem::replace`?")
);
}
});
fn is_same(cx: &LateContext<'_>, lhs: ExprOrIdent<'_>, rhs: &Expr<'_>) -> bool {
match lhs {
ExprOrIdent::Expr(expr) => eq_expr_value(cx, expr, rhs),
ExprOrIdent::Ident(ident) => {
if let ExprKind::Path(QPath::Resolved(None, path)) = rhs.kind
&& let [segment] = &path.segments
&& segment.ident == ident
{
true
} else {
false
}
}
}
}
#[derive(Debug, Clone, Copy)]
enum ExprOrIdent<'a> {
Expr(&'a Expr<'a>),
Ident(Ident),
}
fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr<'hir>)> {
if let StmtKind::Semi(expr) = stmt.kind {
if let ExprKind::Assign(lhs, rhs, _) = expr.kind {
return Some((ExprOrIdent::Expr(lhs), rhs));
}
} else if let StmtKind::Local(expr) = stmt.kind {
if let Some(rhs) = expr.init {
if let PatKind::Binding(_, _, ident_l, _) = expr.pat.kind {
return Some((ExprOrIdent::Ident(ident_l), rhs));
}
}
}
None
}
/// Implementation of the xor case for `MANUAL_SWAP` lint.
fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
for window in block.stmts.windows(3) {

View file

@ -3,6 +3,7 @@ mod transmute_float_to_int;
mod transmute_int_to_bool;
mod transmute_int_to_char;
mod transmute_int_to_float;
mod transmute_int_to_non_zero;
mod transmute_null_to_fn;
mod transmute_num_to_bytes;
mod transmute_ptr_to_ptr;
@ -253,6 +254,31 @@ declare_clippy_lint! {
"transmutes from an integer to a float"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for transmutes from integers to `NonZero*` types, and suggests their `new_unchecked`
/// method instead.
///
/// ### Why is this bad?
/// Transmutes work on any types and thus might cause unsoundness when those types change
/// elsewhere. `new_unchecked` only works for the appropriate types instead.
///
/// ### Example
/// ```rust
/// # use core::num::NonZeroU32;
/// let _non_zero: NonZeroU32 = unsafe { std::mem::transmute(123) };
/// ```
/// Use instead:
/// ```rust
/// # use core::num::NonZeroU32;
/// let _non_zero = unsafe { NonZeroU32::new_unchecked(123) };
/// ```
#[clippy::version = "1.69.0"]
pub TRANSMUTE_INT_TO_NON_ZERO,
complexity,
"transmutes from an integer to a non-zero wrapper"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for transmutes from a float to an integer.
@ -451,6 +477,7 @@ impl_lint_pass!(Transmute => [
TRANSMUTE_BYTES_TO_STR,
TRANSMUTE_INT_TO_BOOL,
TRANSMUTE_INT_TO_FLOAT,
TRANSMUTE_INT_TO_NON_ZERO,
TRANSMUTE_FLOAT_TO_INT,
TRANSMUTE_NUM_TO_BYTES,
UNSOUND_COLLECTION_TRANSMUTE,
@ -501,6 +528,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
| transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
| transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
| transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context)
| transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg)
| transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context)
| transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context)
| (

View file

@ -0,0 +1,61 @@
use super::TRANSMUTE_INT_TO_NON_ZERO;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::sugg;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::{
query::Key,
ty::{self, Ty},
};
use rustc_span::symbol::sym;
/// Checks for `transmute_int_to_non_zero` lint.
/// Returns `true` if it's triggered, otherwise returns `false`.
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
e: &'tcx Expr<'_>,
from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>,
arg: &'tcx Expr<'_>,
) -> bool {
let (ty::Int(_) | ty::Uint(_), Some(to_ty_id)) = (&from_ty.kind(), to_ty.ty_adt_id()) else {
return false;
};
let Some(to_type_sym) = cx.tcx.get_diagnostic_name(to_ty_id) else {
return false;
};
if !matches!(
to_type_sym,
sym::NonZeroU8
| sym::NonZeroU16
| sym::NonZeroU32
| sym::NonZeroU64
| sym::NonZeroU128
| sym::NonZeroI8
| sym::NonZeroI16
| sym::NonZeroI32
| sym::NonZeroI64
| sym::NonZeroI128
) {
return false;
}
span_lint_and_then(
cx,
TRANSMUTE_INT_TO_NON_ZERO,
e.span,
&format!("transmute from a `{from_ty}` to a `{to_type_sym}`"),
|diag| {
let arg = sugg::Sugg::hir(cx, arg, "..");
diag.span_suggestion(
e.span,
"consider using",
format!("{to_type_sym}::{}({arg})", sym::new_unchecked),
Applicability::Unspecified,
);
},
);
true
}

View file

@ -400,7 +400,7 @@ fn drain_matching(
// If `ThinVec` had the `drain_filter` method, this loop could be rewritten
// like so:
//
//
// for pat in alternatives.drain_filter(|p| {
// // Check if we should extract, but only if `idx >= start`.
// idx += 1;
@ -412,12 +412,12 @@ fn drain_matching(
while i < alternatives.len() {
idx += 1;
// Check if we should extract, but only if `idx >= start`.
if idx > start && predicate(&alternatives[i].kind) {
let pat = alternatives.remove(i);
if idx > start && predicate(&alternatives[i].kind) {
let pat = alternatives.remove(i);
tail_or.push(extract(pat.into_inner().kind));
} else {
i += 1;
}
} else {
i += 1;
}
}
tail_or

View file

@ -419,19 +419,19 @@ define_Conf! {
(max_include_file_size: u64 = 1_000_000),
/// Lint: EXPECT_USED.
///
/// Whether `expect` should be allowed within `#[cfg(test)]`
/// Whether `expect` should be allowed in test functions or `#[cfg(test)]`
(allow_expect_in_tests: bool = false),
/// Lint: UNWRAP_USED.
///
/// Whether `unwrap` should be allowed in test cfg
/// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
(allow_unwrap_in_tests: bool = false),
/// Lint: DBG_MACRO.
///
/// Whether `dbg!` should be allowed in test functions
/// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
(allow_dbg_in_tests: bool = false),
/// Lint: PRINT_STDOUT, PRINT_STDERR.
///
/// Whether print macros (ex. `println!`) should be allowed in test functions
/// Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]`
(allow_print_in_tests: bool = false),
/// Lint: RESULT_LARGE_ERR.
///
@ -454,6 +454,11 @@ define_Conf! {
/// configuration will cause restriction lints to trigger even
/// if no suggestion can be made.
(suppress_restriction_lint_in_const: bool = false),
/// Lint: MISSING_DOCS_IN_PRIVATE_ITEMS.
///
/// Whether to **only** check for missing documentation in items visible within the current
/// crate. For example, `pub(crate)` items.
(missing_docs_in_crate_items: bool = false),
}
/// Search for the configuration file.

View file

@ -3,7 +3,7 @@ use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
@ -44,7 +44,7 @@ impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]);
#[derive(Default)]
pub struct UnnecessaryDefPath {
array_def_ids: FxHashSet<(DefId, Span)>,
array_def_ids: FxIndexSet<(DefId, Span)>,
linted_def_ids: FxHashSet<DefId>,
}

View file

@ -391,11 +391,18 @@ impl FormatString {
};
let mut unescaped = String::with_capacity(inner.len());
// Sometimes the original string comes from a macro which accepts a malformed string, such as in a
// #[display(""somestring)] attribute (accepted by the `displaythis` crate). Reconstructing the
// string from the span will not be possible, so we will just return None here.
let mut unparsable = false;
unescape_literal(inner, mode, &mut |_, ch| match ch {
Ok(ch) => unescaped.push(ch),
Err(e) if !e.is_fatal() => (),
Err(e) => panic!("{e:?}"),
Err(_) => unparsable = true,
});
if unparsable {
return None;
}
let mut parts = Vec::new();
let _: Option<!> = for_each_expr(pieces, |expr| {

View file

@ -186,7 +186,7 @@ impl<'a> NumericLiteral<'a> {
// The exponent may have a sign, output it early, otherwise it will be
// treated as a digit
if digits.clone().next() == Some('-') {
let _ = digits.next();
let _: Option<char> = digits.next();
output.push('-');
}

View file

@ -115,6 +115,7 @@ pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
pub const STD_IO_SEEK: [&str; 3] = ["std", "io", "Seek"];
pub const STD_IO_SEEK_FROM_CURRENT: [&str; 4] = ["std", "io", "SeekFrom", "Current"];
pub const STD_IO_SEEKFROM_START: [&str; 4] = ["std", "io", "SeekFrom", "Start"];
pub const STD_PROCESS_COMMAND: [&str; 3] = ["std", "process", "Command"];
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];

View file

@ -20,7 +20,7 @@ use rustc_middle::mir::{FakeReadCause, Mutability};
use rustc_middle::ty;
use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext};
use std::borrow::Cow;
use std::fmt::{Display, Write as _};
use std::fmt::{self, Display, Write as _};
use std::ops::{Add, Neg, Not, Sub};
/// A helper type to build suggestion correctly handling parentheses.
@ -932,7 +932,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
if cmt.place.projections.is_empty() {
// handle item without any projection, that needs an explicit borrowing
// i.e.: suggest `&x` instead of `x`
let _ = write!(self.suggestion_start, "{start_snip}&{ident_str}");
let _: fmt::Result = write!(self.suggestion_start, "{start_snip}&{ident_str}");
} else {
// cases where a parent `Call` or `MethodCall` is using the item
// i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()`
@ -947,7 +947,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
// given expression is the self argument and will be handled completely by the compiler
// i.e.: `|x| x.is_something()`
ExprKind::MethodCall(_, self_expr, ..) if self_expr.hir_id == cmt.hir_id => {
let _ = write!(self.suggestion_start, "{start_snip}{ident_str_with_proj}");
let _: fmt::Result = write!(self.suggestion_start, "{start_snip}{ident_str_with_proj}");
self.next_pos = span.hi();
return;
},
@ -1055,7 +1055,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
}
}
let _ = write!(self.suggestion_start, "{start_snip}{replacement_str}");
let _: fmt::Result = write!(self.suggestion_start, "{start_snip}{replacement_str}");
}
self.next_pos = span.hi();
}

View file

@ -17,8 +17,8 @@ use rustc_lint::LateContext;
use rustc_middle::mir::interpret::{ConstValue, Scalar};
use rustc_middle::ty::{
self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, IntTy, List, ParamEnv, Predicate,
PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy,
VariantDef, VariantDiscr, TypeVisitableExt,
PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
TypeVisitor, UintTy, VariantDef, VariantDiscr,
};
use rustc_middle::ty::{GenericArg, GenericArgKind};
use rustc_span::symbol::Ident;
@ -894,16 +894,29 @@ impl AdtVariantInfo {
}
/// Gets the struct or enum variant from the given `Res`
pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx VariantDef> {
pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<(AdtDef<'tcx>, &'tcx VariantDef)> {
match res {
Res::Def(DefKind::Struct, id) => Some(cx.tcx.adt_def(id).non_enum_variant()),
Res::Def(DefKind::Variant, id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).variant_with_id(id)),
Res::Def(DefKind::Ctor(CtorOf::Struct, _), id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).non_enum_variant()),
Res::Def(DefKind::Struct, id) => {
let adt = cx.tcx.adt_def(id);
Some((adt, adt.non_enum_variant()))
},
Res::Def(DefKind::Variant, id) => {
let adt = cx.tcx.adt_def(cx.tcx.parent(id));
Some((adt, adt.variant_with_id(id)))
},
Res::Def(DefKind::Ctor(CtorOf::Struct, _), id) => {
let adt = cx.tcx.adt_def(cx.tcx.parent(id));
Some((adt, adt.non_enum_variant()))
},
Res::Def(DefKind::Ctor(CtorOf::Variant, _), id) => {
let var_id = cx.tcx.parent(id);
Some(cx.tcx.adt_def(cx.tcx.parent(var_id)).variant_with_id(var_id))
let adt = cx.tcx.adt_def(cx.tcx.parent(var_id));
Some((adt, adt.variant_with_id(var_id)))
},
Res::SelfCtor(id) => {
let adt = cx.tcx.type_of(id).subst_identity().ty_adt_def().unwrap();
Some((adt, adt.non_enum_variant()))
},
Res::SelfCtor(id) => Some(cx.tcx.type_of(id).subst_identity().ty_adt_def().unwrap().non_enum_variant()),
_ => None,
}
}

View file

@ -35,7 +35,7 @@ fn get_clap_config() -> ArgMatches {
.long("markdown")
.help("Change the reports table to use markdown links"),
Arg::new("recursive")
.long("--recursive")
.long("recursive")
.help("Run clippy on the dependencies of crates specified in crates-toml")
.conflicts_with("threads")
.conflicts_with("fix"),

View file

@ -17,9 +17,9 @@ use crate::recursive::LintcheckServer;
use std::collections::{HashMap, HashSet};
use std::env;
use std::env::consts::EXE_SUFFIX;
use std::fmt::Write as _;
use std::fmt::{self, Write as _};
use std::fs;
use std::io::ErrorKind;
use std::io::{self, ErrorKind};
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::atomic::{AtomicUsize, Ordering};
@ -145,8 +145,8 @@ impl ClippyWarning {
}
let mut output = String::from("| ");
let _ = write!(output, "[`{file_with_pos}`]({file}#L{})", self.line);
let _ = write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.message);
let _: fmt::Result = write!(output, "[`{file_with_pos}`]({file}#L{})", self.line);
let _: fmt::Result = write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.message);
output.push('\n');
output
} else {
@ -632,7 +632,7 @@ fn main() {
.unwrap();
let server = config.recursive.then(|| {
let _ = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive");
let _: io::Result<()> = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive");
LintcheckServer::spawn(recursive_options)
});
@ -689,7 +689,7 @@ fn main() {
write!(text, "{}", all_msgs.join("")).unwrap();
text.push_str("\n\n### ICEs:\n");
for (cratename, msg) in &ices {
let _ = write!(text, "{cratename}: '{msg}'");
let _: fmt::Result = write!(text, "{cratename}: '{msg}'");
}
println!("Writing logs to {}", config.lintcheck_results_path.display());

View file

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2023-02-10"
channel = "nightly-2023-02-25"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]

View file

@ -209,10 +209,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
// Separate the output with an empty line
eprintln!();
let fallback_bundle = rustc_errors::fallback_fluent_bundle(
rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
false
);
let fallback_bundle = rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
rustc_errors::ColorConfig::Auto,
None,

View file

@ -1,8 +1,9 @@
// rustc-env:RUST_BACKTRACE=0
// normalize-stderr-test: "Clippy version: .*" -> "Clippy version: foo"
// normalize-stderr-test: "internal_lints.rs:\d*:\d*" -> "internal_lints.rs"
// normalize-stderr-test: "produce_ice.rs:\d*:\d*" -> "produce_ice.rs"
// normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints"
// normalize-stderr-test: "'rustc'" -> "'<unnamed>'"
// normalize-stderr-test: "(?ms)query stack during panic:\n.*end of query stack\n" -> ""
#![deny(clippy::internal)]
#![allow(clippy::missing_clippy_version_attribute)]

View file

@ -1,4 +1,4 @@
thread '<unnamed>' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs:28:9
thread '<unnamed>' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: internal compiler error: unexpected panic
@ -9,5 +9,3 @@ note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy
note: Clippy version: foo
query stack during panic:
end of query stack

View file

@ -1,12 +1,3 @@
error: hardcoded path to a diagnostic item
--> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43
|
LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: convert all references to use `sym::deref_method`
= note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
error: hardcoded path to a diagnostic item
--> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36
|
@ -14,6 +5,7 @@ LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: convert all references to use `sym::Deref`
= note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
error: hardcoded path to a language item
--> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40
@ -23,5 +15,13 @@ LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"]
|
= help: convert all references to use `LangItem::DerefMut`
error: hardcoded path to a diagnostic item
--> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43
|
LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: convert all references to use `sym::deref_method`
error: aborting due to 3 previous errors

View file

@ -16,6 +16,18 @@ fn main() {
expect_result();
}
#[test]
fn test_expect_option() {
let opt = Some(0);
let _ = opt.expect("");
}
#[test]
fn test_expect_result() {
let res: Result<u8, ()> = Ok(0);
let _ = res.expect("");
}
#[cfg(test)]
mod issue9612 {
// should not lint in `#[cfg(test)]` modules

View file

@ -0,0 +1 @@
missing-docs-in-crate-items = true

View file

@ -0,0 +1,59 @@
//! this is crate
#![allow(missing_docs)]
#![warn(clippy::missing_docs_in_private_items)]
/// this is mod
mod my_mod {
/// some docs
fn priv_with_docs() {}
fn priv_no_docs() {}
/// some docs
pub(crate) fn crate_with_docs() {}
pub(crate) fn crate_no_docs() {}
/// some docs
pub(super) fn super_with_docs() {}
pub(super) fn super_no_docs() {}
mod my_sub {
/// some docs
fn sub_priv_with_docs() {}
fn sub_priv_no_docs() {}
/// some docs
pub(crate) fn sub_crate_with_docs() {}
pub(crate) fn sub_crate_no_docs() {}
/// some docs
pub(super) fn sub_super_with_docs() {}
pub(super) fn sub_super_no_docs() {}
}
/// some docs
pub(crate) struct CrateStructWithDocs {
/// some docs
pub(crate) crate_field_with_docs: (),
pub(crate) crate_field_no_docs: (),
/// some docs
priv_field_with_docs: (),
priv_field_no_docs: (),
}
pub(crate) struct CrateStructNoDocs {
/// some docs
pub(crate) crate_field_with_docs: (),
pub(crate) crate_field_no_docs: (),
/// some docs
priv_field_with_docs: (),
priv_field_no_docs: (),
}
}
/// some docs
type CrateTypedefWithDocs = String;
type CrateTypedefNoDocs = String;
/// some docs
pub type PubTypedefWithDocs = String;
pub type PubTypedefNoDocs = String;
fn main() {
my_mod::crate_with_docs();
my_mod::crate_no_docs();
}

View file

@ -0,0 +1,52 @@
error: missing documentation for a function
--> $DIR/pub_crate_missing_doc.rs:12:5
|
LL | pub(crate) fn crate_no_docs() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::missing-docs-in-private-items` implied by `-D warnings`
error: missing documentation for a function
--> $DIR/pub_crate_missing_doc.rs:15:5
|
LL | pub(super) fn super_no_docs() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a function
--> $DIR/pub_crate_missing_doc.rs:23:9
|
LL | pub(crate) fn sub_crate_no_docs() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a struct field
--> $DIR/pub_crate_missing_doc.rs:33:9
|
LL | pub(crate) crate_field_no_docs: (),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a struct
--> $DIR/pub_crate_missing_doc.rs:39:5
|
LL | / pub(crate) struct CrateStructNoDocs {
LL | | /// some docs
LL | | pub(crate) crate_field_with_docs: (),
LL | | pub(crate) crate_field_no_docs: (),
... |
LL | | priv_field_no_docs: (),
LL | | }
| |_____^
error: missing documentation for a struct field
--> $DIR/pub_crate_missing_doc.rs:42:9
|
LL | pub(crate) crate_field_no_docs: (),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: missing documentation for a type alias
--> $DIR/pub_crate_missing_doc.rs:51:1
|
LL | type CrateTypedefNoDocs = String;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 7 previous errors

View file

@ -33,6 +33,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie
max-struct-bools
max-suggested-slice-pattern-length
max-trait-bounds
missing-docs-in-crate-items
msrv
pass-by-value-size-limit
single-char-binding-names-threshold

View file

@ -66,6 +66,12 @@ fn main() {
}
}
#[test]
fn test() {
let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
let _ = boxed_slice.get(1).unwrap();
}
#[cfg(test)]
mod issue9612 {
// should not lint in `#[cfg(test)]` modules

View file

@ -188,10 +188,16 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec();
= help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
--> $DIR/unwrap_used.rs:84:17
--> $DIR/unwrap_used.rs:72:13
|
LL | let _ = boxed_slice.get(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]`
error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
--> $DIR/unwrap_used.rs:90:17
|
LL | let _ = Box::new([0]).get(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&Box::new([0])[1]`
error: aborting due to 27 previous errors
error: aborting due to 28 previous errors

View file

@ -13,6 +13,9 @@
use core::num::{Saturating, Wrapping};
const ONE: i32 = 1;
const ZERO: i32 = 0;
#[derive(Clone, Copy)]
pub struct Custom;
@ -182,6 +185,10 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri
_n += &0;
_n -= 0;
_n -= &0;
_n += ZERO;
_n += &ZERO;
_n -= ZERO;
_n -= &ZERO;
_n /= 99;
_n /= &99;
_n %= 99;
@ -190,10 +197,18 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri
_n *= &0;
_n *= 1;
_n *= &1;
_n *= ZERO;
_n *= &ZERO;
_n *= ONE;
_n *= &ONE;
_n += -0;
_n += &-0;
_n -= -0;
_n -= &-0;
_n += -ZERO;
_n += &-ZERO;
_n -= -ZERO;
_n -= &-ZERO;
_n /= -99;
_n /= &-99;
_n %= -99;
@ -208,10 +223,18 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri
_n = _n + &0;
_n = 0 + _n;
_n = &0 + _n;
_n = _n + ZERO;
_n = _n + &ZERO;
_n = ZERO + _n;
_n = &ZERO + _n;
_n = _n - 0;
_n = _n - &0;
_n = 0 - _n;
_n = &0 - _n;
_n = _n - ZERO;
_n = _n - &ZERO;
_n = ZERO - _n;
_n = &ZERO - _n;
_n = _n / 99;
_n = _n / &99;
_n = _n % 99;
@ -222,6 +245,10 @@ pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_tri
_n = &0 * _n;
_n = _n * 1;
_n = _n * &1;
_n = ZERO * _n;
_n = &ZERO * _n;
_n = _n * ONE;
_n = _n * &ONE;
_n = 1 * _n;
_n = &1 * _n;
_n = 23 + 85;

View file

@ -1,5 +1,5 @@
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:243:5
--> $DIR/arithmetic_side_effects.rs:270:5
|
LL | _n += 1;
| ^^^^^^^
@ -7,589 +7,589 @@ LL | _n += 1;
= note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:244:5
--> $DIR/arithmetic_side_effects.rs:271:5
|
LL | _n += &1;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:245:5
--> $DIR/arithmetic_side_effects.rs:272:5
|
LL | _n -= 1;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:246:5
--> $DIR/arithmetic_side_effects.rs:273:5
|
LL | _n -= &1;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:247:5
--> $DIR/arithmetic_side_effects.rs:274:5
|
LL | _n /= 0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:248:5
--> $DIR/arithmetic_side_effects.rs:275:5
|
LL | _n /= &0;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:249:5
--> $DIR/arithmetic_side_effects.rs:276:5
|
LL | _n %= 0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:250:5
--> $DIR/arithmetic_side_effects.rs:277:5
|
LL | _n %= &0;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:251:5
--> $DIR/arithmetic_side_effects.rs:278:5
|
LL | _n *= 2;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:252:5
--> $DIR/arithmetic_side_effects.rs:279:5
|
LL | _n *= &2;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:253:5
--> $DIR/arithmetic_side_effects.rs:280:5
|
LL | _n += -1;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:254:5
--> $DIR/arithmetic_side_effects.rs:281:5
|
LL | _n += &-1;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:255:5
--> $DIR/arithmetic_side_effects.rs:282:5
|
LL | _n -= -1;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:256:5
--> $DIR/arithmetic_side_effects.rs:283:5
|
LL | _n -= &-1;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:257:5
--> $DIR/arithmetic_side_effects.rs:284:5
|
LL | _n /= -0;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:258:5
--> $DIR/arithmetic_side_effects.rs:285:5
|
LL | _n /= &-0;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:259:5
--> $DIR/arithmetic_side_effects.rs:286:5
|
LL | _n %= -0;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:260:5
--> $DIR/arithmetic_side_effects.rs:287:5
|
LL | _n %= &-0;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:261:5
--> $DIR/arithmetic_side_effects.rs:288:5
|
LL | _n *= -2;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:262:5
--> $DIR/arithmetic_side_effects.rs:289:5
|
LL | _n *= &-2;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:263:5
--> $DIR/arithmetic_side_effects.rs:290:5
|
LL | _custom += Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:264:5
--> $DIR/arithmetic_side_effects.rs:291:5
|
LL | _custom += &Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:265:5
--> $DIR/arithmetic_side_effects.rs:292:5
|
LL | _custom -= Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:266:5
--> $DIR/arithmetic_side_effects.rs:293:5
|
LL | _custom -= &Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:267:5
--> $DIR/arithmetic_side_effects.rs:294:5
|
LL | _custom /= Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:268:5
--> $DIR/arithmetic_side_effects.rs:295:5
|
LL | _custom /= &Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:269:5
--> $DIR/arithmetic_side_effects.rs:296:5
|
LL | _custom %= Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:270:5
--> $DIR/arithmetic_side_effects.rs:297:5
|
LL | _custom %= &Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:271:5
--> $DIR/arithmetic_side_effects.rs:298:5
|
LL | _custom *= Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:272:5
--> $DIR/arithmetic_side_effects.rs:299:5
|
LL | _custom *= &Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:273:5
--> $DIR/arithmetic_side_effects.rs:300:5
|
LL | _custom += -Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:274:5
--> $DIR/arithmetic_side_effects.rs:301:5
|
LL | _custom += &-Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:275:5
--> $DIR/arithmetic_side_effects.rs:302:5
|
LL | _custom -= -Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:276:5
--> $DIR/arithmetic_side_effects.rs:303:5
|
LL | _custom -= &-Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:277:5
--> $DIR/arithmetic_side_effects.rs:304:5
|
LL | _custom /= -Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:278:5
--> $DIR/arithmetic_side_effects.rs:305:5
|
LL | _custom /= &-Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:279:5
--> $DIR/arithmetic_side_effects.rs:306:5
|
LL | _custom %= -Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:280:5
--> $DIR/arithmetic_side_effects.rs:307:5
|
LL | _custom %= &-Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:281:5
--> $DIR/arithmetic_side_effects.rs:308:5
|
LL | _custom *= -Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:282:5
--> $DIR/arithmetic_side_effects.rs:309:5
|
LL | _custom *= &-Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:285:10
--> $DIR/arithmetic_side_effects.rs:312:10
|
LL | _n = _n + 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:286:10
--> $DIR/arithmetic_side_effects.rs:313:10
|
LL | _n = _n + &1;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:287:10
--> $DIR/arithmetic_side_effects.rs:314:10
|
LL | _n = 1 + _n;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:288:10
--> $DIR/arithmetic_side_effects.rs:315:10
|
LL | _n = &1 + _n;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:289:10
--> $DIR/arithmetic_side_effects.rs:316:10
|
LL | _n = _n - 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:290:10
--> $DIR/arithmetic_side_effects.rs:317:10
|
LL | _n = _n - &1;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:291:10
--> $DIR/arithmetic_side_effects.rs:318:10
|
LL | _n = 1 - _n;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:292:10
--> $DIR/arithmetic_side_effects.rs:319:10
|
LL | _n = &1 - _n;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:293:10
--> $DIR/arithmetic_side_effects.rs:320:10
|
LL | _n = _n / 0;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:294:10
--> $DIR/arithmetic_side_effects.rs:321:10
|
LL | _n = _n / &0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:295:10
--> $DIR/arithmetic_side_effects.rs:322:10
|
LL | _n = _n % 0;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:296:10
--> $DIR/arithmetic_side_effects.rs:323:10
|
LL | _n = _n % &0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:297:10
--> $DIR/arithmetic_side_effects.rs:324:10
|
LL | _n = _n * 2;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:298:10
--> $DIR/arithmetic_side_effects.rs:325:10
|
LL | _n = _n * &2;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:299:10
--> $DIR/arithmetic_side_effects.rs:326:10
|
LL | _n = 2 * _n;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:300:10
--> $DIR/arithmetic_side_effects.rs:327:10
|
LL | _n = &2 * _n;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:301:10
--> $DIR/arithmetic_side_effects.rs:328:10
|
LL | _n = 23 + &85;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:302:10
--> $DIR/arithmetic_side_effects.rs:329:10
|
LL | _n = &23 + 85;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:303:10
--> $DIR/arithmetic_side_effects.rs:330:10
|
LL | _n = &23 + &85;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:304:15
--> $DIR/arithmetic_side_effects.rs:331:15
|
LL | _custom = _custom + _custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:305:15
--> $DIR/arithmetic_side_effects.rs:332:15
|
LL | _custom = _custom + &_custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:306:15
--> $DIR/arithmetic_side_effects.rs:333:15
|
LL | _custom = Custom + _custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:307:15
--> $DIR/arithmetic_side_effects.rs:334:15
|
LL | _custom = &Custom + _custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:308:15
--> $DIR/arithmetic_side_effects.rs:335:15
|
LL | _custom = _custom - Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:309:15
--> $DIR/arithmetic_side_effects.rs:336:15
|
LL | _custom = _custom - &Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:310:15
--> $DIR/arithmetic_side_effects.rs:337:15
|
LL | _custom = Custom - _custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:311:15
--> $DIR/arithmetic_side_effects.rs:338:15
|
LL | _custom = &Custom - _custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:312:15
--> $DIR/arithmetic_side_effects.rs:339:15
|
LL | _custom = _custom / Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:313:15
--> $DIR/arithmetic_side_effects.rs:340:15
|
LL | _custom = _custom / &Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:314:15
--> $DIR/arithmetic_side_effects.rs:341:15
|
LL | _custom = _custom % Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:315:15
--> $DIR/arithmetic_side_effects.rs:342:15
|
LL | _custom = _custom % &Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:316:15
--> $DIR/arithmetic_side_effects.rs:343:15
|
LL | _custom = _custom * Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:317:15
--> $DIR/arithmetic_side_effects.rs:344:15
|
LL | _custom = _custom * &Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:318:15
--> $DIR/arithmetic_side_effects.rs:345:15
|
LL | _custom = Custom * _custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:319:15
--> $DIR/arithmetic_side_effects.rs:346:15
|
LL | _custom = &Custom * _custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:320:15
--> $DIR/arithmetic_side_effects.rs:347:15
|
LL | _custom = Custom + &Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:321:15
--> $DIR/arithmetic_side_effects.rs:348:15
|
LL | _custom = &Custom + Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:322:15
--> $DIR/arithmetic_side_effects.rs:349:15
|
LL | _custom = &Custom + &Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:325:10
--> $DIR/arithmetic_side_effects.rs:352:10
|
LL | _n = -_n;
| ^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:326:10
--> $DIR/arithmetic_side_effects.rs:353:10
|
LL | _n = -&_n;
| ^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:327:15
--> $DIR/arithmetic_side_effects.rs:354:15
|
LL | _custom = -_custom;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:328:15
--> $DIR/arithmetic_side_effects.rs:355:15
|
LL | _custom = -&_custom;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:337:5
--> $DIR/arithmetic_side_effects.rs:364:5
|
LL | 1 + i;
| ^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:338:5
--> $DIR/arithmetic_side_effects.rs:365:5
|
LL | i * 2;
| ^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:340:5
--> $DIR/arithmetic_side_effects.rs:367:5
|
LL | i - 2 + 2 - i;
| ^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:341:5
--> $DIR/arithmetic_side_effects.rs:368:5
|
LL | -i;
| ^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:342:5
--> $DIR/arithmetic_side_effects.rs:369:5
|
LL | i >> 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:343:5
--> $DIR/arithmetic_side_effects.rs:370:5
|
LL | i << 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:352:5
--> $DIR/arithmetic_side_effects.rs:379:5
|
LL | i += 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:353:5
--> $DIR/arithmetic_side_effects.rs:380:5
|
LL | i -= 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:354:5
--> $DIR/arithmetic_side_effects.rs:381:5
|
LL | i *= 2;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:356:5
--> $DIR/arithmetic_side_effects.rs:383:5
|
LL | i /= 0;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:358:5
--> $DIR/arithmetic_side_effects.rs:385:5
|
LL | i /= var1;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:359:5
--> $DIR/arithmetic_side_effects.rs:386:5
|
LL | i /= var2;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:361:5
--> $DIR/arithmetic_side_effects.rs:388:5
|
LL | i %= 0;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:363:5
--> $DIR/arithmetic_side_effects.rs:390:5
|
LL | i %= var1;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:364:5
--> $DIR/arithmetic_side_effects.rs:391:5
|
LL | i %= var2;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:365:5
--> $DIR/arithmetic_side_effects.rs:392:5
|
LL | i <<= 3;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:366:5
--> $DIR/arithmetic_side_effects.rs:393:5
|
LL | i >>= 2;
| ^^^^^^^

View file

@ -33,6 +33,7 @@ fn main() {
let _vec4: Box<_> = Box::<Vec<bool>>::default();
let _more = ret_ty_fn();
call_ty_fn(Box::default());
issue_10381();
}
fn ret_ty_fn() -> Box<bool> {
@ -65,3 +66,20 @@ fn issue_10089() {
let _ = Box::<WeirdPathed>::default();
};
}
fn issue_10381() {
#[derive(Default)]
pub struct Foo {}
pub trait Bar {}
impl Bar for Foo {}
fn maybe_get_bar(i: u32) -> Option<Box<dyn Bar>> {
if i % 2 == 0 {
Some(Box::<Foo>::default())
} else {
None
}
}
assert!(maybe_get_bar(2).is_some());
}

View file

@ -33,6 +33,7 @@ fn main() {
let _vec4: Box<_> = Box::new(Vec::from([false; 0]));
let _more = ret_ty_fn();
call_ty_fn(Box::new(u8::default()));
issue_10381();
}
fn ret_ty_fn() -> Box<bool> {
@ -65,3 +66,20 @@ fn issue_10089() {
let _ = Box::new(WeirdPathed::default());
};
}
fn issue_10381() {
#[derive(Default)]
pub struct Foo {}
pub trait Bar {}
impl Bar for Foo {}
fn maybe_get_bar(i: u32) -> Option<Box<dyn Bar>> {
if i % 2 == 0 {
Some(Box::new(Foo::default()))
} else {
None
}
}
assert!(maybe_get_bar(2).is_some());
}

View file

@ -73,22 +73,28 @@ LL | call_ty_fn(Box::new(u8::default()));
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()`
error: `Box::new(_)` of default value
--> $DIR/box_default.rs:39:5
--> $DIR/box_default.rs:40:5
|
LL | Box::new(bool::default())
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<bool>::default()`
error: `Box::new(_)` of default value
--> $DIR/box_default.rs:56:28
--> $DIR/box_default.rs:57:28
|
LL | let _: Box<dyn Read> = Box::new(ImplementsDefault::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<ImplementsDefault>::default()`
error: `Box::new(_)` of default value
--> $DIR/box_default.rs:65:17
--> $DIR/box_default.rs:66:17
|
LL | let _ = Box::new(WeirdPathed::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<WeirdPathed>::default()`
error: aborting due to 15 previous errors
error: `Box::new(_)` of default value
--> $DIR/box_default.rs:78:18
|
LL | Some(Box::new(Foo::default()))
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<Foo>::default()`
error: aborting due to 16 previous errors

View file

@ -5,7 +5,7 @@
fn main() {
let s = String::from("String");
let _ = s.as_bytes().get(3);
let _ = &s.as_bytes().get(3);
let _ = s[..].as_bytes().get(3);
let _ = s.as_bytes().get(3).copied();
let _ = &s.as_bytes()[3];
let _ = s[..].as_bytes().get(3).copied();
}

View file

@ -6,6 +6,6 @@
fn main() {
let s = String::from("String");
let _ = s.bytes().nth(3);
let _ = &s.bytes().nth(3);
let _ = &s.bytes().nth(3).unwrap();
let _ = s[..].bytes().nth(3);
}

View file

@ -2,21 +2,21 @@ error: called `.bytes().nth()` on a `String`
--> $DIR/bytes_nth.rs:8:13
|
LL | let _ = s.bytes().nth(3);
| ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3)`
| ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3).copied()`
|
= note: `-D clippy::bytes-nth` implied by `-D warnings`
error: called `.bytes().nth()` on a `String`
error: called `.bytes().nth().unwrap()` on a `String`
--> $DIR/bytes_nth.rs:9:14
|
LL | let _ = &s.bytes().nth(3);
| ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3)`
LL | let _ = &s.bytes().nth(3).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.as_bytes()[3]`
error: called `.bytes().nth()` on a `str`
--> $DIR/bytes_nth.rs:10:13
|
LL | let _ = s[..].bytes().nth(3);
| ^^^^^^^^^^^^^^^^^^^^ help: try: `s[..].as_bytes().get(3)`
| ^^^^^^^^^^^^^^^^^^^^ help: try: `s[..].as_bytes().get(3).copied()`
error: aborting due to 3 previous errors

View file

@ -42,7 +42,7 @@ error: casting `f32` to `i32` may truncate the value
LL | 1f32 as i32;
| ^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
= note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
help: ... or use `try_from` and handle the error accordingly
|
@ -55,7 +55,7 @@ error: casting `f32` to `u32` may truncate the value
LL | 1f32 as u32;
| ^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | u32::try_from(1f32);
@ -75,7 +75,7 @@ error: casting `f64` to `f32` may truncate the value
LL | 1f64 as f32;
| ^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | f32::try_from(1f64);
@ -87,7 +87,7 @@ error: casting `i32` to `i8` may truncate the value
LL | 1i32 as i8;
| ^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | i8::try_from(1i32);
@ -99,7 +99,7 @@ error: casting `i32` to `u8` may truncate the value
LL | 1i32 as u8;
| ^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | u8::try_from(1i32);
@ -111,7 +111,7 @@ error: casting `f64` to `isize` may truncate the value
LL | 1f64 as isize;
| ^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | isize::try_from(1f64);
@ -123,7 +123,7 @@ error: casting `f64` to `usize` may truncate the value
LL | 1f64 as usize;
| ^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | usize::try_from(1f64);
@ -141,7 +141,7 @@ error: casting `u32` to `u16` may truncate the value
LL | 1f32 as u32 as u16;
| ^^^^^^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | u16::try_from(1f32 as u32);
@ -153,7 +153,7 @@ error: casting `f32` to `u32` may truncate the value
LL | 1f32 as u32 as u16;
| ^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | u32::try_from(1f32) as u16;
@ -215,7 +215,7 @@ error: casting `i64` to `i8` may truncate the value
LL | (-99999999999i64).min(1) as i8; // should be linted because signed
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | i8::try_from((-99999999999i64).min(1)); // should be linted because signed
@ -227,7 +227,7 @@ error: casting `u64` to `u8` may truncate the value
LL | 999999u64.clamp(0, 256) as u8; // should still be linted
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | u8::try_from(999999u64.clamp(0, 256)); // should still be linted
@ -239,7 +239,7 @@ error: casting `main::E2` to `u8` may truncate the value
LL | let _ = self as u8;
| ^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | let _ = u8::try_from(self);
@ -259,7 +259,7 @@ error: casting `main::E5` to `i8` may truncate the value
LL | let _ = self as i8;
| ^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | let _ = i8::try_from(self);
@ -277,7 +277,7 @@ error: casting `main::E6` to `i16` may truncate the value
LL | let _ = self as i16;
| ^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | let _ = i16::try_from(self);
@ -289,7 +289,7 @@ error: casting `main::E7` to `usize` may truncate the value on targets with 32-b
LL | let _ = self as usize;
| ^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | let _ = usize::try_from(self);
@ -301,7 +301,7 @@ error: casting `main::E10` to `u16` may truncate the value
LL | let _ = self as u16;
| ^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | let _ = u16::try_from(self);
@ -313,7 +313,7 @@ error: casting `u32` to `u8` may truncate the value
LL | let c = (q >> 16) as u8;
| ^^^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | let c = u8::try_from((q >> 16));
@ -325,7 +325,7 @@ error: casting `u32` to `u8` may truncate the value
LL | let c = (q / 1000) as u8;
| ^^^^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | let c = u8::try_from((q / 1000));

View file

@ -4,7 +4,7 @@ error: casting `isize` to `i8` may truncate the value
LL | 1isize as i8;
| ^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
= note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
help: ... or use `try_from` and handle the error accordingly
|
@ -43,7 +43,7 @@ error: casting `isize` to `i32` may truncate the value on targets with 64-bit wi
LL | 1isize as i32;
| ^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | i32::try_from(1isize);
@ -55,7 +55,7 @@ error: casting `isize` to `u32` may truncate the value on targets with 64-bit wi
LL | 1isize as u32;
| ^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | u32::try_from(1isize);
@ -67,7 +67,7 @@ error: casting `usize` to `u32` may truncate the value on targets with 64-bit wi
LL | 1usize as u32;
| ^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | u32::try_from(1usize);
@ -79,7 +79,7 @@ error: casting `usize` to `i32` may truncate the value on targets with 64-bit wi
LL | 1usize as i32;
| ^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | i32::try_from(1usize);
@ -99,7 +99,7 @@ error: casting `i64` to `isize` may truncate the value on targets with 32-bit wi
LL | 1i64 as isize;
| ^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | isize::try_from(1i64);
@ -111,7 +111,7 @@ error: casting `i64` to `usize` may truncate the value on targets with 32-bit wi
LL | 1i64 as usize;
| ^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | usize::try_from(1i64);
@ -123,7 +123,7 @@ error: casting `u64` to `isize` may truncate the value on targets with 32-bit wi
LL | 1u64 as isize;
| ^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | isize::try_from(1u64);
@ -141,7 +141,7 @@ error: casting `u64` to `usize` may truncate the value on targets with 32-bit wi
LL | 1u64 as usize;
| ^^^^^^^^^^^^^
|
= help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
help: ... or use `try_from` and handle the error accordingly
|
LL | usize::try_from(1u64);

View file

@ -0,0 +1,9 @@
// https://github.com/rust-lang/rust/issues/107147
#![warn(clippy::needless_pass_by_value)]
struct Foo<'a>(&'a [(); 100]);
fn test(x: Foo<'_>) {}
fn main() {}

View file

@ -0,0 +1,15 @@
error: this argument is passed by value, but not consumed in the function body
--> $DIR/needless_pass_by_value-w-late-bound.rs:7:12
|
LL | fn test(x: Foo<'_>) {}
| ^^^^^^^ help: consider taking a reference instead: `&Foo<'_>`
|
help: consider marking this type as `Copy`
--> $DIR/needless_pass_by_value-w-late-bound.rs:5:1
|
LL | struct Foo<'a>(&'a [(); 100]);
| ^^^^^^^^^^^^^^
= note: `-D clippy::needless-pass-by-value` implied by `-D warnings`
error: aborting due to previous error

View file

@ -78,7 +78,7 @@ fn test_allowed() {
/// This test has [a `link_with_underscores`][chunked-example] inside it. See #823.
/// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues)
/// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link].
/// It can also be [`inline_link2`].
/// It can also be [inline_link2]. A link to [StackOverflow](https://stackoverflow.com) is also acceptable.
///
/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example
/// [inline_link]: https://foobar

View file

@ -75,10 +75,10 @@ fn test_units() {
fn test_allowed() {
}
/// This test has [a link_with_underscores][chunked-example] inside it. See #823.
/// This test has [a `link_with_underscores`][chunked-example] inside it. See #823.
/// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues)
/// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link].
/// It can also be [inline_link2].
/// It can also be [inline_link2]. A link to [StackOverflow](https://stackoverflow.com) is also acceptable.
///
/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example
/// [inline_link]: https://foobar

View file

@ -142,28 +142,6 @@ help: try
LL | /// `be_sure_we_got_to_the_end_of_it`
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:78:22
|
LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823.
| ^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL | /// This test has [a `link_with_underscores`][chunked-example] inside it. See #823.
| ~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:81:21
|
LL | /// It can also be [inline_link2].
| ^^^^^^^^^^^^
|
help: try
|
LL | /// It can also be [`inline_link2`].
| ~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:91:5
|
@ -329,5 +307,5 @@ help: try
LL | /// An iterator over `mycrate::Collection`'s values.
| ~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 30 previous errors
error: aborting due to 28 previous errors

View file

@ -152,4 +152,18 @@ fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMa
});
}
// Issue 10331
// do not suggest a bad expansion because the compiler unrolls the first
// occurrence of the loop
pub fn issue_10331() {
let mut m = HashMap::new();
let mut i = 0;
let mut x = 0;
while !m.contains_key(&x) {
m.insert(x, i);
i += 1;
x += 1;
}
}
fn main() {}

View file

@ -156,4 +156,18 @@ fn hash_map<K: Eq + Hash + Copy, V: Copy>(m: &mut HashMap<K, V>, m2: &mut HashMa
}
}
// Issue 10331
// do not suggest a bad expansion because the compiler unrolls the first
// occurrence of the loop
pub fn issue_10331() {
let mut m = HashMap::new();
let mut i = 0;
let mut x = 0;
while !m.contains_key(&x) {
m.insert(x, i);
i += 1;
x += 1;
}
}
fn main() {}

View file

@ -269,6 +269,9 @@ fn main() {
trait WithAssoc {
type Assoc: ?Sized;
fn to_assoc(&self) -> &Self::Assoc {
panic!()
}
}
impl WithAssoc for String {
type Assoc = str;
@ -281,4 +284,15 @@ fn main() {
// Issue #9901
fn takes_ref(_: &i32) {}
takes_ref(*Box::new(&0i32));
// Issue #10384
impl<'a> WithAssoc for &'a u32 {
type Assoc = dyn core::fmt::Display;
fn to_assoc(&self) -> &Self::Assoc {
*self
}
}
fn return_dyn_assoc<'a>(x: &'a &'a u32) -> &'a <&'a u32 as WithAssoc>::Assoc {
*x
}
}

View file

@ -269,6 +269,9 @@ fn main() {
trait WithAssoc {
type Assoc: ?Sized;
fn to_assoc(&self) -> &Self::Assoc {
panic!()
}
}
impl WithAssoc for String {
type Assoc = str;
@ -281,4 +284,15 @@ fn main() {
// Issue #9901
fn takes_ref(_: &i32) {}
takes_ref(*Box::new(&0i32));
// Issue #10384
impl<'a> WithAssoc for &'a u32 {
type Assoc = dyn core::fmt::Display;
fn to_assoc(&self) -> &Self::Assoc {
*self
}
}
fn return_dyn_assoc<'a>(x: &'a &'a u32) -> &'a <&'a u32 as WithAssoc>::Assoc {
*x
}
}

View file

@ -1,11 +1,17 @@
#![allow(unused, clippy::needless_lifetimes)]
#![warn(clippy::extra_unused_type_parameters)]
fn unused_ty<T>(x: u8) {}
fn unused_ty<T>(x: u8) {
unimplemented!()
}
fn unused_multi<T, U>(x: u8) {}
fn unused_multi<T, U>(x: u8) {
unimplemented!()
}
fn unused_with_lt<'a, T>(x: &'a u8) {}
fn unused_with_lt<'a, T>(x: &'a u8) {
unimplemented!()
}
fn used_ty<T>(x: T, y: u8) {}
@ -15,15 +21,20 @@ fn used_ret<T: Default>(x: u8) -> T {
T::default()
}
fn unused_bounded<T: Default, U>(x: U) {}
fn unused_bounded<T: Default, U>(x: U) {
unimplemented!();
}
fn unused_where_clause<T, U>(x: U)
where
T: Default,
{
unimplemented!();
}
fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {}
fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {
unimplemented!();
}
fn used_opaque<A>(iter: impl Iterator<Item = A>) -> usize {
iter.count()
@ -46,7 +57,9 @@ fn used_closure<T: Default + ToString>() -> impl Fn() {
struct S;
impl S {
fn unused_ty_impl<T>(&self) {}
fn unused_ty_impl<T>(&self) {
unimplemented!()
}
}
// Don't lint on trait methods
@ -66,4 +79,32 @@ where
.filter_map(move |(i, a)| if i == index { None } else { Some(a) })
}
fn unused_opaque<A, B>(dummy: impl Default) {
unimplemented!()
}
mod unexported_trait_bounds {
mod private {
pub trait Private {}
}
fn priv_trait_bound<T: private::Private>() {
unimplemented!();
}
fn unused_with_priv_trait_bound<T: private::Private, U>() {
unimplemented!();
}
}
mod issue10319 {
fn assert_send<T: Send>() {}
fn assert_send_where<T>()
where
T: Send,
{
}
}
fn main() {}

View file

@ -1,38 +1,38 @@
error: type parameter goes unused in function definition
--> $DIR/extra_unused_type_parameters.rs:4:13
|
LL | fn unused_ty<T>(x: u8) {}
LL | fn unused_ty<T>(x: u8) {
| ^^^
|
= help: consider removing the parameter
= note: `-D clippy::extra-unused-type-parameters` implied by `-D warnings`
error: type parameters go unused in function definition
--> $DIR/extra_unused_type_parameters.rs:6:16
--> $DIR/extra_unused_type_parameters.rs:8:16
|
LL | fn unused_multi<T, U>(x: u8) {}
LL | fn unused_multi<T, U>(x: u8) {
| ^^^^^^
|
= help: consider removing the parameters
error: type parameter goes unused in function definition
--> $DIR/extra_unused_type_parameters.rs:8:23
--> $DIR/extra_unused_type_parameters.rs:12:23
|
LL | fn unused_with_lt<'a, T>(x: &'a u8) {}
LL | fn unused_with_lt<'a, T>(x: &'a u8) {
| ^
|
= help: consider removing the parameter
error: type parameter goes unused in function definition
--> $DIR/extra_unused_type_parameters.rs:18:19
--> $DIR/extra_unused_type_parameters.rs:24:19
|
LL | fn unused_bounded<T: Default, U>(x: U) {}
LL | fn unused_bounded<T: Default, U>(x: U) {
| ^^^^^^^^^^^
|
= help: consider removing the parameter
error: type parameter goes unused in function definition
--> $DIR/extra_unused_type_parameters.rs:20:24
--> $DIR/extra_unused_type_parameters.rs:28:24
|
LL | fn unused_where_clause<T, U>(x: U)
| ^^
@ -40,20 +40,36 @@ LL | fn unused_where_clause<T, U>(x: U)
= help: consider removing the parameter
error: type parameters go unused in function definition
--> $DIR/extra_unused_type_parameters.rs:26:16
--> $DIR/extra_unused_type_parameters.rs:35:16
|
LL | fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {}
LL | fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {
| ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^
|
= help: consider removing the parameters
error: type parameter goes unused in function definition
--> $DIR/extra_unused_type_parameters.rs:49:22
--> $DIR/extra_unused_type_parameters.rs:60:22
|
LL | fn unused_ty_impl<T>(&self) {}
LL | fn unused_ty_impl<T>(&self) {
| ^^^
|
= help: consider removing the parameter
error: aborting due to 7 previous errors
error: type parameters go unused in function definition
--> $DIR/extra_unused_type_parameters.rs:82:17
|
LL | fn unused_opaque<A, B>(dummy: impl Default) {
| ^^^^^^
|
= help: consider removing the parameters
error: type parameter goes unused in function definition
--> $DIR/extra_unused_type_parameters.rs:95:58
|
LL | fn unused_with_priv_trait_bound<T: private::Private, U>() {
| ^
|
= help: consider removing the parameter
error: aborting due to 9 previous errors

View file

@ -1,4 +1,5 @@
// run-rustfix
// aux-build: proc_macro_with_span.rs
#![warn(clippy::useless_format)]
#![allow(
unused_tuple_struct_fields,
@ -9,6 +10,8 @@
clippy::uninlined_format_args
)]
extern crate proc_macro_with_span;
struct Foo(pub String);
macro_rules! foo {
@ -87,4 +90,7 @@ fn main() {
let _ = abc.to_string();
let xx = "xx";
let _ = xx.to_string();
// Issue #10148
println!(proc_macro_with_span::with_span!(""something ""));
}

View file

@ -1,4 +1,5 @@
// run-rustfix
// aux-build: proc_macro_with_span.rs
#![warn(clippy::useless_format)]
#![allow(
unused_tuple_struct_fields,
@ -9,6 +10,8 @@
clippy::uninlined_format_args
)]
extern crate proc_macro_with_span;
struct Foo(pub String);
macro_rules! foo {
@ -89,4 +92,7 @@ fn main() {
let _ = format!("{abc}");
let xx = "xx";
let _ = format!("{xx}");
// Issue #10148
println!(proc_macro_with_span::with_span!(""something ""));
}

View file

@ -1,5 +1,5 @@
error: useless use of `format!`
--> $DIR/format.rs:19:5
--> $DIR/format.rs:22:5
|
LL | format!("foo");
| ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
@ -7,19 +7,19 @@ LL | format!("foo");
= note: `-D clippy::useless-format` implied by `-D warnings`
error: useless use of `format!`
--> $DIR/format.rs:20:5
--> $DIR/format.rs:23:5
|
LL | format!("{{}}");
| ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()`
error: useless use of `format!`
--> $DIR/format.rs:21:5
--> $DIR/format.rs:24:5
|
LL | format!("{{}} abc {{}}");
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()`
error: useless use of `format!`
--> $DIR/format.rs:22:5
--> $DIR/format.rs:25:5
|
LL | / format!(
LL | | r##"foo {{}}
@ -34,67 +34,67 @@ LL ~ " bar"##.to_string();
|
error: useless use of `format!`
--> $DIR/format.rs:27:13
--> $DIR/format.rs:30:13
|
LL | let _ = format!("");
| ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()`
error: useless use of `format!`
--> $DIR/format.rs:29:5
--> $DIR/format.rs:32:5
|
LL | format!("{}", "foo");
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()`
error: useless use of `format!`
--> $DIR/format.rs:37:5
--> $DIR/format.rs:40:5
|
LL | format!("{}", arg);
| ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()`
error: useless use of `format!`
--> $DIR/format.rs:67:5
--> $DIR/format.rs:70:5
|
LL | format!("{}", 42.to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()`
error: useless use of `format!`
--> $DIR/format.rs:69:5
--> $DIR/format.rs:72:5
|
LL | format!("{}", x.display().to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()`
error: useless use of `format!`
--> $DIR/format.rs:73:18
--> $DIR/format.rs:76:18
|
LL | let _ = Some(format!("{}", a + "bar"));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"`
error: useless use of `format!`
--> $DIR/format.rs:77:22
--> $DIR/format.rs:80:22
|
LL | let _s: String = format!("{}", &*v.join("/n"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()`
error: useless use of `format!`
--> $DIR/format.rs:83:13
--> $DIR/format.rs:86:13
|
LL | let _ = format!("{x}");
| ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
error: useless use of `format!`
--> $DIR/format.rs:85:13
--> $DIR/format.rs:88:13
|
LL | let _ = format!("{y}", y = x);
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()`
error: useless use of `format!`
--> $DIR/format.rs:89:13
--> $DIR/format.rs:92:13
|
LL | let _ = format!("{abc}");
| ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()`
error: useless use of `format!`
--> $DIR/format.rs:91:13
--> $DIR/format.rs:94:13
|
LL | let _ = format!("{xx}");
| ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()`

View file

@ -0,0 +1,17 @@
#![allow(unused)]
#![warn(clippy::impl_trait_in_params)]
pub trait Trait {}
pub trait AnotherTrait<T> {}
// Should warn
pub fn a(_: impl Trait) {}
pub fn c<C: Trait>(_: C, _: impl Trait) {}
fn d(_: impl AnotherTrait<u32>) {}
// Shouldn't warn
pub fn b<B: Trait>(_: B) {}
fn e<T: AnotherTrait<u32>>(_: T) {}
fn main() {}

View file

@ -0,0 +1,25 @@
error: '`impl Trait` used as a function parameter'
--> $DIR/impl_trait_in_params.rs:8:13
|
LL | pub fn a(_: impl Trait) {}
| ^^^^^^^^^^
|
= note: `-D clippy::impl-trait-in-params` implied by `-D warnings`
help: add a type paremeter
|
LL | pub fn a<{ /* Generic name */ }: Trait>(_: impl Trait) {}
| +++++++++++++++++++++++++++++++
error: '`impl Trait` used as a function parameter'
--> $DIR/impl_trait_in_params.rs:9:29
|
LL | pub fn c<C: Trait>(_: C, _: impl Trait) {}
| ^^^^^^^^^^
|
help: add a type paremeter
|
LL | pub fn c<C: Trait, { /* Generic name */ }: Trait>(_: C, _: impl Trait) {}
| +++++++++++++++++++++++++++++++
error: aborting due to 2 previous errors

View file

@ -11,7 +11,7 @@ fn main() {
let _good = (
0b1011_i64,
0o1_234_u32,
0x0123_4567,
0x1_234_567,
1_2345_6789,
1234_f32,
1_234.12_f32,
@ -19,7 +19,7 @@ fn main() {
1.123_4_f32,
);
let _bad = (
0b11_0110_i64,
0b1_10110_i64,
0xdead_beef_usize,
123_456_f32,
123_456.12_f32,

View file

@ -1,22 +1,10 @@
error: digits of hex or binary literal not grouped by four
--> $DIR/large_digit_groups.rs:14:9
|
LL | 0x1_234_567,
| ^^^^^^^^^^^ help: consider: `0x0123_4567`
|
= note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
error: digits of hex or binary literal not grouped by four
--> $DIR/large_digit_groups.rs:22:9
|
LL | 0b1_10110_i64,
| ^^^^^^^^^^^^^ help: consider: `0b11_0110_i64`
error: digits of hex or binary literal not grouped by four
error: digits of hex, binary or octal literal not in groups of equal size
--> $DIR/large_digit_groups.rs:23:9
|
LL | 0xd_e_adbee_f_usize,
| ^^^^^^^^^^^^^^^^^^^ help: consider: `0xdead_beef_usize`
|
= note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
error: digit groups should be smaller
--> $DIR/large_digit_groups.rs:24:9
@ -44,5 +32,5 @@ error: digit groups should be smaller
LL | 1_23456.12345_6_f64,
| ^^^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_456_f64`
error: aborting due to 7 previous errors
error: aborting due to 5 previous errors

View file

@ -0,0 +1,54 @@
#![allow(unused)]
#![warn(clippy::let_underscore_untyped)]
use std::future::Future;
use std::{boxed::Box, fmt::Display};
fn a() -> u32 {
1
}
fn b<T>(x: T) -> T {
x
}
fn c() -> impl Display {
1
}
fn d(x: &u32) -> &u32 {
x
}
fn e() -> Result<u32, ()> {
Ok(1)
}
fn f() -> Box<dyn Display> {
Box::new(1)
}
fn main() {
let _ = a();
let _ = b(1);
let _ = c();
let _ = d(&1);
let _ = e();
let _ = f();
_ = a();
_ = b(1);
_ = c();
_ = d(&1);
_ = e();
_ = f();
let _: u32 = a();
let _: u32 = b(1);
let _: &u32 = d(&1);
let _: Result<_, _> = e();
let _: Box<_> = f();
#[allow(clippy::let_underscore_untyped)]
let _ = a();
}

View file

@ -0,0 +1,51 @@
error: non-binding `let` without a type annotation
--> $DIR/let_underscore_untyped.rs:32:5
|
LL | let _ = a();
| ^^^^^^^^^^^^
|
= help: consider adding a type annotation or removing the `let` keyword
= note: `-D clippy::let-underscore-untyped` implied by `-D warnings`
error: non-binding `let` without a type annotation
--> $DIR/let_underscore_untyped.rs:33:5
|
LL | let _ = b(1);
| ^^^^^^^^^^^^^
|
= help: consider adding a type annotation or removing the `let` keyword
error: non-binding `let` without a type annotation
--> $DIR/let_underscore_untyped.rs:34:5
|
LL | let _ = c();
| ^^^^^^^^^^^^
|
= help: consider adding a type annotation or removing the `let` keyword
error: non-binding `let` without a type annotation
--> $DIR/let_underscore_untyped.rs:35:5
|
LL | let _ = d(&1);
| ^^^^^^^^^^^^^^
|
= help: consider adding a type annotation or removing the `let` keyword
error: non-binding `let` without a type annotation
--> $DIR/let_underscore_untyped.rs:36:5
|
LL | let _ = e();
| ^^^^^^^^^^^^
|
= help: consider adding a type annotation or removing the `let` keyword
error: non-binding `let` without a type annotation
--> $DIR/let_underscore_untyped.rs:37:5
|
LL | let _ = f();
| ^^^^^^^^^^^^
|
= help: consider adding a type annotation or removing the `let` keyword
error: aborting due to 6 previous errors

View file

@ -121,7 +121,7 @@ error: digits grouped inconsistently by underscores
LL | let fail23 = 3__16___23;
| ^^^^^^^^^^ help: consider: `31_623`
error: digits of hex or binary literal not grouped by four
error: digits of hex, binary or octal literal not in groups of equal size
--> $DIR/literals.rs:38:18
|
LL | let fail24 = 0xAB_ABC_AB;
@ -129,12 +129,6 @@ LL | let fail24 = 0xAB_ABC_AB;
|
= note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
error: digits of hex or binary literal not grouped by four
--> $DIR/literals.rs:39:18
|
LL | let fail25 = 0b01_100_101;
| ^^^^^^^^^^^^ help: consider: `0b0110_0101`
error: this is a decimal constant
--> $DIR/literals.rs:46:13
|
@ -168,5 +162,5 @@ help: if you mean to use a decimal constant, remove the `0` to avoid confusion
LL | let _ = 89;
| ~~
error: aborting due to 21 previous errors
error: aborting due to 20 previous errors

View file

@ -248,4 +248,15 @@ fn not_fire() {
Some(value) => value,
_ => macro_call!(),
};
// Issue 10296
// The let/else block in the else part is not divergent despite the presence of return
let _x = if let Some(x) = Some(1) {
x
} else {
let Some(_z) = Some(3) else {
return
};
1
};
}

View file

@ -42,13 +42,13 @@ fn fire() {
loop {
// More complex pattern for the identity arm and diverging arm
let v = match h() {
(Some(_), Some(_)) | (None, None) => continue,
(Some(v), None) | (None, Some(v)) => v,
(Some(_), Some(_)) | (None, None) => continue,
};
// Custom enums are supported as long as the "else" arm is a simple _
let v = match build_enum() {
_ => continue,
Variant::Bar(v) | Variant::Baz(v) => v,
_ => continue,
};
}
@ -71,6 +71,12 @@ fn fire() {
Variant::Bar(_) | Variant::Baz(_) => (),
_ => return,
};
let data = [1_u8, 2, 3, 4, 0, 0, 0, 0];
let data = match data.as_slice() {
[data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ .., 0] => data,
_ => return,
};
}
fn not_fire() {
@ -125,4 +131,23 @@ fn not_fire() {
Ok(v) | Err(Variant::Bar(v) | Variant::Baz(v)) => v,
Err(Variant::Foo) => return,
};
// Issue 10241
// The non-divergent arm arrives in second position and
// may cover values already matched in the first arm.
let v = match h() {
(Some(_), Some(_)) | (None, None) => return,
(Some(v), _) | (None, Some(v)) => v,
};
let v = match build_enum() {
_ => return,
Variant::Bar(v) | Variant::Baz(v) => v,
};
let data = [1_u8, 2, 3, 4, 0, 0, 0, 0];
let data = match data.as_slice() {
[] | [0, 0] => return,
[data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ ..] => data,
};
}

View file

@ -22,8 +22,8 @@ error: this could be rewritten as `let...else`
--> $DIR/manual_let_else_match.rs:44:9
|
LL | / let v = match h() {
LL | | (Some(_), Some(_)) | (None, None) => continue,
LL | | (Some(v), None) | (None, Some(v)) => v,
LL | | (Some(_), Some(_)) | (None, None) => continue,
LL | | };
| |__________^ help: consider writing: `let ((Some(v), None) | (None, Some(v))) = h() else { continue };`
@ -31,8 +31,8 @@ error: this could be rewritten as `let...else`
--> $DIR/manual_let_else_match.rs:49:9
|
LL | / let v = match build_enum() {
LL | | _ => continue,
LL | | Variant::Bar(v) | Variant::Baz(v) => v,
LL | | _ => continue,
LL | | };
| |__________^ help: consider writing: `let (Variant::Bar(v) | Variant::Baz(v)) = build_enum() else { continue };`
@ -63,5 +63,14 @@ LL | | _ => return,
LL | | };
| |______^ help: consider writing: `let (Variant::Bar(_) | Variant::Baz(_)) = f else { return };`
error: aborting due to 7 previous errors
error: this could be rewritten as `let...else`
--> $DIR/manual_let_else_match.rs:76:5
|
LL | / let data = match data.as_slice() {
LL | | [data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ .., 0] => data,
LL | | _ => return,
LL | | };
| |______^ help: consider writing: `let ([data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ .., 0]) = data.as_slice() else { return };`
error: aborting due to 8 previous errors

View file

@ -1,6 +1,7 @@
// run-rustfix
#![warn(clippy::all, clippy::pedantic)]
#![allow(clippy::let_underscore_untyped)]
#![allow(clippy::missing_docs_in_private_items)]
#![allow(clippy::map_identity)]
#![allow(clippy::redundant_closure)]

View file

@ -1,6 +1,7 @@
// run-rustfix
#![warn(clippy::all, clippy::pedantic)]
#![allow(clippy::let_underscore_untyped)]
#![allow(clippy::missing_docs_in_private_items)]
#![allow(clippy::map_identity)]
#![allow(clippy::redundant_closure)]

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