Merge commit '0eff589afc83e21a03a168497bbab6b4dfbb4ef6' into clippyup

This commit is contained in:
flip1995 2021-12-30 15:10:43 +01:00
parent 01217f6f4c
commit 97ab44ca97
80 changed files with 1573 additions and 519 deletions

View file

@ -58,6 +58,10 @@ jobs:
run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
working-directory: clippy_lints
- name: Test clippy_utils
run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
working-directory: clippy_utils
- name: Test rustc_tools_util
run: cargo test --features deny-warnings
working-directory: rustc_tools_util

View file

@ -121,6 +121,10 @@ jobs:
run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
working-directory: clippy_lints
- name: Test clippy_utils
run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
working-directory: clippy_utils
- name: Test rustc_tools_util
run: cargo test --features deny-warnings
working-directory: rustc_tools_util

View file

@ -6,13 +6,127 @@ document.
## Unreleased / In Rust Nightly
[b7f3f7f...master](https://github.com/rust-lang/rust-clippy/compare/b7f3f7f...master)
[e181011...master](https://github.com/rust-lang/rust-clippy/compare/e181011...master)
## Rust 1.58 (beta)
Current beta, release 2022-01-13
[00e31fa...e181011](https://github.com/rust-lang/rust-clippy/compare/00e31fa...e181011)
### New lints
* [`transmute_num_to_bytes`]
[#7805](https://github.com/rust-lang/rust-clippy/pull/7805)
* [`match_str_case_mismatch`]
[#7806](https://github.com/rust-lang/rust-clippy/pull/7806)
* [`format_in_format_args`], [`to_string_in_format_args`]
[#7743](https://github.com/rust-lang/rust-clippy/pull/7743)
* [`uninit_vec`]
[#7682](https://github.com/rust-lang/rust-clippy/pull/7682)
* [`fn_to_numeric_cast_any`]
[#7705](https://github.com/rust-lang/rust-clippy/pull/7705)
* [`undocumented_unsafe_blocks`]
[#7748](https://github.com/rust-lang/rust-clippy/pull/7748)
* [`trailing_empty_array`]
[#7838](https://github.com/rust-lang/rust-clippy/pull/7838)
* [`string_slice`]
[#7878](https://github.com/rust-lang/rust-clippy/pull/7878)
### Moves or deprecations of lints
* Move [`non_send_fields_in_send_ty`] to `suspicious`
[#7874](https://github.com/rust-lang/rust-clippy/pull/7874)
* Move [`non_ascii_literal`] to `restriction`
[#7907](https://github.com/rust-lang/rust-clippy/pull/7907)
### Changes that expand what code existing lints cover
* [`question_mark`] now covers `Result`
[#7840](https://github.com/rust-lang/rust-clippy/pull/7840)
* Make [`useless_format`] recognize bare `format!("")`
[#7801](https://github.com/rust-lang/rust-clippy/pull/7801)
* Lint on underscored variables with no side effects in [`no_effect`]
[#7775](https://github.com/rust-lang/rust-clippy/pull/7775)
* Expand [`match_ref_pats`] to check for multiple reference patterns
[#7800](https://github.com/rust-lang/rust-clippy/pull/7800)
### False positive fixes
* Fix false positive of [`implicit_saturating_sub`] with `else` clause
[#7832](https://github.com/rust-lang/rust-clippy/pull/7832)
* Fix [`question_mark`] when there is call in conditional predicate
[#7860](https://github.com/rust-lang/rust-clippy/pull/7860)
* [`mut_mut`] no longer lints when type is defined in external macros
[#7795](https://github.com/rust-lang/rust-clippy/pull/7795)
* Avoid [`eq_op`] in test functions
[#7811](https://github.com/rust-lang/rust-clippy/pull/7811)
* [`cast_possible_truncation`] no longer lints when cast is coming from `signum`
method call [#7850](https://github.com/rust-lang/rust-clippy/pull/7850)
* [`match_str_case_mismatch`] no longer lints on uncased characters
[#7865](https://github.com/rust-lang/rust-clippy/pull/7865)
* [`ptr_arg`] no longer lints references to type aliases
[#7890](https://github.com/rust-lang/rust-clippy/pull/7890)
* [`missing_safety_doc`] now also accepts "implementation safety" headers
[#7856](https://github.com/rust-lang/rust-clippy/pull/7856)
* [`missing_safety_doc`] no longer lints if any parent has `#[doc(hidden)]`
attribute [#7849](https://github.com/rust-lang/rust-clippy/pull/7849)
* [`if_not_else`] now ignores else-if statements
[#7895](https://github.com/rust-lang/rust-clippy/pull/7895)
* Avoid linting [`cast_possible_truncation`] on bit-reducing operations
[#7819](https://github.com/rust-lang/rust-clippy/pull/7819)
* Avoid linting [`field_reassign_with_default`] when `Drop` and `Copy` are
involved [#7794](https://github.com/rust-lang/rust-clippy/pull/7794)
* [`unnecessary_sort_by`] now checks if argument implements `Ord` trait
[#7824](https://github.com/rust-lang/rust-clippy/pull/7824)
* Fix false positive in [`match_overlapping_arm`]
[#7847](https://github.com/rust-lang/rust-clippy/pull/7847)
* Prevent [`needless_lifetimes`] false positive in `async` function definition
[#7901](https://github.com/rust-lang/rust-clippy/pull/7901)
### Suggestion fixes/improvements
* Keep an initial `::` when [`doc_markdown`] suggests to use ticks
[#7916](https://github.com/rust-lang/rust-clippy/pull/7916)
* Add a machine applicable suggestion for the [`doc_markdown`] missing backticks
lint [#7904](https://github.com/rust-lang/rust-clippy/pull/7904)
* [`equatable_if_let`] no longer expands macros in the suggestion
[#7788](https://github.com/rust-lang/rust-clippy/pull/7788)
* Make [`shadow_reuse`] suggestion less verbose
[#7782](https://github.com/rust-lang/rust-clippy/pull/7782)
### ICE fixes
* Fix ICE in [`enum_variant_names`]
[#7873](https://github.com/rust-lang/rust-clippy/pull/7873)
* Fix ICE in [`undocumented_unsafe_blocks`]
[#7891](https://github.com/rust-lang/rust-clippy/pull/7891)
### Documentation improvements
* Fixed naive doc formatting for `#[must_use]` lints ([`must_use_unit`],
[`double_must_use`], [`must_use_candidate`], [`let_underscore_must_use`])
[#7827](https://github.com/rust-lang/rust-clippy/pull/7827)
* Fix typo in example for [`match_result_ok`]
[#7815](https://github.com/rust-lang/rust-clippy/pull/7815)
### Others
* Allow giving reasons for [`disallowed_types`]
[#7791](https://github.com/rust-lang/rust-clippy/pull/7791)
* Fix [`manual_assert`] and [`match_wild_err_arm`] for `#![no_std]` and Rust
2021. [#7851](https://github.com/rust-lang/rust-clippy/pull/7851)
* Fix regression in [`semicolon_if_nothing_returned`] on macros containing while
loops [#7789](https://github.com/rust-lang/rust-clippy/pull/7789)
* Added a new configuration `literal-suffix-style` to enforce a certain style
writing [`unseparated_literal_suffix`]
[#7726](https://github.com/rust-lang/rust-clippy/pull/7726)
## Rust 1.57
Current beta, release 2021-12-02
Current stable, released 2021-12-02
[7bfc26e...b7f3f7f](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...b7f3f7f)
[7bfc26e...00e31fa](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...00e31fa)
### New Lints
@ -161,7 +275,7 @@ Current beta, release 2021-12-02
## Rust 1.56
Current stable, released 2021-10-21
Released 2021-10-21
[74d1561...7bfc26e](https://github.com/rust-lang/rust-clippy/compare/74d1561...7bfc26e)
@ -2912,6 +3026,7 @@ Released 2018-09-13
[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
[`init_numbered_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#init_numbered_fields
[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax
[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax

View file

@ -118,7 +118,7 @@ which `IntelliJ Rust` will be able to understand.
Run `cargo dev setup intellij --repo-path <repo-path>` where `<repo-path>` is a path to the rustc repo
you just cloned.
The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
Clippys `Cargo.toml`s and should allow `IntelliJ Rust` to understand most of the types that Clippy uses.
Clippy's `Cargo.toml`s and should allow `IntelliJ Rust` to understand most of the types that Clippy uses.
Just make sure to remove the dependencies again before finally making a pull request!
[rustc_repo]: https://github.com/rust-lang/rust/
@ -126,8 +126,8 @@ Just make sure to remove the dependencies again before finally making a pull req
### Rust Analyzer
As of [#6869][6869], [`rust-analyzer`][ra_homepage] can understand that Clippy uses compiler-internals
using `extern crate` when `package.metadata.rust-analyzer.rustc_private` is set to `true` in Clippys `Cargo.toml.`
You will required a `nightly` toolchain with the `rustc-dev` component installed.
using `extern crate` when `package.metadata.rust-analyzer.rustc_private` is set to `true` in Clippy's `Cargo.toml.`
You will require a `nightly` toolchain with the `rustc-dev` component installed.
Make sure that in the `rust-analyzer` configuration, you set
```
{ "rust-analyzer.rustcSource": "discover" }
@ -228,7 +228,7 @@ about `subtree`s in the Rust repository see [Rust's `CONTRIBUTING.md`][subtree].
### Patching git-subtree to work with big repos
Currently there's a bug in `git-subtree` that prevents it from working properly
Currently, there's a bug in `git-subtree` that prevents it from working properly
with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's stale.
Before continuing with the following steps, we need to manually apply that fix to
our local copy of `git-subtree`.

View file

@ -144,7 +144,7 @@ line. (You can swap `clippy::all` with the specific lint category you are target
## Configuration
Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable =
value` mapping eg.
value` mapping e.g.
```toml
avoid-breaking-exported-api = false
@ -155,6 +155,10 @@ cognitive-complexity-threshold = 30
See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which
lints can be configured and the meaning of the variables.
Note that configuration changes will not apply for code that has already been compiled and cached under `./target/`;
for example, adding a new string to `doc-valid-idents` may still result in Clippy flagging that string. To be sure that
any configuration changes are applied, you may want to run `cargo clean` and re-compile your crate from scratch.
To deactivate the “for further information visit *lint-link*” message you can
define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable.
@ -193,7 +197,7 @@ And to warn on `lint_name`, run
cargo clippy -- -W clippy::lint_name
```
This also works with lint groups. For example you
This also works with lint groups. For example, you
can run Clippy with warnings for all lints enabled:
```terminal
cargo clippy -- -W clippy::pedantic
@ -228,7 +232,7 @@ fn main() {
You can also omit the patch version when specifying the MSRV, so `msrv = 1.30`
is equivalent to `msrv = 1.30.0`.
Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly.
Note: `custom_inner_attributes` is an unstable feature, so it has to be enabled explicitly.
Lints that recognize this configuration option can be found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv)

View file

@ -87,7 +87,7 @@ impl ApproxConstant {
let s = s.as_str();
if s.parse::<f64>().is_ok() {
for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS {
if is_approx_const(constant, &s, min_digits)
if is_approx_const(constant, s, min_digits)
&& msrv.as_ref().map_or(true, |msrv| meets_msrv(self.msrv.as_ref(), msrv))
{
span_lint_and_help(

View file

@ -310,8 +310,10 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|| is_word(lint, sym::deprecated)
|| is_word(lint, sym!(unreachable_pub))
|| is_word(lint, sym!(unused))
|| extract_clippy_lint(lint).map_or(false, |s| s.as_str() == "wildcard_imports")
|| extract_clippy_lint(lint).map_or(false, |s| s.as_str() == "enum_glob_use")
|| extract_clippy_lint(lint)
.map_or(false, |s| s.as_str() == "wildcard_imports")
|| extract_clippy_lint(lint)
.map_or(false, |s| s.as_str() == "enum_glob_use")
{
return;
}

View file

@ -49,8 +49,9 @@ pub(super) fn check(
if cast_from.kind() == cast_to.kind() =>
{
if let Some(src) = snippet_opt(cx, lit.span) {
let num_lit = NumericLiteral::from_lit_kind(&src, &lit.node).unwrap();
lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) {
lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
}
}
},
_ => {

View file

@ -2,8 +2,8 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::source::is_present_in_source;
use clippy_utils::str_utils::{self, count_match_end, count_match_start};
use rustc_hir::{EnumDef, Item, ItemKind};
use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start};
use rustc_hir::{EnumDef, Item, ItemKind, Variant};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
@ -18,6 +18,12 @@ declare_clippy_lint! {
/// Enumeration variant names should specify their variant,
/// not repeat the enumeration name.
///
/// ### Limitations
/// Characters with no casing will be considered when comparing prefixes/suffixes
/// This applies to numbers and non-ascii characters without casing
/// e.g. `Foo1` and `Foo2` is considered to have different prefixes
/// (the prefixes are `Foo1` and `Foo2` respectively), as also `Bar螃`, `Bar蟹`
///
/// ### Example
/// ```rust
/// enum Cake {
@ -120,72 +126,73 @@ impl_lint_pass!(EnumVariantNames => [
MODULE_INCEPTION
]);
fn check_variant(
cx: &LateContext<'_>,
threshold: u64,
def: &EnumDef<'_>,
item_name: &str,
item_name_chars: usize,
span: Span,
) {
fn check_enum_start(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) {
let name = variant.ident.name.as_str();
let item_name_chars = item_name.chars().count();
if count_match_start(item_name, name).char_count == item_name_chars
&& name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase())
&& name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric())
{
span_lint(
cx,
ENUM_VARIANT_NAMES,
variant.span,
"variant name starts with the enum's name",
);
}
}
fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) {
let name = variant.ident.name.as_str();
let item_name_chars = item_name.chars().count();
if count_match_end(item_name, name).char_count == item_name_chars {
span_lint(
cx,
ENUM_VARIANT_NAMES,
variant.span,
"variant name ends with the enum's name",
);
}
}
fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_name: &str, span: Span) {
if (def.variants.len() as u64) < threshold {
return;
}
let first = &def.variants[0].ident.name.as_str();
let mut pre = camel_case_split(first);
let mut post = pre.clone();
post.reverse();
for var in def.variants {
let name = var.ident.name.as_str();
if count_match_start(item_name, &name).char_count == item_name_chars
&& name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase())
&& name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric())
{
span_lint(
cx,
ENUM_VARIANT_NAMES,
var.span,
"variant name starts with the enum's name",
);
}
if count_match_end(item_name, &name).char_count == item_name_chars {
span_lint(
cx,
ENUM_VARIANT_NAMES,
var.span,
"variant name ends with the enum's name",
);
}
}
let first = def.variants[0].ident.name.as_str();
let mut pre = &first[..str_utils::camel_case_until(&*first).byte_index];
let mut post = &first[str_utils::camel_case_start(&*first).byte_index..];
for var in def.variants {
check_enum_start(cx, item_name, var);
check_enum_end(cx, item_name, var);
let name = var.ident.name.as_str();
let pre_match = count_match_start(pre, &name).byte_count;
pre = &pre[..pre_match];
let pre_camel = str_utils::camel_case_until(pre).byte_index;
pre = &pre[..pre_camel];
while let Some((next, last)) = name[pre.len()..].chars().zip(pre.chars().rev()).next() {
if next.is_numeric() {
return;
}
if next.is_lowercase() {
let last = pre.len() - last.len_utf8();
let last_camel = str_utils::camel_case_until(&pre[..last]);
pre = &pre[..last_camel.byte_index];
} else {
break;
}
}
let variant_split = camel_case_split(name);
let post_match = count_match_end(post, &name);
let post_end = post.len() - post_match.byte_count;
post = &post[post_end..];
let post_camel = str_utils::camel_case_start(post);
post = &post[post_camel.byte_index..];
pre = pre
.iter()
.zip(variant_split.iter())
.take_while(|(a, b)| a == b)
.map(|e| *e.0)
.collect();
post = post
.iter()
.zip(variant_split.iter().rev())
.take_while(|(a, b)| a == b)
.map(|e| *e.0)
.collect();
}
let (what, value) = match (pre.is_empty(), post.is_empty()) {
(true, true) => return,
(false, _) => ("pre", pre),
(true, false) => ("post", post),
(false, _) => ("pre", pre.join("")),
(true, false) => {
post.reverse();
("post", post.join(""))
},
};
span_lint_and_help(
cx,
@ -233,8 +240,7 @@ impl LateLintPass<'_> for EnumVariantNames {
#[allow(clippy::similar_names)]
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
let item_name = item.ident.name.as_str();
let item_name_chars = item_name.chars().count();
let item_camel = to_camel_case(&item_name);
let item_camel = to_camel_case(item_name);
if !item.span.from_expansion() && is_present_in_source(cx, item.span) {
if let Some(&(ref mod_name, ref mod_camel)) = self.modules.last() {
// constants don't have surrounding modules
@ -283,7 +289,7 @@ impl LateLintPass<'_> for EnumVariantNames {
}
if let ItemKind::Enum(ref def, _) = item.kind {
if !(self.avoid_breaking_exported_api && cx.access_levels.is_exported(item.def_id)) {
check_variant(cx, self.threshold, def, &item_name, item_name_chars, item.span);
check_variant(cx, self.threshold, def, item_name, item.span);
}
}
self.modules.push((item.ident.name, item_camel));

View file

@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
// If its within the 2 decimal digits of being out of precision we
// check if the parsed representation is the same as the string
// since we'll need the truncated string anyway.
let digits = count_digits(&sym_str);
let digits = count_digits(sym_str);
let max = max_digits(fty);
let type_suffix = match lit_float_ty {
LitFloatType::Suffixed(ast::FloatTy::F32) => Some("f32"),

View file

@ -356,7 +356,7 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
if eq_expr_value(cx, lmul_lhs, lmul_rhs);
if eq_expr_value(cx, rmul_lhs, rmul_rhs);
then {
return Some(format!("{}.hypot({})", Sugg::hir(cx, lmul_lhs, ".."), Sugg::hir(cx, rmul_lhs, "..")));
return Some(format!("{}.hypot({})", Sugg::hir(cx, lmul_lhs, "..").maybe_par(), Sugg::hir(cx, rmul_lhs, "..")));
}
}
@ -379,7 +379,7 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), rargs_1);
if Int(2) == lvalue && Int(2) == rvalue;
then {
return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, ".."), Sugg::hir(cx, rargs_0, "..")));
return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, "..").maybe_par(), Sugg::hir(cx, rargs_0, "..")));
}
}
}
@ -654,26 +654,52 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
(F32(180_f32) == lvalue || F64(180_f64) == lvalue)
{
let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
if_chain! {
if let ExprKind::Lit(ref literal) = mul_lhs.kind;
if let ast::LitKind::Float(ref value, float_type) = literal.node;
if float_type == ast::LitFloatType::Unsuffixed;
then {
if value.as_str().ends_with('.') {
proposal = format!("{}0_f64.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
} else {
proposal = format!("{}_f64.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
}
}
}
span_lint_and_sugg(
cx,
SUBOPTIMAL_FLOPS,
expr.span,
"conversion to degrees can be done more accurately",
"consider using",
format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..")),
proposal,
Applicability::MachineApplicable,
);
} else if
(F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
(F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
{
let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
if_chain! {
if let ExprKind::Lit(ref literal) = mul_lhs.kind;
if let ast::LitKind::Float(ref value, float_type) = literal.node;
if float_type == ast::LitFloatType::Unsuffixed;
then {
if value.as_str().ends_with('.') {
proposal = format!("{}0_f64.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
} else {
proposal = format!("{}_f64.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
}
}
}
span_lint_and_sugg(
cx,
SUBOPTIMAL_FLOPS,
expr.span,
"conversion to radians can be done more accurately",
"consider using",
format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..")),
proposal,
Applicability::MachineApplicable,
);
}

View file

@ -61,15 +61,18 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp {
}
fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool {
// `1 << 0` is a common pattern in bit manipulation code
cmp.node == BinOpKind::Shl
&& constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0))
&& constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1))
// This lint applies to integers
!cx.typeck_results().expr_ty(left).peel_refs().is_integral()
|| !cx.typeck_results().expr_ty(right).peel_refs().is_integral()
// `1 << 0` is a common pattern in bit manipulation code
|| (cmp.node == BinOpKind::Shl
&& constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0))
&& constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)))
}
fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) {
if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e) {
let check = match *cx.typeck_results().expr_ty(e).kind() {
if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) {
let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
ty::Uint(uty) => clip(cx.tcx, !0, uty),
_ => return,

View file

@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
if let LitKind::Int(0, _) = cond_lit.node {
if cx.typeck_results().expr_ty(cond_left).is_signed() {
} else {
print_lint_and_sugg(cx, &var_name, expr);
print_lint_and_sugg(cx, var_name, expr);
};
}
},
@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok());
if int_ids.any(|int_id| int_id == impl_id);
then {
print_lint_and_sugg(cx, &var_name, expr)
print_lint_and_sugg(cx, var_name, expr)
}
}
},
@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok());
if int_ids.any(|int_id| int_id == impl_id);
then {
print_lint_and_sugg(cx, &var_name, expr)
print_lint_and_sugg(cx, var_name, expr)
}
}
},

View file

@ -0,0 +1,80 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::in_macro;
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use std::borrow::Cow;
use std::cmp::Reverse;
use std::collections::BinaryHeap;
declare_clippy_lint! {
/// ### What it does
/// Checks for tuple structs initialized with field syntax.
/// It will however not lint if a base initializer is present.
/// The lint will also ignore code in macros.
///
/// ### Why is this bad?
/// This may be confusing to the uninitiated and adds no
/// benefit as opposed to tuple initializers
///
/// ### Example
/// ```rust
/// struct TupleStruct(u8, u16);
///
/// let _ = TupleStruct {
/// 0: 1,
/// 1: 23,
/// };
///
/// // should be written as
/// let base = TupleStruct(1, 23);
///
/// // This is OK however
/// let _ = TupleStruct { 0: 42, ..base };
/// ```
#[clippy::version = "1.59.0"]
pub INIT_NUMBERED_FIELDS,
style,
"numbered fields in tuple struct initializer"
}
declare_lint_pass!(NumberedFields => [INIT_NUMBERED_FIELDS]);
impl<'tcx> LateLintPass<'tcx> for NumberedFields {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
if let ExprKind::Struct(path, fields, None) = e.kind {
if !fields.is_empty()
&& !in_macro(e.span)
&& fields
.iter()
.all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit))
{
let expr_spans = fields
.iter()
.map(|f| (Reverse(f.ident.as_str().parse::<usize>().unwrap()), f.expr.span))
.collect::<BinaryHeap<_>>();
let mut appl = Applicability::MachineApplicable;
let snippet = format!(
"{}({})",
snippet_with_applicability(cx, path.span(), "..", &mut appl),
expr_spans
.into_iter_sorted()
.map(|(_, span)| snippet_with_applicability(cx, span, "..", &mut appl))
.intersperse(Cow::Borrowed(", "))
.collect::<String>()
);
span_lint_and_sugg(
cx,
INIT_NUMBERED_FIELDS,
e.span,
"used a field initializer for a tuple struct",
"try this instead",
snippet,
appl,
);
}
}
}
}

View file

@ -441,7 +441,7 @@ fn is_empty_string(expr: &Expr<'_>) -> bool {
if let ExprKind::Lit(ref lit) = expr.kind {
if let LitKind::Str(lit, _) = lit.node {
let lit = lit.as_str();
return lit == "";
return lit.is_empty();
}
}
false

View file

@ -81,6 +81,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(infinite_iter::INFINITE_ITER),
LintId::of(inherent_to_string::INHERENT_TO_STRING),
LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
LintId::of(int_plus_one::INT_PLUS_ONE),
LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),

View file

@ -178,6 +178,7 @@ store.register_lints(&[
inherent_impl::MULTIPLE_INHERENT_IMPL,
inherent_to_string::INHERENT_TO_STRING,
inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY,
init_numbered_fields::INIT_NUMBERED_FIELDS,
inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
int_plus_one::INT_PLUS_ONE,
integer_division::INTEGER_DIVISION,

View file

@ -29,6 +29,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(functions::MUST_USE_UNIT),
LintId::of(functions::RESULT_UNIT_ERR),
LintId::of(inherent_to_string::INHERENT_TO_STRING),
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
LintId::of(len_zero::COMPARISON_TO_EMPTY),
LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
LintId::of(len_zero::LEN_ZERO),

View file

@ -1,13 +1,15 @@
// error-pattern:cargo-clippy
#![feature(binary_heap_into_iter_sorted)]
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(drain_filter)]
#![feature(in_band_lifetimes)]
#![feature(iter_intersperse)]
#![feature(let_else)]
#![feature(once_cell)]
#![feature(rustc_private)]
#![feature(stmt_expr_attributes)]
#![feature(control_flow_enum)]
#![feature(let_else)]
#![recursion_limit = "512"]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
@ -242,6 +244,7 @@ mod indexing_slicing;
mod infinite_iter;
mod inherent_impl;
mod inherent_to_string;
mod init_numbered_fields;
mod inline_fn_without_body;
mod int_plus_one;
mod integer_division;
@ -854,6 +857,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit));
store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View file

@ -12,6 +12,7 @@ use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Pat, PatKind, StmtKind}
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
use rustc_span::symbol::sym;
use std::fmt::Display;
use std::iter::Iterator;
/// Checks for for loops that sequentially copy items from one slice-like
@ -108,7 +109,7 @@ fn build_manual_memcpy_suggestion<'tcx>(
src: &IndexExpr<'_>,
) -> String {
fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> {
if offset.as_str() == "0" {
if offset.to_string() == "0" {
sugg::EMPTY.into()
} else {
offset
@ -123,7 +124,7 @@ fn build_manual_memcpy_suggestion<'tcx>(
if let Some(arg) = len_args.get(0);
if path_to_local(arg) == path_to_local(base);
then {
if sugg.as_str() == end_str {
if sugg.to_string() == end_str {
sugg::EMPTY.into()
} else {
sugg
@ -147,7 +148,7 @@ fn build_manual_memcpy_suggestion<'tcx>(
print_offset(apply_offset(&start_str, &idx_expr.idx_offset)).into_sugg(),
print_limit(
end,
end_str.as_str(),
end_str.to_string().as_str(),
idx_expr.base,
apply_offset(&end_str, &idx_expr.idx_offset),
)
@ -159,7 +160,7 @@ fn build_manual_memcpy_suggestion<'tcx>(
print_offset(apply_offset(&counter_start, &idx_expr.idx_offset)).into_sugg(),
print_limit(
end,
end_str.as_str(),
end_str.to_string().as_str(),
idx_expr.base,
apply_offset(&end_str, &idx_expr.idx_offset) + &counter_start - &start_str,
)
@ -202,15 +203,13 @@ fn build_manual_memcpy_suggestion<'tcx>(
#[derive(Clone)]
struct MinifyingSugg<'a>(Sugg<'a>);
impl<'a> MinifyingSugg<'a> {
fn as_str(&self) -> &str {
// HACK: Don't sync to Clippy! Required because something with the `or_patterns` feature
// changed and this would now require parentheses.
match &self.0 {
Sugg::NonParen(s) | Sugg::MaybeParen(s) | Sugg::BinOp(_, s) => s.as_ref(),
}
impl Display for MinifyingSugg<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl<'a> MinifyingSugg<'a> {
fn into_sugg(self) -> Sugg<'a> {
self.0
}
@ -225,7 +224,7 @@ impl<'a> From<Sugg<'a>> for MinifyingSugg<'a> {
impl std::ops::Add for &MinifyingSugg<'static> {
type Output = MinifyingSugg<'static>;
fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> {
match (self.as_str(), rhs.as_str()) {
match (self.to_string().as_str(), rhs.to_string().as_str()) {
("0", _) => rhs.clone(),
(_, "0") => self.clone(),
(_, _) => (&self.0 + &rhs.0).into(),
@ -236,7 +235,7 @@ impl std::ops::Add for &MinifyingSugg<'static> {
impl std::ops::Sub for &MinifyingSugg<'static> {
type Output = MinifyingSugg<'static>;
fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> {
match (self.as_str(), rhs.as_str()) {
match (self.to_string().as_str(), rhs.to_string().as_str()) {
(_, "0") => self.clone(),
("0", _) => (-rhs.0.clone()).into(),
(x, y) if x == y => sugg::ZERO.into(),
@ -248,7 +247,7 @@ impl std::ops::Sub for &MinifyingSugg<'static> {
impl std::ops::Add<&MinifyingSugg<'static>> for MinifyingSugg<'static> {
type Output = MinifyingSugg<'static>;
fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> {
match (self.as_str(), rhs.as_str()) {
match (self.to_string().as_str(), rhs.to_string().as_str()) {
("0", _) => rhs.clone(),
(_, "0") => self,
(_, _) => (self.0 + &rhs.0).into(),
@ -259,7 +258,7 @@ impl std::ops::Add<&MinifyingSugg<'static>> for MinifyingSugg<'static> {
impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> {
type Output = MinifyingSugg<'static>;
fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> {
match (self.as_str(), rhs.as_str()) {
match (self.to_string().as_str(), rhs.to_string().as_str()) {
(_, "0") => self,
("0", _) => (-rhs.0.clone()).into(),
(x, y) if x == y => sugg::ZERO.into(),

View file

@ -1,4 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::in_macro;
use clippy_utils::source::snippet;
use hir::def::{DefKind, Res};
use if_chain::if_chain;
@ -8,7 +9,6 @@ use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::hygiene::ExpnKind;
use rustc_span::{edition::Edition, sym, Span};
declare_clippy_lint! {
@ -213,7 +213,3 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
}
}
}
fn in_macro(span: Span) -> bool {
span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
}

View file

@ -94,8 +94,8 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchExprVisitor<'a, 'tcx> {
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
match ex.kind {
ExprKind::MethodCall(segment, _, [receiver], _)
if self.case_altered(segment.ident.as_str(), receiver) => {},
ExprKind::MethodCall(segment, _, [receiver], _) if self.case_altered(segment.ident.as_str(), receiver) => {
},
_ => walk_expr(self, ex),
}
}
@ -142,7 +142,7 @@ fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<(
}) = arm.pat.kind;
if let LitKind::Str(symbol, _) = lit.node;
let input = symbol.as_str();
if !case_check(&input);
if !case_check(input);
then {
return Some((lit.span, symbol));
}

View file

@ -1,8 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_trait_method;
use clippy_utils::path_to_local;
use clippy_utils::source::snippet;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::{BindingAnnotation, Node, PatKind};
use rustc_lint::LateContext;
use rustc_span::sym;
@ -11,14 +13,34 @@ use super::ITER_SKIP_NEXT;
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
// lint if caller of skip is an Iterator
if is_trait_method(cx, expr, sym::Iterator) {
span_lint_and_sugg(
let mut application = Applicability::MachineApplicable;
span_lint_and_then(
cx,
ITER_SKIP_NEXT,
expr.span.trim_start(recv.span).unwrap(),
"called `skip(..).next()` on an iterator",
"use `nth` instead",
format!(".nth({})", snippet(cx, arg.span, "..")),
Applicability::MachineApplicable,
|diag| {
if_chain! {
if let Some(id) = path_to_local(recv);
if let Node::Binding(pat) = cx.tcx.hir().get(id);
if let PatKind::Binding(ann, _, _, _) = pat.kind;
if ann != BindingAnnotation::Mutable;
then {
application = Applicability::Unspecified;
diag.span_help(
pat.span,
&format!("for this change `{}` has to be mutable", snippet(cx, pat.span, "..")),
);
}
}
diag.span_suggestion(
expr.span.trim_start(recv.span).unwrap(),
"use `nth` instead",
format!(".nth({})", snippet(cx, arg.span, "..")),
application,
);
},
);
}
}

View file

@ -2112,7 +2112,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
{
wrong_self_convention::check(
cx,
&name,
name,
self_ty,
first_arg_ty,
first_arg.pat.span,

View file

@ -2,7 +2,8 @@
use super::UNWRAP_OR_ELSE_DEFAULT;
use clippy_utils::{
diagnostics::span_lint_and_sugg, is_trait_item, source::snippet_with_applicability, ty::is_type_diagnostic_item,
diagnostics::span_lint_and_sugg, is_default_equivalent_call, source::snippet_with_applicability,
ty::is_type_diagnostic_item,
};
use rustc_errors::Applicability;
use rustc_hir as hir;
@ -24,7 +25,7 @@ pub(super) fn check<'tcx>(
if_chain! {
if is_option || is_result;
if is_trait_item(cx, u_arg, sym::Default);
if is_default_equivalent_call(cx, u_arg);
then {
let mut applicability = Applicability::MachineApplicable;

View file

@ -57,7 +57,7 @@ pub(super) fn get_hint_if_single_char_arg(
let string = r.as_str();
if string.chars().count() == 1;
then {
let snip = snippet_with_applicability(cx, arg.span, &string, applicability);
let snip = snippet_with_applicability(cx, arg.span, string, applicability);
let ch = if let ast::StrStyle::Raw(nhash) = style {
let nhash = nhash as usize;
// for raw string: r##"a"##

View file

@ -187,14 +187,14 @@ impl<'tcx> LateLintPass<'tcx> for BoolComparison {
BinOpKind::Eq => {
let true_case = Some((|h| h, "equality checks against true are unnecessary"));
let false_case = Some((
|h: Sugg<'_>| !h,
|h: Sugg<'tcx>| !h,
"equality checks against false can be replaced by a negation",
));
check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal);
},
BinOpKind::Ne => {
let true_case = Some((
|h: Sugg<'_>| !h,
|h: Sugg<'tcx>| !h,
"inequality checks against true can be replaced by a negation",
));
let false_case = Some((|h| h, "inequality checks against false are unnecessary"));
@ -206,12 +206,12 @@ impl<'tcx> LateLintPass<'tcx> for BoolComparison {
ignore_case,
Some((|h| h, "greater than checks against false are unnecessary")),
Some((
|h: Sugg<'_>| !h,
|h: Sugg<'tcx>| !h,
"less than comparison against true can be replaced by a negation",
)),
ignore_case,
Some((
|l: Sugg<'_>, r: Sugg<'_>| (!l).bit_and(&r),
|l: Sugg<'tcx>, r: Sugg<'tcx>| (!l).bit_and(&r),
"order comparisons between booleans can be simplified",
)),
),
@ -219,14 +219,14 @@ impl<'tcx> LateLintPass<'tcx> for BoolComparison {
cx,
e,
Some((
|h: Sugg<'_>| !h,
|h: Sugg<'tcx>| !h,
"less than comparison against true can be replaced by a negation",
)),
ignore_case,
ignore_case,
Some((|h| h, "greater than checks against false are unnecessary")),
Some((
|l: Sugg<'_>, r: Sugg<'_>| l.bit_and(&(!r)),
|l: Sugg<'tcx>, r: Sugg<'tcx>| l.bit_and(&(!r)),
"order comparisons between booleans can be simplified",
)),
),

View file

@ -1,6 +1,8 @@
use clippy_utils::consts::{self, Constant};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -18,12 +20,16 @@ declare_clippy_lint! {
///
/// ### Example
/// ```ignore
/// x * -1
/// // Bad
/// let a = x * -1;
///
/// // Good
/// let b = -x;
/// ```
#[clippy::version = "pre 1.29.0"]
pub NEG_MULTIPLY,
style,
"multiplying integers with `-1`"
"multiplying integers by `-1`"
}
declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]);
@ -49,8 +55,19 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
if let ExprKind::Lit(ref l) = lit.kind;
if consts::lit_to_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)) == Constant::Int(1);
if cx.typeck_results().expr_ty(exp).is_integral();
then {
span_lint(cx, NEG_MULTIPLY, span, "negation by multiplying with `-1`");
let mut applicability = Applicability::MachineApplicable;
let suggestion = format!("-{}", snippet_with_applicability(cx, exp.span, "..", &mut applicability));
span_lint_and_sugg(
cx,
NEG_MULTIPLY,
span,
"this multiplication by -1 can be written more succinctly",
"consider using",
suggestion,
applicability,
);
}
}
}

View file

@ -218,20 +218,20 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
return;
}
for existing_name in &self.0.names {
if allowed_to_be_similar(&interned_name, existing_name.exemptions) {
if allowed_to_be_similar(interned_name, existing_name.exemptions) {
continue;
}
match existing_name.len.cmp(&count) {
Ordering::Greater => {
if existing_name.len - count != 1
|| levenstein_not_1(&interned_name, existing_name.interned.as_str())
|| levenstein_not_1(interned_name, existing_name.interned.as_str())
{
continue;
}
},
Ordering::Less => {
if count - existing_name.len != 1
|| levenstein_not_1(existing_name.interned.as_str(), &interned_name)
|| levenstein_not_1(existing_name.interned.as_str(), interned_name)
{
continue;
}
@ -298,7 +298,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
return;
}
self.0.names.push(ExistingName {
exemptions: get_exemptions(&interned_name).unwrap_or(&[]),
exemptions: get_exemptions(interned_name).unwrap_or(&[]),
interned: ident.name,
span: ident.span,
len: count,

View file

@ -378,8 +378,8 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
span,
"an inclusive range would be more readable",
|diag| {
let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").to_string());
let end = Sugg::hir(cx, y, "y");
let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string());
let end = Sugg::hir(cx, y, "y").maybe_par();
if let Some(is_wrapped) = &snippet_opt(cx, span) {
if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') {
diag.span_suggestion(
@ -415,8 +415,8 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
expr.span,
"an exclusive range would be more readable",
|diag| {
let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").to_string());
let end = Sugg::hir(cx, y, "y");
let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string());
let end = Sugg::hir(cx, y, "y").maybe_par();
diag.span_suggestion(
expr.span,
"use",

View file

@ -1,11 +1,12 @@
use clippy_utils::{diagnostics::span_lint, must_use_attr, nth_arg, return_ty};
use clippy_utils::ty::is_must_use_ty;
use clippy_utils::{diagnostics::span_lint, nth_arg, return_ty};
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, FnDecl, HirId, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;
use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
@ -50,9 +51,9 @@ fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalD
if decl.implicit_self.has_implicit_self();
// We only show this warning for public exported methods.
if cx.access_levels.is_exported(fn_def);
// We don't want to emit this lint if the `#[must_use]` attribute is already there.
if !cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::must_use));
if cx.tcx.visibility(fn_def.to_def_id()).is_public();
// No need to warn if the attribute is already present.
if must_use_attr(cx.tcx.hir().attrs(hir_id)).is_none();
let ret_ty = return_ty(cx, hir_id);
let self_arg = nth_arg(cx, hir_id, 0);
// If `Self` has the same type as the returned type, then we want to warn.
@ -60,6 +61,8 @@ fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalD
// For this check, we don't want to remove the reference on the returned type because if
// there is one, we shouldn't emit a warning!
if self_arg.peel_refs() == ret_ty;
// If `Self` is already marked as `#[must_use]`, no need for the attribute here.
if !is_must_use_ty(cx, ret_ty);
then {
span_lint(

View file

@ -73,6 +73,7 @@ declare_clippy_lint! {
enum RetReplacement {
Empty,
Block,
Unit,
}
declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]);
@ -212,7 +213,7 @@ fn check_final_expr<'tcx>(
// (except for unit type functions) so we don't match it
ExprKind::Match(_, arms, MatchSource::Normal) => {
for arm in arms.iter() {
check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block);
check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Unit);
}
},
ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty),
@ -259,6 +260,17 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option<Spa
Applicability::MachineApplicable,
);
},
RetReplacement::Unit => {
span_lint_and_sugg(
cx,
NEEDLESS_RETURN,
ret_span,
"unneeded `return` statement",
"replace `return` with a unit value",
"()".to_string(),
Applicability::MachineApplicable,
);
},
},
}
}

View file

@ -5,7 +5,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::Res;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::hir_id::ItemLocalId;
use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, UnOp};
use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Let, Node, Pat, PatKind, QPath, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{Span, Symbol};
@ -220,14 +220,14 @@ fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_
}
}
/// Finds the "init" expression for a pattern: `let <pat> = <init>;` or
/// Finds the "init" expression for a pattern: `let <pat> = <init>;` (or `if let`) or
/// `match <init> { .., <pat> => .., .. }`
fn find_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
for (_, node) in cx.tcx.hir().parent_iter(hir_id) {
let init = match node {
Node::Arm(_) | Node::Pat(_) => continue,
Node::Expr(expr) => match expr.kind {
ExprKind::Match(e, _, _) => Some(e),
ExprKind::Match(e, _, _) | ExprKind::Let(&Let { init: e, .. }) => Some(e),
_ => None,
},
Node::Local(local) => local.init,

View file

@ -64,7 +64,7 @@ impl TabsInDocComments {
if let ast::AttrKind::DocComment(_, comment) = attr.kind {
let comment = comment.as_str();
for (lo, hi) in get_chunks_of_tabs(&comment) {
for (lo, hi) in get_chunks_of_tabs(comment) {
// +3 skips the opening delimiter
let new_span = Span::new(
attr.span.lo() + BytePos(3 + lo),

View file

@ -15,7 +15,7 @@ use std::borrow::Cow;
declare_clippy_lint! {
/// ### What it does
/// Checks for `unsafe` blocks without a `// Safety: ` comment
/// Checks for `unsafe` blocks without a `// SAFETY: ` comment
/// explaining why the unsafe operations performed inside
/// the block are safe.
///
@ -36,7 +36,7 @@ declare_clippy_lint! {
/// use std::ptr::NonNull;
/// let a = &mut 42;
///
/// // Safety: references are guaranteed to be non-null.
/// // SAFETY: references are guaranteed to be non-null.
/// let ptr = unsafe { NonNull::new_unchecked(a) };
/// ```
#[clippy::version = "1.58.0"]
@ -213,7 +213,7 @@ impl UndocumentedUnsafeBlocks {
);
} else {
let block_indent = indent_of(cx, span);
let suggestion = format!("// Safety: ...\n{}", snippet(cx, span, ".."));
let suggestion = format!("// SAFETY: ...\n{}", snippet(cx, span, ".."));
span_lint_and_sugg(
cx,

View file

@ -60,7 +60,7 @@ fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) {
fn unsafe_to_safe_check(old_name: Ident, new_name: Ident, cx: &EarlyContext<'_>, span: Span) {
let old_str = old_name.name.as_str();
let new_str = new_name.name.as_str();
if contains_unsafe(&old_str) && !contains_unsafe(&new_str) {
if contains_unsafe(old_str) && !contains_unsafe(new_str) {
span_lint(
cx,
UNSAFE_REMOVED_FROM_NAME,

View file

@ -161,7 +161,7 @@ fn collect_unwrap_info<'tcx>(
if is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name);
then {
assert!(args.len() == 1);
let unwrappable = match name.as_ref() {
let unwrappable = match name {
"is_some" | "is_ok" => true,
"is_err" | "is_none" => false,
_ => unreachable!(),

View file

@ -87,7 +87,7 @@ fn check_ident(cx: &LateContext<'_>, ident: &Ident, be_aggressive: bool) {
if (ident.chars().all(|c| c.is_ascii_uppercase()) && ident.len() > 2)
// otherwise, warn if we have SOmeTHING lIKE THIs but only warn with the aggressive
// upper-case-acronyms-aggressive config option enabled
|| (be_aggressive && ident != &corrected)
|| (be_aggressive && ident != corrected)
{
span_lint_and_sugg(
cx,

View file

@ -28,7 +28,7 @@ use rustc_middle::ty;
use rustc_semver::RustcVersion;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{Symbol, SymbolStr};
use rustc_span::symbol::Symbol;
use rustc_span::{sym, BytePos, Span};
use rustc_typeck::hir_ty_to_ty;
@ -344,11 +344,11 @@ impl EarlyLintPass for ClippyLintsInternal {
if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind {
if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") {
if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind {
let mut last_name: Option<SymbolStr> = None;
let mut last_name: Option<&str> = None;
for item in items {
let name = item.ident.as_str();
if let Some(ref last_name) = last_name {
if **last_name > *name {
if let Some(last_name) = last_name {
if *last_name > *name {
span_lint(
cx,
CLIPPY_LINTS_INTERNAL,
@ -608,8 +608,7 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
}
let (method_names, arg_lists, spans) = method_calls(expr, 2);
let method_names: Vec<SymbolStr> = method_names.iter().map(|s| s.as_str()).collect();
let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect();
let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect();
if_chain! {
if let ["expn_data", "outer_expn"] = method_names.as_slice();
let args = arg_lists[1];
@ -839,7 +838,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]);
// Extract the path to the matched type
if let Some(segments) = path_to_matched_type(cx, ty_path);
let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect();
if let Some(ty_did) = path_to_res(cx, &segments[..]).opt_def_id();
// Check if the matched type is a diagnostic item
if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did);
@ -862,7 +861,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
}
}
fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<SymbolStr>> {
fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<Symbol>> {
use rustc_hir::ItemKind;
match &expr.kind {
@ -887,12 +886,12 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve
_ => {},
},
ExprKind::Array(exprs) => {
let segments: Vec<SymbolStr> = exprs
let segments: Vec<Symbol> = exprs
.iter()
.filter_map(|expr| {
if let ExprKind::Lit(lit) = &expr.kind {
if let LitKind::Str(sym, _) = lit.node {
return Some(sym.as_str());
return Some(sym);
}
}
@ -1076,7 +1075,6 @@ impl InterningDefinedSymbol {
&paths::SYMBOL_TO_IDENT_STRING,
&paths::TO_STRING_METHOD,
];
// SymbolStr might be de-referenced: `&*symbol.as_str()`
let call = if_chain! {
if let ExprKind::AddrOf(_, _, e) = expr.kind;
if let ExprKind::Unary(UnOp::Deref, e) = e.kind;

View file

@ -168,6 +168,14 @@ impl Constant {
None
}
}
#[must_use]
pub fn peel_refs(mut self) -> Self {
while let Constant::Ref(r) = self {
self = *r;
}
self
}
}
/// Parses a `LitKind` to a `Constant`.
@ -320,7 +328,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
let res = self.typeck_results.qpath_res(qpath, callee.hir_id);
if let Some(def_id) = res.opt_def_id();
let def_path = self.lcx.get_def_path(def_id);
let def_path: Vec<&str> = def_path.iter().take(4).map(|s| s.as_str()).collect();
let def_path: Vec<&str> = def_path.iter().take(4).map(Symbol::as_str).collect();
if let ["core", "num", int_impl, "max_value"] = *def_path;
then {
let value = match int_impl {

View file

@ -1,8 +1,9 @@
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(in_band_lifetimes)]
#![feature(let_else)]
#![feature(once_cell)]
#![feature(rustc_private)]
#![feature(control_flow_enum)]
#![recursion_limit = "512"]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
@ -60,9 +61,12 @@ pub use self::hir_utils::{both, count_eq, eq_expr_value, over, SpanlessEq, Spanl
use std::collections::hash_map::Entry;
use std::hash::BuildHasherDefault;
use std::lazy::SyncOnceCell;
use std::sync::{Mutex, MutexGuard};
use if_chain::if_chain;
use rustc_ast::ast::{self, Attribute, LitKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::unhash::UnhashMap;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
@ -87,6 +91,7 @@ use rustc_middle::ty::binding::BindingMode;
use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable, UpvarCapture};
use rustc_semver::RustcVersion;
use rustc_session::Session;
use rustc_span::def_id::LocalDefId;
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::original_sp;
use rustc_span::sym;
@ -142,6 +147,13 @@ macro_rules! extract_msrv_attr {
};
}
/// Returns `true` if the span comes from a macro expansion, no matter if from a
/// macro by example or from a procedural macro
#[must_use]
pub fn in_macro(span: Span) -> bool {
span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
}
/// Returns `true` if the two spans come from differing expansions (i.e., one is
/// from a macro and one isn't).
#[must_use]
@ -156,18 +168,18 @@ pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
/// instead.
///
/// Examples:
/// ```ignore
/// ```
/// let abc = 1;
/// // ^ output
/// let def = abc;
/// dbg!(def)
/// dbg!(def);
/// // ^^^ input
///
/// // or...
/// let abc = 1;
/// let def = abc + 2;
/// // ^^^^^^^ output
/// dbg!(def)
/// dbg!(def);
/// // ^^^ input
/// ```
pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
@ -664,6 +676,22 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<
false
}
/// Return true if the expr is equal to `Default::default` when evaluated.
pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool {
if_chain! {
if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
if is_diag_trait_item(cx, repl_def_id, sym::Default)
|| is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
then {
true
}
else {
false
}
}
}
/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
/// It doesn't cover all cases, for example indirect function calls (some of std
/// functions are supported) but it is the best we have.
@ -686,18 +714,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
false
}
},
ExprKind::Call(repl_func, _) => if_chain! {
if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
if is_diag_trait_item(cx, repl_def_id, sym::Default)
|| is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
then {
true
}
else {
false
}
},
ExprKind::Call(repl_func, _) => is_default_equivalent_call(cx, repl_func),
ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone),
ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
_ => false,
@ -1136,7 +1153,7 @@ pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> {
/// Extends the span to the beginning of the spans line, incl. whitespaces.
///
/// ```rust,ignore
/// ```rust
/// let x = ();
/// // ^^
/// // will be converted to
@ -1337,7 +1354,7 @@ pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
cx.typeck_results().adjustments().get(e.hir_id).is_some()
}
/// Returns the pre-expansion span if is this comes from an expansion of the
/// Returns the pre-expansion span if this comes from an expansion of the
/// macro `name`.
/// See also [`is_direct_expn_of`].
#[must_use]
@ -1364,7 +1381,8 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
/// of the macro `name`.
/// The difference with [`is_expn_of`] is that in
/// ```rust
/// # macro_rules! foo { ($e:tt) => { $e } }; macro_rules! bar { ($e:expr) => { $e } }
/// # macro_rules! foo { ($name:tt!$args:tt) => { $name!$args } }
/// # macro_rules! bar { ($e:expr) => { $e } }
/// foo!(bar!(42));
/// ```
/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
@ -1905,7 +1923,9 @@ pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
/// Check if parent of a hir node is a trait implementation block.
/// For example, `f` in
/// ```rust,ignore
/// ```rust
/// # struct S;
/// # trait Trait { fn f(); }
/// impl Trait for S {
/// fn f() {}
/// }
@ -2124,17 +2144,16 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
false
}
struct VisitConstTestStruct<'tcx> {
struct TestItemNamesVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
names: Vec<Symbol>,
found: bool,
}
impl<'hir> ItemLikeVisitor<'hir> for VisitConstTestStruct<'hir> {
impl<'hir> ItemLikeVisitor<'hir> for TestItemNamesVisitor<'hir> {
fn visit_item(&mut self, item: &Item<'_>) {
if let ItemKind::Const(ty, _body) = item.kind {
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
// We could also check for the type name `test::TestDescAndFn`
// and the `#[rustc_test_marker]` attribute?
if let Res::Def(DefKind::Struct, _) = path.res {
let has_test_marker = self
.tcx
@ -2142,8 +2161,8 @@ impl<'hir> ItemLikeVisitor<'hir> for VisitConstTestStruct<'hir> {
.attrs(item.hir_id())
.iter()
.any(|a| a.has_name(sym::rustc_test_marker));
if has_test_marker && self.names.contains(&item.ident.name) {
self.found = true;
if has_test_marker {
self.names.push(item.ident.name);
}
}
}
@ -2154,32 +2173,42 @@ impl<'hir> ItemLikeVisitor<'hir> for VisitConstTestStruct<'hir> {
fn visit_foreign_item(&mut self, _: &ForeignItem<'_>) {}
}
static TEST_ITEM_NAMES_CACHE: SyncOnceCell<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = SyncOnceCell::new();
fn with_test_item_names(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
match map.entry(module) {
Entry::Occupied(entry) => f(entry.get()),
Entry::Vacant(entry) => {
let mut visitor = TestItemNamesVisitor { tcx, names: Vec::new() };
tcx.hir().visit_item_likes_in_module(module, &mut visitor);
visitor.names.sort_unstable();
f(&*entry.insert(visitor.names))
},
}
}
/// Checks if the function containing the given `HirId` is a `#[test]` function
///
/// Note: If you use this function, please add a `#[test]` case in `tests/ui_test`.
pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
let names: Vec<_> = tcx
.hir()
.parent_iter(id)
// Since you can nest functions we need to collect all until we leave
// function scope
.filter_map(|(_id, node)| {
if let Node::Item(item) = node {
if let ItemKind::Fn(_, _, _) = item.kind {
return Some(item.ident.name);
with_test_item_names(tcx, tcx.parent_module(id), |names| {
tcx.hir()
.parent_iter(id)
// Since you can nest functions we need to collect all until we leave
// function scope
.any(|(_id, node)| {
if let Node::Item(item) = node {
if let ItemKind::Fn(_, _, _) = item.kind {
// Note that we have sorted the item names in the visitor,
// so the binary_search gets the same as `contains`, but faster.
return names.binary_search(&item.ident.name).is_ok();
}
}
}
None
})
.collect();
let parent_mod = tcx.parent_module(id);
let mut vis = VisitConstTestStruct {
tcx,
names,
found: false,
};
tcx.hir().visit_item_likes_in_module(parent_mod, &mut vis);
vis.found
false
})
})
}
/// Checks whether item either has `test` attribute applied, or

View file

@ -15,6 +15,7 @@ impl StrIndex {
/// Returns the index of the character after the first camel-case component of `s`.
///
/// ```
/// # use clippy_utils::str_utils::{camel_case_until, StrIndex};
/// assert_eq!(camel_case_until("AbcDef"), StrIndex::new(6, 6));
/// assert_eq!(camel_case_until("ABCD"), StrIndex::new(0, 0));
/// assert_eq!(camel_case_until("AbcDD"), StrIndex::new(3, 3));
@ -55,9 +56,10 @@ pub fn camel_case_until(s: &str) -> StrIndex {
}
}
/// Returns index of the last camel-case component of `s`.
/// Returns index of the first camel-case component of `s`.
///
/// ```
/// # use clippy_utils::str_utils::{camel_case_start, StrIndex};
/// assert_eq!(camel_case_start("AbcDef"), StrIndex::new(0, 0));
/// assert_eq!(camel_case_start("abcDef"), StrIndex::new(3, 3));
/// assert_eq!(camel_case_start("ABCD"), StrIndex::new(4, 4));
@ -66,19 +68,37 @@ pub fn camel_case_until(s: &str) -> StrIndex {
/// ```
#[must_use]
pub fn camel_case_start(s: &str) -> StrIndex {
camel_case_start_from_idx(s, 0)
}
/// Returns `StrIndex` of the last camel-case component of `s[idx..]`.
///
/// ```
/// # use clippy_utils::str_utils::{camel_case_start_from_idx, StrIndex};
/// assert_eq!(camel_case_start_from_idx("AbcDef", 0), StrIndex::new(0, 0));
/// assert_eq!(camel_case_start_from_idx("AbcDef", 1), StrIndex::new(3, 3));
/// assert_eq!(camel_case_start_from_idx("AbcDefGhi", 0), StrIndex::new(0, 0));
/// assert_eq!(camel_case_start_from_idx("AbcDefGhi", 1), StrIndex::new(3, 3));
/// assert_eq!(camel_case_start_from_idx("Abcdefg", 1), StrIndex::new(7, 7));
/// ```
pub fn camel_case_start_from_idx(s: &str, start_idx: usize) -> StrIndex {
let char_count = s.chars().count();
let range = 0..char_count;
let mut iter = range.rev().zip(s.char_indices().rev());
if let Some((char_index, (_, first))) = iter.next() {
if let Some((_, (_, first))) = iter.next() {
if !first.is_lowercase() {
return StrIndex::new(char_index, s.len());
return StrIndex::new(char_count, s.len());
}
} else {
return StrIndex::new(char_count, s.len());
}
let mut down = true;
let mut last_index = StrIndex::new(char_count, s.len());
for (char_index, (byte_index, c)) in iter {
if byte_index < start_idx {
break;
}
if down {
if c.is_uppercase() {
down = false;
@ -96,9 +116,55 @@ pub fn camel_case_start(s: &str) -> StrIndex {
return last_index;
}
}
last_index
}
/// Get the indexes of camel case components of a string `s`
///
/// ```
/// # use clippy_utils::str_utils::{camel_case_indices, StrIndex};
/// assert_eq!(
/// camel_case_indices("AbcDef"),
/// vec![StrIndex::new(0, 0), StrIndex::new(3, 3), StrIndex::new(6, 6)]
/// );
/// assert_eq!(
/// camel_case_indices("abcDef"),
/// vec![StrIndex::new(3, 3), StrIndex::new(6, 6)]
/// );
/// ```
pub fn camel_case_indices(s: &str) -> Vec<StrIndex> {
let mut result = Vec::new();
let mut str_idx = camel_case_start(s);
while str_idx.byte_index < s.len() {
let next_idx = str_idx.byte_index + 1;
result.push(str_idx);
str_idx = camel_case_start_from_idx(s, next_idx);
}
result.push(str_idx);
result
}
/// Split camel case string into a vector of its components
///
/// ```
/// # use clippy_utils::str_utils::{camel_case_split, StrIndex};
/// assert_eq!(camel_case_split("AbcDef"), vec!["Abc", "Def"]);
/// ```
pub fn camel_case_split(s: &str) -> Vec<&str> {
let mut offsets = camel_case_indices(s)
.iter()
.map(|e| e.byte_index)
.collect::<Vec<usize>>();
if offsets[0] != 0 {
offsets.insert(0, 0);
}
offsets.windows(2).map(|w| &s[w[0]..w[1]]).collect()
}
/// Dealing with sting comparison can be complicated, this struct ensures that both the
/// character and byte count are provided for correct indexing.
#[derive(Debug, Default, PartialEq, Eq)]
@ -116,6 +182,7 @@ impl StrCount {
/// Returns the number of chars that match from the start
///
/// ```
/// # use clippy_utils::str_utils::{count_match_start, StrCount};
/// assert_eq!(count_match_start("hello_mouse", "hello_penguin"), StrCount::new(6, 6));
/// assert_eq!(count_match_start("hello_clippy", "bye_bugs"), StrCount::new(0, 0));
/// assert_eq!(count_match_start("hello_world", "hello_world"), StrCount::new(11, 11));
@ -141,6 +208,7 @@ pub fn count_match_start(str1: &str, str2: &str) -> StrCount {
/// Returns the number of chars and bytes that match from the end
///
/// ```
/// # use clippy_utils::str_utils::{count_match_end, StrCount};
/// assert_eq!(count_match_end("hello_cat", "bye_cat"), StrCount::new(4, 4));
/// assert_eq!(count_match_end("if_item_thing", "enum_value"), StrCount::new(0, 0));
/// assert_eq!(count_match_end("Clippy", "Clippy"), StrCount::new(6, 6));
@ -227,4 +295,31 @@ mod test {
fn until_caps() {
assert_eq!(camel_case_until("ABCD"), StrIndex::new(0, 0));
}
#[test]
fn camel_case_start_from_idx_full() {
assert_eq!(camel_case_start_from_idx("AbcDef", 0), StrIndex::new(0, 0));
assert_eq!(camel_case_start_from_idx("AbcDef", 1), StrIndex::new(3, 3));
assert_eq!(camel_case_start_from_idx("AbcDef", 4), StrIndex::new(6, 6));
assert_eq!(camel_case_start_from_idx("AbcDefGhi", 0), StrIndex::new(0, 0));
assert_eq!(camel_case_start_from_idx("AbcDefGhi", 1), StrIndex::new(3, 3));
assert_eq!(camel_case_start_from_idx("Abcdefg", 1), StrIndex::new(7, 7));
}
#[test]
fn camel_case_indices_full() {
assert_eq!(camel_case_indices("Abc\u{f6}\u{f6}DD"), vec![StrIndex::new(7, 9)]);
}
#[test]
fn camel_case_split_full() {
assert_eq!(camel_case_split("A"), vec!["A"]);
assert_eq!(camel_case_split("AbcDef"), vec!["Abc", "Def"]);
assert_eq!(camel_case_split("Abc"), vec!["Abc"]);
assert_eq!(camel_case_split("abcDef"), vec!["abc", "Def"]);
assert_eq!(
camel_case_split("\u{f6}\u{f6}AabABcd"),
vec!["\u{f6}\u{f6}", "Aab", "A", "Bcd"]
);
}
}

View file

@ -1,9 +1,7 @@
//! Contains utility functions to generate suggestions.
#![deny(clippy::missing_docs_in_private_items)]
use crate::source::{
snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite,
};
use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite};
use crate::{get_parent_expr_for_hir, higher};
use rustc_ast::util::parser::AssocOp;
use rustc_ast::{ast, token};
@ -33,7 +31,7 @@ pub enum Sugg<'a> {
MaybeParen(Cow<'a, str>),
/// A binary operator expression, including `as`-casts and explicit type
/// coercion.
BinOp(AssocOp, Cow<'a, str>),
BinOp(AssocOp, Cow<'a, str>, Cow<'a, str>),
}
/// Literal constant `0`, for convenience.
@ -46,7 +44,8 @@ pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed(""));
impl Display for Sugg<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match *self {
Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) | Sugg::BinOp(_, ref s) => s.fmt(f),
Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) => s.fmt(f),
Sugg::BinOp(op, ref lhs, ref rhs) => binop_to_string(op, lhs, rhs).fmt(f),
}
}
}
@ -55,10 +54,8 @@ impl Display for Sugg<'_> {
impl<'a> Sugg<'a> {
/// Prepare a suggestion from an expression.
pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Self> {
snippet_opt(cx, expr.span).map(|snippet| {
let snippet = Cow::Owned(snippet);
Self::hir_from_snippet(expr, snippet)
})
let get_snippet = |span| snippet(cx, span, "");
snippet_opt(cx, expr.span).map(|_| Self::hir_from_snippet(expr, get_snippet))
}
/// Convenience function around `hir_opt` for suggestions with a default
@ -93,9 +90,8 @@ impl<'a> Sugg<'a> {
/// Same as `hir`, but will use the pre expansion span if the `expr` was in a macro.
pub fn hir_with_macro_callsite(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
let snippet = snippet_with_macro_callsite(cx, expr.span, default);
Self::hir_from_snippet(expr, snippet)
let get_snippet = |span| snippet_with_macro_callsite(cx, span, default);
Self::hir_from_snippet(expr, get_snippet)
}
/// Same as `hir`, but first walks the span up to the given context. This will result in the
@ -112,24 +108,26 @@ impl<'a> Sugg<'a> {
default: &'a str,
applicability: &mut Applicability,
) -> Self {
let (snippet, in_macro) = snippet_with_context(cx, expr.span, ctxt, default, applicability);
if in_macro {
Sugg::NonParen(snippet)
if expr.span.ctxt() == ctxt {
Self::hir_from_snippet(expr, |span| snippet(cx, span, default))
} else {
Self::hir_from_snippet(expr, snippet)
let snip = snippet_with_applicability(cx, expr.span, default, applicability);
Sugg::NonParen(snip)
}
}
/// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
/// function variants of `Sugg`, since these use different snippet functions.
fn hir_from_snippet(expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self {
fn hir_from_snippet(expr: &hir::Expr<'_>, get_snippet: impl Fn(Span) -> Cow<'a, str>) -> Self {
if let Some(range) = higher::Range::hir(expr) {
let op = match range.limits {
ast::RangeLimits::HalfOpen => AssocOp::DotDot,
ast::RangeLimits::Closed => AssocOp::DotDotEq,
};
return Sugg::BinOp(op, snippet);
let start = range.start.map_or("".into(), |expr| get_snippet(expr.span));
let end = range.end.map_or("".into(), |expr| get_snippet(expr.span));
return Sugg::BinOp(op, start, end);
}
match expr.kind {
@ -139,7 +137,7 @@ impl<'a> Sugg<'a> {
| hir::ExprKind::Let(..)
| hir::ExprKind::Closure(..)
| hir::ExprKind::Unary(..)
| hir::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
| hir::ExprKind::Match(..) => Sugg::MaybeParen(get_snippet(expr.span)),
hir::ExprKind::Continue(..)
| hir::ExprKind::Yield(..)
| hir::ExprKind::Array(..)
@ -160,12 +158,20 @@ impl<'a> Sugg<'a> {
| hir::ExprKind::Struct(..)
| hir::ExprKind::Tup(..)
| hir::ExprKind::DropTemps(_)
| hir::ExprKind::Err => Sugg::NonParen(snippet),
hir::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
hir::ExprKind::AssignOp(op, ..) => Sugg::BinOp(hirbinop2assignop(op), snippet),
hir::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(op.node.into()), snippet),
hir::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
hir::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
| hir::ExprKind::Err => Sugg::NonParen(get_snippet(expr.span)),
hir::ExprKind::Assign(lhs, rhs, _) => {
Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span))
},
hir::ExprKind::AssignOp(op, lhs, rhs) => {
Sugg::BinOp(hirbinop2assignop(op), get_snippet(lhs.span), get_snippet(rhs.span))
},
hir::ExprKind::Binary(op, lhs, rhs) => Sugg::BinOp(
AssocOp::from_ast_binop(op.node.into()),
get_snippet(lhs.span),
get_snippet(rhs.span),
),
hir::ExprKind::Cast(lhs, ty) => Sugg::BinOp(AssocOp::As, get_snippet(lhs.span), get_snippet(ty.span)),
hir::ExprKind::Type(lhs, ty) => Sugg::BinOp(AssocOp::Colon, get_snippet(lhs.span), get_snippet(ty.span)),
}
}
@ -173,10 +179,12 @@ impl<'a> Sugg<'a> {
pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
use rustc_ast::ast::RangeLimits;
let snippet = if expr.span.from_expansion() {
snippet_with_macro_callsite(cx, expr.span, default)
} else {
snippet(cx, expr.span, default)
let get_whole_snippet = || {
if expr.span.from_expansion() {
snippet_with_macro_callsite(cx, expr.span, default)
} else {
snippet(cx, expr.span, default)
}
};
match expr.kind {
@ -186,7 +194,7 @@ impl<'a> Sugg<'a> {
| ast::ExprKind::If(..)
| ast::ExprKind::Let(..)
| ast::ExprKind::Unary(..)
| ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
| ast::ExprKind::Match(..) => Sugg::MaybeParen(get_whole_snippet()),
ast::ExprKind::Async(..)
| ast::ExprKind::Block(..)
| ast::ExprKind::Break(..)
@ -215,14 +223,42 @@ impl<'a> Sugg<'a> {
| ast::ExprKind::Array(..)
| ast::ExprKind::While(..)
| ast::ExprKind::Await(..)
| ast::ExprKind::Err => Sugg::NonParen(snippet),
ast::ExprKind::Range(.., RangeLimits::HalfOpen) => Sugg::BinOp(AssocOp::DotDot, snippet),
ast::ExprKind::Range(.., RangeLimits::Closed) => Sugg::BinOp(AssocOp::DotDotEq, snippet),
ast::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
ast::ExprKind::AssignOp(op, ..) => Sugg::BinOp(astbinop2assignop(op), snippet),
ast::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(op.node), snippet),
ast::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
ast::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
| ast::ExprKind::Err => Sugg::NonParen(get_whole_snippet()),
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
AssocOp::DotDot,
lhs.as_ref().map_or("".into(), |lhs| snippet(cx, lhs.span, default)),
rhs.as_ref().map_or("".into(), |rhs| snippet(cx, rhs.span, default)),
),
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp(
AssocOp::DotDotEq,
lhs.as_ref().map_or("".into(), |lhs| snippet(cx, lhs.span, default)),
rhs.as_ref().map_or("".into(), |rhs| snippet(cx, rhs.span, default)),
),
ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp(
AssocOp::Assign,
snippet(cx, lhs.span, default),
snippet(cx, rhs.span, default),
),
ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
astbinop2assignop(op),
snippet(cx, lhs.span, default),
snippet(cx, rhs.span, default),
),
ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
AssocOp::from_ast_binop(op.node),
snippet(cx, lhs.span, default),
snippet(cx, rhs.span, default),
),
ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp(
AssocOp::As,
snippet(cx, lhs.span, default),
snippet(cx, ty.span, default),
),
ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
AssocOp::Colon,
snippet(cx, lhs.span, default),
snippet(cx, ty.span, default),
),
}
}
@ -306,17 +342,51 @@ impl<'a> Sugg<'a> {
Sugg::NonParen(format!("({})", sugg).into())
}
},
Sugg::BinOp(_, sugg) => {
if has_enclosing_paren(&sugg) {
Sugg::NonParen(sugg)
} else {
Sugg::NonParen(format!("({})", sugg).into())
}
Sugg::BinOp(op, lhs, rhs) => {
let sugg = binop_to_string(op, &lhs, &rhs);
Sugg::NonParen(format!("({})", sugg).into())
},
}
}
}
/// Generates a string from the operator and both sides.
fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String {
match op {
AssocOp::Add
| AssocOp::Subtract
| AssocOp::Multiply
| AssocOp::Divide
| AssocOp::Modulus
| AssocOp::LAnd
| AssocOp::LOr
| AssocOp::BitXor
| AssocOp::BitAnd
| AssocOp::BitOr
| AssocOp::ShiftLeft
| AssocOp::ShiftRight
| AssocOp::Equal
| AssocOp::Less
| AssocOp::LessEqual
| AssocOp::NotEqual
| AssocOp::Greater
| AssocOp::GreaterEqual => format!(
"{} {} {}",
lhs,
op.to_ast_binop().expect("Those are AST ops").to_string(),
rhs
),
AssocOp::Assign => format!("{} = {}", lhs, rhs),
AssocOp::AssignOp(op) => {
format!("{} {}= {}", lhs, token_kind_to_string(&token::BinOp(op)), rhs)
},
AssocOp::As => format!("{} as {}", lhs, rhs),
AssocOp::DotDot => format!("{}..{}", lhs, rhs),
AssocOp::DotDotEq => format!("{}..={}", lhs, rhs),
AssocOp::Colon => format!("{}: {}", lhs, rhs),
}
}
/// Return `true` if `sugg` is enclosed in parenthesis.
fn has_enclosing_paren(sugg: impl AsRef<str>) -> bool {
let mut chars = sugg.as_ref().chars();
@ -391,10 +461,25 @@ impl Neg for Sugg<'_> {
}
}
impl Not for Sugg<'_> {
type Output = Sugg<'static>;
fn not(self) -> Sugg<'static> {
make_unop("!", self)
impl Not for Sugg<'a> {
type Output = Sugg<'a>;
fn not(self) -> Sugg<'a> {
use AssocOp::{Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual};
if let Sugg::BinOp(op, lhs, rhs) = self {
let to_op = match op {
Equal => NotEqual,
NotEqual => Equal,
Less => GreaterEqual,
GreaterEqual => Less,
Greater => LessEqual,
LessEqual => Greater,
_ => return make_unop("!", Sugg::BinOp(op, lhs, rhs)),
};
Sugg::BinOp(to_op, lhs, rhs)
} else {
make_unop("!", self)
}
}
}
@ -463,53 +548,21 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static>
|| is_shift(other) && is_arith(op)
}
let lhs_paren = if let Sugg::BinOp(lop, _) = *lhs {
let lhs_paren = if let Sugg::BinOp(lop, _, _) = *lhs {
needs_paren(op, lop, Associativity::Left)
} else {
false
};
let rhs_paren = if let Sugg::BinOp(rop, _) = *rhs {
let rhs_paren = if let Sugg::BinOp(rop, _, _) = *rhs {
needs_paren(op, rop, Associativity::Right)
} else {
false
};
let lhs = ParenHelper::new(lhs_paren, lhs);
let rhs = ParenHelper::new(rhs_paren, rhs);
let sugg = match op {
AssocOp::Add
| AssocOp::BitAnd
| AssocOp::BitOr
| AssocOp::BitXor
| AssocOp::Divide
| AssocOp::Equal
| AssocOp::Greater
| AssocOp::GreaterEqual
| AssocOp::LAnd
| AssocOp::LOr
| AssocOp::Less
| AssocOp::LessEqual
| AssocOp::Modulus
| AssocOp::Multiply
| AssocOp::NotEqual
| AssocOp::ShiftLeft
| AssocOp::ShiftRight
| AssocOp::Subtract => format!(
"{} {} {}",
lhs,
op.to_ast_binop().expect("Those are AST ops").to_string(),
rhs
),
AssocOp::Assign => format!("{} = {}", lhs, rhs),
AssocOp::AssignOp(op) => format!("{} {}= {}", lhs, token_kind_to_string(&token::BinOp(op)), rhs),
AssocOp::As => format!("{} as {}", lhs, rhs),
AssocOp::DotDot => format!("{}..{}", lhs, rhs),
AssocOp::DotDotEq => format!("{}..={}", lhs, rhs),
AssocOp::Colon => format!("{}: {}", lhs, rhs),
};
Sugg::BinOp(op, sugg.into())
let lhs = ParenHelper::new(lhs_paren, lhs).to_string();
let rhs = ParenHelper::new(rhs_paren, rhs).to_string();
Sugg::BinOp(op, lhs.into(), rhs.into())
}
/// Convenience wrapper around `make_assoc` and `AssocOp::from_ast_binop`.
@ -1007,10 +1060,32 @@ mod test {
#[test]
fn binop_maybe_par() {
let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1)".into());
let sugg = Sugg::BinOp(AssocOp::Add, "1".into(), "1".into());
assert_eq!("(1 + 1)", sugg.maybe_par().to_string());
let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1) + (1 + 1)".into());
let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1)".into(), "(1 + 1)".into());
assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_par().to_string());
}
#[test]
fn not_op() {
use AssocOp::{Add, Equal, Greater, GreaterEqual, LAnd, LOr, Less, LessEqual, NotEqual};
fn test_not(op: AssocOp, correct: &str) {
let sugg = Sugg::BinOp(op, "x".into(), "y".into());
assert_eq!((!sugg).to_string(), correct);
}
// Invert the comparison operator.
test_not(Equal, "x != y");
test_not(NotEqual, "x == y");
test_not(Less, "x >= y");
test_not(LessEqual, "x > y");
test_not(Greater, "x <= y");
test_not(GreaterEqual, "x < y");
// Other operators are inverted like !(..).
test_not(Add, "!(x + y)");
test_not(LAnd, "!(x && y)");
test_not(LOr, "!(x || y)");
}
}

View file

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2021-12-17"
channel = "nightly-2021-12-30"
components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]

View file

@ -7,7 +7,7 @@ LL | unsafe { 0 };
= note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
help: consider adding a safety comment
|
LL ~ // Safety: ...
LL ~ // SAFETY: ...
LL ~ unsafe { 0 };
|

View file

@ -145,4 +145,10 @@ enum HIDataRequest {
DeleteUnpubHIData(String),
}
enum North {
Normal,
NoLeft,
NoRight,
}
fn main() {}

View file

@ -6,6 +6,18 @@ LL | cFoo,
|
= note: `-D clippy::enum-variant-names` implied by `-D warnings`
error: all variants have the same prefix: `c`
--> $DIR/enum_variants.rs:14:1
|
LL | / enum Foo {
LL | | cFoo,
LL | | cBar,
LL | | cBaz,
LL | | }
| |_^
|
= help: remove the prefixes and use full paths to the variants instead of glob imports
error: variant name starts with the enum's name
--> $DIR/enum_variants.rs:26:5
|
@ -60,6 +72,18 @@ LL | | }
|
= help: remove the prefixes and use full paths to the variants instead of glob imports
error: all variants have the same prefix: `C`
--> $DIR/enum_variants.rs:59:1
|
LL | / enum Something {
LL | | CCall,
LL | | CCreate,
LL | | CCryogenize,
LL | | }
| |_^
|
= help: remove the prefixes and use full paths to the variants instead of glob imports
error: all variants have the same prefix: `WithOut`
--> $DIR/enum_variants.rs:81:1
|
@ -72,18 +96,6 @@ LL | | }
|
= help: remove the prefixes and use full paths to the variants instead of glob imports
error: all variants have the same prefix: `Prefix`
--> $DIR/enum_variants.rs:87:1
|
LL | / enum NonCaps {
LL | | Prefix的,
LL | | PrefixTea,
LL | | PrefixCake,
LL | | }
| |_^
|
= help: remove the prefixes and use full paths to the variants instead of glob imports
error: all variants have the same postfix: `IData`
--> $DIR/enum_variants.rs:136:1
|
@ -108,5 +120,5 @@ LL | | }
|
= help: remove the postfixes and use full paths to the variants instead of glob imports
error: aborting due to 11 previous errors
error: aborting due to 12 previous errors

View file

@ -11,7 +11,12 @@ pub const fn const_context() {
fn main() {
let x = 3f32;
let _ = x.to_degrees();
let _ = 90.0_f64.to_degrees();
let _ = 90.5_f64.to_degrees();
let _ = x.to_radians();
let _ = 90.0_f64.to_radians();
let _ = 90.5_f64.to_radians();
// let _ = 90.5 * 80. * std::f32::consts::PI / 180f32;
// Cases where the lint shouldn't be applied
let _ = x * 90f32 / std::f32::consts::PI;
let _ = x * std::f32::consts::PI / 90f32;

View file

@ -11,7 +11,12 @@ pub const fn const_context() {
fn main() {
let x = 3f32;
let _ = x * 180f32 / std::f32::consts::PI;
let _ = 90. * 180f64 / std::f64::consts::PI;
let _ = 90.5 * 180f64 / std::f64::consts::PI;
let _ = x * std::f32::consts::PI / 180f32;
let _ = 90. * std::f32::consts::PI / 180f32;
let _ = 90.5 * std::f32::consts::PI / 180f32;
// let _ = 90.5 * 80. * std::f32::consts::PI / 180f32;
// Cases where the lint shouldn't be applied
let _ = x * 90f32 / std::f32::consts::PI;
let _ = x * std::f32::consts::PI / 90f32;

View file

@ -6,11 +6,35 @@ LL | let _ = x * 180f32 / std::f32::consts::PI;
|
= note: `-D clippy::suboptimal-flops` implied by `-D warnings`
error: conversion to radians can be done more accurately
error: conversion to degrees can be done more accurately
--> $DIR/floating_point_rad.rs:14:13
|
LL | let _ = 90. * 180f64 / std::f64::consts::PI;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.0_f64.to_degrees()`
error: conversion to degrees can be done more accurately
--> $DIR/floating_point_rad.rs:15:13
|
LL | let _ = 90.5 * 180f64 / std::f64::consts::PI;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.5_f64.to_degrees()`
error: conversion to radians can be done more accurately
--> $DIR/floating_point_rad.rs:16:13
|
LL | let _ = x * std::f32::consts::PI / 180f32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()`
error: aborting due to 2 previous errors
error: conversion to radians can be done more accurately
--> $DIR/floating_point_rad.rs:17:13
|
LL | let _ = 90. * std::f32::consts::PI / 180f32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.0_f64.to_radians()`
error: conversion to radians can be done more accurately
--> $DIR/floating_point_rad.rs:18:13
|
LL | let _ = 90.5 * std::f32::consts::PI / 180f32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.5_f64.to_radians()`
error: aborting due to 6 previous errors

View file

@ -2,10 +2,20 @@ const ONE: i64 = 1;
const NEG_ONE: i64 = -1;
const ZERO: i64 = 0;
struct A(String);
impl std::ops::Shl<i32> for A {
type Output = A;
fn shl(mut self, other: i32) -> Self {
self.0.push_str(&format!("{}", other));
self
}
}
#[allow(
clippy::eq_op,
clippy::no_effect,
clippy::unnecessary_operation,
clippy::op_ref,
clippy::double_parens
)]
#[warn(clippy::identity_op)]
@ -38,4 +48,9 @@ fn main() {
42 << 0;
1 >> 0;
42 >> 0;
&x >> 0;
x >> &0;
let mut a = A("".into());
let b = a << 0; // no error: non-integer
}

View file

@ -1,5 +1,5 @@
error: the operation is ineffective. Consider reducing it to `x`
--> $DIR/identity_op.rs:16:5
--> $DIR/identity_op.rs:26:5
|
LL | x + 0;
| ^^^^^
@ -7,64 +7,76 @@ LL | x + 0;
= note: `-D clippy::identity-op` implied by `-D warnings`
error: the operation is ineffective. Consider reducing it to `x`
--> $DIR/identity_op.rs:17:5
--> $DIR/identity_op.rs:27:5
|
LL | x + (1 - 1);
| ^^^^^^^^^^^
error: the operation is ineffective. Consider reducing it to `x`
--> $DIR/identity_op.rs:19:5
--> $DIR/identity_op.rs:29:5
|
LL | 0 + x;
| ^^^^^
error: the operation is ineffective. Consider reducing it to `x`
--> $DIR/identity_op.rs:22:5
--> $DIR/identity_op.rs:32:5
|
LL | x | (0);
| ^^^^^^^
error: the operation is ineffective. Consider reducing it to `x`
--> $DIR/identity_op.rs:25:5
--> $DIR/identity_op.rs:35:5
|
LL | x * 1;
| ^^^^^
error: the operation is ineffective. Consider reducing it to `x`
--> $DIR/identity_op.rs:26:5
--> $DIR/identity_op.rs:36:5
|
LL | 1 * x;
| ^^^^^
error: the operation is ineffective. Consider reducing it to `x`
--> $DIR/identity_op.rs:32:5
--> $DIR/identity_op.rs:42:5
|
LL | -1 & x;
| ^^^^^^
error: the operation is ineffective. Consider reducing it to `u`
--> $DIR/identity_op.rs:35:5
--> $DIR/identity_op.rs:45:5
|
LL | u & 255;
| ^^^^^^^
error: the operation is ineffective. Consider reducing it to `42`
--> $DIR/identity_op.rs:38:5
--> $DIR/identity_op.rs:48:5
|
LL | 42 << 0;
| ^^^^^^^
error: the operation is ineffective. Consider reducing it to `1`
--> $DIR/identity_op.rs:39:5
--> $DIR/identity_op.rs:49:5
|
LL | 1 >> 0;
| ^^^^^^
error: the operation is ineffective. Consider reducing it to `42`
--> $DIR/identity_op.rs:40:5
--> $DIR/identity_op.rs:50:5
|
LL | 42 >> 0;
| ^^^^^^^
error: aborting due to 11 previous errors
error: the operation is ineffective. Consider reducing it to `&x`
--> $DIR/identity_op.rs:51:5
|
LL | &x >> 0;
| ^^^^^^^
error: the operation is ineffective. Consider reducing it to `x`
--> $DIR/identity_op.rs:52:5
|
LL | x >> &0;
| ^^^^^^^
error: aborting due to 13 previous errors

View file

@ -4,6 +4,7 @@
#![warn(clippy::iter_skip_next)]
#![allow(clippy::blacklisted_name)]
#![allow(clippy::iter_nth)]
#![allow(unused_mut, dead_code)]
extern crate option_helpers;
@ -19,4 +20,18 @@ fn main() {
let foo = IteratorFalsePositives { foo: 0 };
let _ = foo.skip(42).next();
let _ = foo.filter().skip(42).next();
// fix #8128
let test_string = "1|1 2";
let mut sp = test_string.split('|').map(|s| s.trim());
let _: Vec<&str> = sp.nth(1).unwrap().split(' ').collect();
if let Some(mut s) = Some(test_string.split('|').map(|s| s.trim())) {
let _: Vec<&str> = s.nth(1).unwrap().split(' ').collect();
};
fn check<T>(mut s: T)
where
T: Iterator<Item = String>,
{
let _: Vec<&str> = s.nth(1).unwrap().split(' ').collect();
}
}

View file

@ -4,6 +4,7 @@
#![warn(clippy::iter_skip_next)]
#![allow(clippy::blacklisted_name)]
#![allow(clippy::iter_nth)]
#![allow(unused_mut, dead_code)]
extern crate option_helpers;
@ -19,4 +20,18 @@ fn main() {
let foo = IteratorFalsePositives { foo: 0 };
let _ = foo.skip(42).next();
let _ = foo.filter().skip(42).next();
// fix #8128
let test_string = "1|1 2";
let mut sp = test_string.split('|').map(|s| s.trim());
let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect();
if let Some(mut s) = Some(test_string.split('|').map(|s| s.trim())) {
let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
};
fn check<T>(mut s: T)
where
T: Iterator<Item = String>,
{
let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
}
}

View file

@ -1,5 +1,5 @@
error: called `skip(..).next()` on an iterator
--> $DIR/iter_skip_next.rs:15:28
--> $DIR/iter_skip_next.rs:16:28
|
LL | let _ = some_vec.iter().skip(42).next();
| ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
@ -7,22 +7,40 @@ LL | let _ = some_vec.iter().skip(42).next();
= note: `-D clippy::iter-skip-next` implied by `-D warnings`
error: called `skip(..).next()` on an iterator
--> $DIR/iter_skip_next.rs:16:36
--> $DIR/iter_skip_next.rs:17:36
|
LL | let _ = some_vec.iter().cycle().skip(42).next();
| ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)`
error: called `skip(..).next()` on an iterator
--> $DIR/iter_skip_next.rs:17:20
--> $DIR/iter_skip_next.rs:18:20
|
LL | let _ = (1..10).skip(10).next();
| ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)`
error: called `skip(..).next()` on an iterator
--> $DIR/iter_skip_next.rs:18:33
--> $DIR/iter_skip_next.rs:19:33
|
LL | let _ = &some_vec[..].iter().skip(3).next();
| ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(3)`
error: aborting due to 4 previous errors
error: called `skip(..).next()` on an iterator
--> $DIR/iter_skip_next.rs:27:26
|
LL | let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect();
| ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
error: called `skip(..).next()` on an iterator
--> $DIR/iter_skip_next.rs:29:29
|
LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
| ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
error: called `skip(..).next()` on an iterator
--> $DIR/iter_skip_next.rs:35:29
|
LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
| ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
error: aborting due to 7 previous errors

View file

@ -0,0 +1,19 @@
#![warn(clippy::iter_skip_next)]
#![allow(dead_code)]
/// Checks implementation of `ITER_SKIP_NEXT` lint
fn main() {
// fix #8128
let test_string = "1|1 2";
let sp = test_string.split('|').map(|s| s.trim());
let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect();
if let Some(s) = Some(test_string.split('|').map(|s| s.trim())) {
let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
};
fn check<T>(s: T)
where
T: Iterator<Item = String>,
{
let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
}
}

View file

@ -0,0 +1,39 @@
error: called `skip(..).next()` on an iterator
--> $DIR/iter_skip_next_unfixable.rs:9:26
|
LL | let _: Vec<&str> = sp.skip(1).next().unwrap().split(' ').collect();
| ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
|
= note: `-D clippy::iter-skip-next` implied by `-D warnings`
help: for this change `sp` has to be mutable
--> $DIR/iter_skip_next_unfixable.rs:8:9
|
LL | let sp = test_string.split('|').map(|s| s.trim());
| ^^
error: called `skip(..).next()` on an iterator
--> $DIR/iter_skip_next_unfixable.rs:11:29
|
LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
| ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
|
help: for this change `s` has to be mutable
--> $DIR/iter_skip_next_unfixable.rs:10:17
|
LL | if let Some(s) = Some(test_string.split('|').map(|s| s.trim())) {
| ^
error: called `skip(..).next()` on an iterator
--> $DIR/iter_skip_next_unfixable.rs:17:29
|
LL | let _: Vec<&str> = s.skip(1).next().unwrap().split(' ').collect();
| ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(1)`
|
help: for this change `s` has to be mutable
--> $DIR/iter_skip_next_unfixable.rs:13:17
|
LL | fn check<T>(s: T)
| ^
error: aborting due to 3 previous errors

View file

@ -43,7 +43,7 @@ LL | / for i in 3..(3 + src.len()) {
LL | | dst[i] = src[count];
LL | | count += 1;
LL | | }
| |_____^ help: try replacing the loop by: `dst[3..(3 + src.len())].clone_from_slice(&src[..((3 + src.len()) - 3)]);`
| |_____^ help: try replacing the loop by: `dst[3..(3 + src.len())].clone_from_slice(&src[..(3 + src.len() - 3)]);`
error: it looks like you're manually copying between slices
--> $DIR/with_loop_counters.rs:35:5

View file

@ -41,6 +41,15 @@ fn main() {
x;
!x;
!(x && y);
let a = 0;
let b = 1;
a != b;
a == b;
a >= b;
a > b;
a <= b;
a < b;
if x {
x
} else {

View file

@ -53,6 +53,39 @@ fn main() {
} else {
true
};
let a = 0;
let b = 1;
if a == b {
false
} else {
true
};
if a != b {
false
} else {
true
};
if a < b {
false
} else {
true
};
if a <= b {
false
} else {
true
};
if a > b {
false
} else {
true
};
if a >= b {
false
} else {
true
};
if x {
x
} else {

View file

@ -31,7 +31,67 @@ LL | | };
| |_____^ help: you can reduce it to: `!(x && y)`
error: this if-then-else expression returns a bool literal
--> $DIR/fixable.rs:72:5
--> $DIR/fixable.rs:59:5
|
LL | / if a == b {
LL | | false
LL | | } else {
LL | | true
LL | | };
| |_____^ help: you can reduce it to: `a != b`
error: this if-then-else expression returns a bool literal
--> $DIR/fixable.rs:64:5
|
LL | / if a != b {
LL | | false
LL | | } else {
LL | | true
LL | | };
| |_____^ help: you can reduce it to: `a == b`
error: this if-then-else expression returns a bool literal
--> $DIR/fixable.rs:69:5
|
LL | / if a < b {
LL | | false
LL | | } else {
LL | | true
LL | | };
| |_____^ help: you can reduce it to: `a >= b`
error: this if-then-else expression returns a bool literal
--> $DIR/fixable.rs:74:5
|
LL | / if a <= b {
LL | | false
LL | | } else {
LL | | true
LL | | };
| |_____^ help: you can reduce it to: `a > b`
error: this if-then-else expression returns a bool literal
--> $DIR/fixable.rs:79:5
|
LL | / if a > b {
LL | | false
LL | | } else {
LL | | true
LL | | };
| |_____^ help: you can reduce it to: `a <= b`
error: this if-then-else expression returns a bool literal
--> $DIR/fixable.rs:84:5
|
LL | / if a >= b {
LL | | false
LL | | } else {
LL | | true
LL | | };
| |_____^ help: you can reduce it to: `a < b`
error: this if-then-else expression returns a bool literal
--> $DIR/fixable.rs:105:5
|
LL | / if x {
LL | | return true;
@ -41,7 +101,7 @@ LL | | };
| |_____^ help: you can reduce it to: `return x`
error: this if-then-else expression returns a bool literal
--> $DIR/fixable.rs:80:5
--> $DIR/fixable.rs:113:5
|
LL | / if x {
LL | | return false;
@ -51,7 +111,7 @@ LL | | };
| |_____^ help: you can reduce it to: `return !x`
error: this if-then-else expression returns a bool literal
--> $DIR/fixable.rs:88:5
--> $DIR/fixable.rs:121:5
|
LL | / if x && y {
LL | | return true;
@ -61,7 +121,7 @@ LL | | };
| |_____^ help: you can reduce it to: `return x && y`
error: this if-then-else expression returns a bool literal
--> $DIR/fixable.rs:96:5
--> $DIR/fixable.rs:129:5
|
LL | / if x && y {
LL | | return false;
@ -71,7 +131,7 @@ LL | | };
| |_____^ help: you can reduce it to: `return !(x && y)`
error: equality checks against true are unnecessary
--> $DIR/fixable.rs:104:8
--> $DIR/fixable.rs:137:8
|
LL | if x == true {};
| ^^^^^^^^^ help: try simplifying it as shown: `x`
@ -79,25 +139,25 @@ LL | if x == true {};
= note: `-D clippy::bool-comparison` implied by `-D warnings`
error: equality checks against false can be replaced by a negation
--> $DIR/fixable.rs:108:8
--> $DIR/fixable.rs:141:8
|
LL | if x == false {};
| ^^^^^^^^^^ help: try simplifying it as shown: `!x`
error: equality checks against true are unnecessary
--> $DIR/fixable.rs:118:8
--> $DIR/fixable.rs:151:8
|
LL | if x == true {};
| ^^^^^^^^^ help: try simplifying it as shown: `x`
error: equality checks against false can be replaced by a negation
--> $DIR/fixable.rs:119:8
--> $DIR/fixable.rs:152:8
|
LL | if x == false {};
| ^^^^^^^^^^ help: try simplifying it as shown: `!x`
error: this if-then-else expression returns a bool literal
--> $DIR/fixable.rs:128:12
--> $DIR/fixable.rs:161:12
|
LL | } else if returns_bool() {
| ____________^
@ -108,7 +168,7 @@ LL | | };
| |_____^ help: you can reduce it to: `{ !returns_bool() }`
error: this if-then-else expression returns a bool literal
--> $DIR/fixable.rs:141:5
--> $DIR/fixable.rs:174:5
|
LL | / if unsafe { no(4) } & 1 != 0 {
LL | | true
@ -118,16 +178,16 @@ LL | | };
| |_____^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)`
error: this if-then-else expression returns a bool literal
--> $DIR/fixable.rs:146:30
--> $DIR/fixable.rs:179:30
|
LL | let _brackets_unneeded = if unsafe { no(4) } & 1 != 0 { true } else { false };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `unsafe { no(4) } & 1 != 0`
error: this if-then-else expression returns a bool literal
--> $DIR/fixable.rs:149:9
--> $DIR/fixable.rs:182:9
|
LL | if unsafe { no(4) } & 1 != 0 { true } else { false }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)`
error: aborting due to 15 previous errors
error: aborting due to 21 previous errors

View file

@ -71,7 +71,18 @@ fn test_void_if_fun(b: bool) {
fn test_void_match(x: u32) {
match x {
0 => (),
_ => {},
_ => (),
}
}
fn test_nested_match(x: u32) {
match x {
0 => (),
1 => {
let _ = 42;
},
_ => (),
}
}
@ -182,7 +193,7 @@ async fn async_test_void_if_fun(b: bool) {
async fn async_test_void_match(x: u32) {
match x {
0 => (),
_ => {},
_ => (),
}
}

View file

@ -75,6 +75,17 @@ fn test_void_match(x: u32) {
}
}
fn test_nested_match(x: u32) {
match x {
0 => (),
1 => {
let _ = 42;
return;
},
_ => return,
}
}
fn read_line() -> String {
use std::io::BufRead;
let stdin = ::std::io::stdin();

View file

@ -70,127 +70,139 @@ error: unneeded `return` statement
--> $DIR/needless_return.rs:74:14
|
LL | _ => return,
| ^^^^^^ help: replace `return` with an empty block: `{}`
| ^^^^^^ help: replace `return` with a unit value: `()`
error: unneeded `return` statement
--> $DIR/needless_return.rs:89:9
|
LL | return String::from("test");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
error: unneeded `return` statement
--> $DIR/needless_return.rs:91:9
|
LL | return String::new();
| ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
error: unneeded `return` statement
--> $DIR/needless_return.rs:113:32
|
LL | bar.unwrap_or_else(|_| return)
| ^^^^^^ help: replace `return` with an empty block: `{}`
error: unneeded `return` statement
--> $DIR/needless_return.rs:118:13
--> $DIR/needless_return.rs:83:13
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
--> $DIR/needless_return.rs:120:20
|
LL | let _ = || return;
| ^^^^^^ help: replace `return` with an empty block: `{}`
error: unneeded `return` statement
--> $DIR/needless_return.rs:126:32
|
LL | res.unwrap_or_else(|_| return Foo)
| ^^^^^^^^^^ help: remove `return`: `Foo`
error: unneeded `return` statement
--> $DIR/needless_return.rs:135:5
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
--> $DIR/needless_return.rs:139:5
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
--> $DIR/needless_return.rs:144:9
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
--> $DIR/needless_return.rs:146:9
|
LL | return false;
| ^^^^^^^^^^^^^ help: remove `return`: `false`
error: unneeded `return` statement
--> $DIR/needless_return.rs:152:17
|
LL | true => return false,
| ^^^^^^^^^^^^ help: remove `return`: `false`
error: unneeded `return` statement
--> $DIR/needless_return.rs:154:13
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
--> $DIR/needless_return.rs:161:9
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
--> $DIR/needless_return.rs:163:16
|
LL | let _ = || return true;
| ^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
--> $DIR/needless_return.rs:171:5
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
--> $DIR/needless_return.rs:176:9
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
--> $DIR/needless_return.rs:178:9
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
--> $DIR/needless_return.rs:185:14
--> $DIR/needless_return.rs:85:14
|
LL | _ => return,
| ^^^^^^ help: replace `return` with an empty block: `{}`
| ^^^^^^ help: replace `return` with a unit value: `()`
error: unneeded `return` statement
--> $DIR/needless_return.rs:200:9
--> $DIR/needless_return.rs:100:9
|
LL | return String::from("test");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
error: unneeded `return` statement
--> $DIR/needless_return.rs:202:9
--> $DIR/needless_return.rs:102:9
|
LL | return String::new();
| ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
error: aborting due to 32 previous errors
error: unneeded `return` statement
--> $DIR/needless_return.rs:124:32
|
LL | bar.unwrap_or_else(|_| return)
| ^^^^^^ help: replace `return` with an empty block: `{}`
error: unneeded `return` statement
--> $DIR/needless_return.rs:129:13
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
--> $DIR/needless_return.rs:131:20
|
LL | let _ = || return;
| ^^^^^^ help: replace `return` with an empty block: `{}`
error: unneeded `return` statement
--> $DIR/needless_return.rs:137:32
|
LL | res.unwrap_or_else(|_| return Foo)
| ^^^^^^^^^^ help: remove `return`: `Foo`
error: unneeded `return` statement
--> $DIR/needless_return.rs:146:5
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
--> $DIR/needless_return.rs:150:5
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
--> $DIR/needless_return.rs:155:9
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
--> $DIR/needless_return.rs:157:9
|
LL | return false;
| ^^^^^^^^^^^^^ help: remove `return`: `false`
error: unneeded `return` statement
--> $DIR/needless_return.rs:163:17
|
LL | true => return false,
| ^^^^^^^^^^^^ help: remove `return`: `false`
error: unneeded `return` statement
--> $DIR/needless_return.rs:165:13
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
--> $DIR/needless_return.rs:172:9
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
--> $DIR/needless_return.rs:174:16
|
LL | let _ = || return true;
| ^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
--> $DIR/needless_return.rs:182:5
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
--> $DIR/needless_return.rs:187:9
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
--> $DIR/needless_return.rs:189:9
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
--> $DIR/needless_return.rs:196:14
|
LL | _ => return,
| ^^^^^^ help: replace `return` with a unit value: `()`
error: unneeded `return` statement
--> $DIR/needless_return.rs:211:9
|
LL | return String::from("test");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
error: unneeded `return` statement
--> $DIR/needless_return.rs:213:9
|
LL | return String::new();
| ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
error: aborting due to 34 previous errors

View file

@ -0,0 +1,45 @@
// run-rustfix
#![warn(clippy::neg_multiply)]
#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::precedence)]
#![allow(unused)]
use std::ops::Mul;
struct X;
impl Mul<isize> for X {
type Output = X;
fn mul(self, _r: isize) -> Self {
self
}
}
impl Mul<X> for isize {
type Output = X;
fn mul(self, _r: X) -> X {
X
}
}
fn main() {
let x = 0;
-x;
-x;
100 + -x;
-(100 + x);
-17;
0xcafe | -0xff00;
-1 * -1; // should be ok
X * -1; // should be ok
-1 * X; // should also be ok
}

View file

@ -1,5 +1,7 @@
// run-rustfix
#![warn(clippy::neg_multiply)]
#![allow(clippy::no_effect, clippy::unnecessary_operation)]
#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::precedence)]
#![allow(unused)]
use std::ops::Mul;
@ -28,6 +30,14 @@ fn main() {
-1 * x;
100 + x * -1;
(100 + x) * -1;
-1 * 17;
0xcafe | 0xff00 * -1;
-1 * -1; // should be ok
X * -1; // should be ok

View file

@ -1,16 +1,40 @@
error: negation by multiplying with `-1`
--> $DIR/neg_multiply.rs:27:5
error: this multiplication by -1 can be written more succinctly
--> $DIR/neg_multiply.rs:29:5
|
LL | x * -1;
| ^^^^^^
| ^^^^^^ help: consider using: `-x`
|
= note: `-D clippy::neg-multiply` implied by `-D warnings`
error: negation by multiplying with `-1`
--> $DIR/neg_multiply.rs:29:5
error: this multiplication by -1 can be written more succinctly
--> $DIR/neg_multiply.rs:31:5
|
LL | -1 * x;
| ^^^^^^
| ^^^^^^ help: consider using: `-x`
error: aborting due to 2 previous errors
error: this multiplication by -1 can be written more succinctly
--> $DIR/neg_multiply.rs:33:11
|
LL | 100 + x * -1;
| ^^^^^^ help: consider using: `-x`
error: this multiplication by -1 can be written more succinctly
--> $DIR/neg_multiply.rs:35:5
|
LL | (100 + x) * -1;
| ^^^^^^^^^^^^^^ help: consider using: `-(100 + x)`
error: this multiplication by -1 can be written more succinctly
--> $DIR/neg_multiply.rs:37:5
|
LL | -1 * 17;
| ^^^^^^^ help: consider using: `-17`
error: this multiplication by -1 can be written more succinctly
--> $DIR/neg_multiply.rs:39:14
|
LL | 0xcafe | 0xff00 * -1;
| ^^^^^^^^^^^ help: consider using: `-0xff00`
error: aborting due to 6 previous errors

View file

@ -0,0 +1,33 @@
//run-rustfix
#![warn(clippy::init_numbered_fields)]
#[derive(Default)]
struct TupleStruct(u32, u32, u8);
// This shouldn't lint because it's in a macro
macro_rules! tuple_struct_init {
() => {
TupleStruct { 0: 0, 1: 1, 2: 2 }
};
}
fn main() {
let tuple_struct = TupleStruct::default();
// This should lint
let _ = TupleStruct(1u32, 42, 23u8);
// This should also lint and order the fields correctly
let _ = TupleStruct(1u32, 3u32, 2u8);
// Ok because of default initializer
let _ = TupleStruct { 0: 42, ..tuple_struct };
let _ = TupleStruct {
1: 23,
..TupleStruct::default()
};
// Ok because it's in macro
let _ = tuple_struct_init!();
}

View file

@ -0,0 +1,41 @@
//run-rustfix
#![warn(clippy::init_numbered_fields)]
#[derive(Default)]
struct TupleStruct(u32, u32, u8);
// This shouldn't lint because it's in a macro
macro_rules! tuple_struct_init {
() => {
TupleStruct { 0: 0, 1: 1, 2: 2 }
};
}
fn main() {
let tuple_struct = TupleStruct::default();
// This should lint
let _ = TupleStruct {
0: 1u32,
1: 42,
2: 23u8,
};
// This should also lint and order the fields correctly
let _ = TupleStruct {
0: 1u32,
2: 2u8,
1: 3u32,
};
// Ok because of default initializer
let _ = TupleStruct { 0: 42, ..tuple_struct };
let _ = TupleStruct {
1: 23,
..TupleStruct::default()
};
// Ok because it's in macro
let _ = tuple_struct_init!();
}

View file

@ -0,0 +1,26 @@
error: used a field initializer for a tuple struct
--> $DIR/numbered_fields.rs:18:13
|
LL | let _ = TupleStruct {
| _____________^
LL | | 0: 1u32,
LL | | 1: 42,
LL | | 2: 23u8,
LL | | };
| |_____^ help: try this instead: `TupleStruct(1u32, 42, 23u8)`
|
= note: `-D clippy::init-numbered-fields` implied by `-D warnings`
error: used a field initializer for a tuple struct
--> $DIR/numbered_fields.rs:25:13
|
LL | let _ = TupleStruct {
| _____________^
LL | | 0: 1u32,
LL | | 2: 2u8,
LL | | 1: 3u32,
LL | | };
| |_____^ help: try this instead: `TupleStruct(1u32, 3u32, 2u8)`
error: aborting due to 2 previous errors

View file

@ -5,12 +5,12 @@ pub struct Bar;
pub trait Whatever {
fn what(&self) -> Self;
// There should be no warning here!
// There should be no warning here! (returns a reference)
fn what2(&self) -> &Self;
}
impl Bar {
// There should be no warning here!
// There should be no warning here! (note taking a self argument)
pub fn not_new() -> Self {
Self
}
@ -20,23 +20,38 @@ impl Bar {
pub fn bar(self) -> Self {
self
}
// There should be no warning here!
// There should be no warning here! (private method)
fn foo2(&self) -> Self {
Self
}
// There should be no warning here!
// There should be no warning here! (returns a reference)
pub fn foo3(&self) -> &Self {
self
}
// There should be no warning here! (already a `must_use` attribute)
#[must_use]
pub fn foo4(&self) -> Self {
Self
}
}
impl Whatever for Bar {
// There should be no warning here!
// There should be no warning here! (comes from the trait)
fn what(&self) -> Self {
self.foo2()
}
// There should be no warning here!
// There should be no warning here! (comes from the trait)
fn what2(&self) -> &Self {
self
}
}
#[must_use]
pub struct Foo;
impl Foo {
// There should be no warning here! (`Foo` already implements `#[must_use]`)
fn foo(&self) -> Self {
Self
}
}

View file

@ -47,6 +47,8 @@ fn syntax() {
let _ = |[x]: [u32; 1]| {
let x = 1;
};
let y = Some(1);
if let Some(y) = y {}
}
fn negative() {

View file

@ -241,17 +241,29 @@ note: previous binding is here
LL | let _ = |[x]: [u32; 1]| {
| ^
error: `y` is shadowed
--> $DIR/shadow.rs:51:17
|
LL | if let Some(y) = y {}
| ^
|
note: previous binding is here
--> $DIR/shadow.rs:50:9
|
LL | let y = Some(1);
| ^
error: `_b` shadows a previous, unrelated binding
--> $DIR/shadow.rs:85:9
--> $DIR/shadow.rs:87:9
|
LL | let _b = _a;
| ^^
|
note: previous binding is here
--> $DIR/shadow.rs:84:28
--> $DIR/shadow.rs:86:28
|
LL | pub async fn foo2(_a: i32, _b: i64) {
| ^^
error: aborting due to 21 previous errors
error: aborting due to 22 previous errors

View file

@ -6,7 +6,7 @@
fn main() {
if f() { g(); }
if !f() { g(); }
if !(1 == 2) { g(); }
if 1 != 2 { g(); }
}
fn f() -> bool {

View file

@ -16,7 +16,7 @@ error: boolean short circuit operator in statement may be clearer using an expli
--> $DIR/short_circuit_statement.rs:9:5
|
LL | 1 == 2 || g();
| ^^^^^^^^^^^^^^ help: replace it with: `if !(1 == 2) { g(); }`
| ^^^^^^^^^^^^^^ help: replace it with: `if 1 != 2 { g(); }`
error: aborting due to 3 previous errors

View file

@ -5,7 +5,7 @@
fn nested_local() {
let _ = {
let _ = {
// Safety:
// SAFETY:
let _ = unsafe {};
};
};
@ -14,7 +14,7 @@ fn nested_local() {
fn deep_nest() {
let _ = {
let _ = {
// Safety:
// SAFETY:
let _ = unsafe {};
// Safety:
@ -28,7 +28,7 @@ fn deep_nest() {
// Safety:
let _ = unsafe {};
// Safety:
// SAFETY:
unsafe {};
};
};
@ -44,7 +44,7 @@ fn deep_nest() {
unsafe {};
};
// Safety:
// SAFETY:
unsafe {};
}
@ -59,7 +59,7 @@ fn line_comment() {
}
fn line_comment_newlines() {
// Safety:
// SAFETY:
unsafe {}
}
@ -84,7 +84,7 @@ fn block_comment() {
}
fn block_comment_newlines() {
/* Safety: */
/* SAFETY: */
unsafe {}
}
@ -96,7 +96,7 @@ fn inline_block_comment() {
fn block_comment_with_extras() {
/* This is a description
* Safety:
* SAFETY:
*/
unsafe {}
}
@ -122,7 +122,7 @@ fn buried_safety() {
}
fn safety_with_prepended_text() {
// This is a test. Safety:
// This is a test. safety:
unsafe {}
}
@ -132,7 +132,7 @@ fn local_line_comment() {
}
fn local_block_comment() {
/* Safety: */
/* SAFETY: */
let _ = unsafe {};
}
@ -142,18 +142,18 @@ fn comment_array() {
}
fn comment_tuple() {
// Safety:
// sAFETY:
let _ = (42, unsafe {}, "test", unsafe {});
}
fn comment_unary() {
// Safety:
// SAFETY:
let _ = *unsafe { &42 };
}
#[allow(clippy::match_single_binding)]
fn comment_match() {
// Safety:
// SAFETY:
let _ = match unsafe {} {
_ => {},
};
@ -177,7 +177,7 @@ fn comment_macro_call() {
}
t!(
// Safety:
// SAFETY:
unsafe {}
);
}
@ -194,18 +194,18 @@ fn comment_macro_def() {
}
fn non_ascii_comment() {
// ॐ᧻໒ Safety: ௵∰
// ॐ᧻໒ SaFeTy: ௵∰
unsafe {};
}
fn local_commented_block() {
let _ =
// Safety:
// safety:
unsafe {};
}
fn local_nest() {
// Safety:
// safety:
let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})];
}
@ -267,17 +267,17 @@ fn no_comment_macro_def() {
}
fn trailing_comment() {
unsafe {} // Safety:
unsafe {} // SAFETY:
}
fn internal_comment() {
unsafe {
// Safety:
// SAFETY:
}
}
fn interference() {
// Safety
// SAFETY
let _ = 42;

View file

@ -7,7 +7,7 @@ LL | unsafe {}
= note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
help: consider adding a safety comment
|
LL ~ // Safety: ...
LL ~ // SAFETY: ...
LL + unsafe {}
|
@ -19,7 +19,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
|
help: consider adding a safety comment
|
LL ~ // Safety: ...
LL ~ // SAFETY: ...
LL + let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
|
@ -31,7 +31,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {});
|
help: consider adding a safety comment
|
LL ~ // Safety: ...
LL ~ // SAFETY: ...
LL + let _ = (42, unsafe {}, "test", unsafe {});
|
@ -43,7 +43,7 @@ LL | let _ = *unsafe { &42 };
|
help: consider adding a safety comment
|
LL ~ // Safety: ...
LL ~ // SAFETY: ...
LL + let _ = *unsafe { &42 };
|
@ -55,7 +55,7 @@ LL | let _ = match unsafe {} {
|
help: consider adding a safety comment
|
LL ~ // Safety: ...
LL ~ // SAFETY: ...
LL + let _ = match unsafe {} {
|
@ -67,7 +67,7 @@ LL | let _ = &unsafe {};
|
help: consider adding a safety comment
|
LL ~ // Safety: ...
LL ~ // SAFETY: ...
LL + let _ = &unsafe {};
|
@ -79,7 +79,7 @@ LL | let _ = [unsafe {}; 5];
|
help: consider adding a safety comment
|
LL ~ // Safety: ...
LL ~ // SAFETY: ...
LL + let _ = [unsafe {}; 5];
|
@ -91,7 +91,7 @@ LL | let _ = unsafe {};
|
help: consider adding a safety comment
|
LL ~ // Safety: ...
LL ~ // SAFETY: ...
LL + let _ = unsafe {};
|
@ -103,7 +103,7 @@ LL | t!(unsafe {});
|
help: consider adding a safety comment
|
LL ~ t!(// Safety: ...
LL ~ t!(// SAFETY: ...
LL ~ unsafe {});
|
@ -122,13 +122,13 @@ LL | t!();
error: unsafe block missing a safety comment
--> $DIR/undocumented_unsafe_blocks.rs:270:5
|
LL | unsafe {} // Safety:
LL | unsafe {} // SAFETY:
| ^^^^^^^^^
|
help: consider adding a safety comment
|
LL ~ // Safety: ...
LL ~ unsafe {} // Safety:
LL ~ // SAFETY: ...
LL ~ unsafe {} // SAFETY:
|
error: unsafe block missing a safety comment
@ -139,7 +139,7 @@ LL | unsafe {
|
help: consider adding a safety comment
|
LL ~ // Safety: ...
LL ~ // SAFETY: ...
LL + unsafe {
|
@ -151,7 +151,7 @@ LL | unsafe {};
|
help: consider adding a safety comment
|
LL ~ // Safety: ...
LL ~ // SAFETY: ...
LL ~ unsafe {};
|
@ -163,7 +163,7 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
|
help: consider adding a safety comment
|
LL ~ println!("{}", // Safety: ...
LL ~ println!("{}", // SAFETY: ...
LL ~ unsafe { String::from_utf8_unchecked(vec![]) });
|

View file

@ -45,7 +45,7 @@ fn unwrap_or_else_default() {
with_enum.unwrap_or_else(Enum::A);
let with_new = Some(vec![1]);
with_new.unwrap_or_else(Vec::new);
with_new.unwrap_or_default();
let with_err: Result<_, ()> = Ok(vec![1]);
with_err.unwrap_or_else(make);
@ -66,6 +66,9 @@ fn unwrap_or_else_default() {
let with_default_type = Some(1);
with_default_type.unwrap_or_default();
let with_default_type: Option<Vec<u64>> = None;
with_default_type.unwrap_or_default();
}
fn main() {}

View file

@ -66,6 +66,9 @@ fn unwrap_or_else_default() {
let with_default_type = Some(1);
with_default_type.unwrap_or_else(u64::default);
let with_default_type: Option<Vec<u64>> = None;
with_default_type.unwrap_or_else(Vec::new);
}
fn main() {}

View file

@ -1,10 +1,16 @@
error: use of `.unwrap_or_else(..)` to construct default value
--> $DIR/unwrap_or_else_default.rs:48:5
|
LL | with_new.unwrap_or_else(Vec::new);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_new.unwrap_or_default()`
|
= note: `-D clippy::unwrap-or-else-default` implied by `-D warnings`
error: use of `.unwrap_or_else(..)` to construct default value
--> $DIR/unwrap_or_else_default.rs:62:5
|
LL | with_real_default.unwrap_or_else(<HasDefaultAndDuplicate as Default>::default);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_real_default.unwrap_or_default()`
|
= note: `-D clippy::unwrap-or-else-default` implied by `-D warnings`
error: use of `.unwrap_or_else(..)` to construct default value
--> $DIR/unwrap_or_else_default.rs:65:5
@ -18,5 +24,11 @@ error: use of `.unwrap_or_else(..)` to construct default value
LL | with_default_type.unwrap_or_else(u64::default);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_default_type.unwrap_or_default()`
error: aborting due to 3 previous errors
error: use of `.unwrap_or_else(..)` to construct default value
--> $DIR/unwrap_or_else_default.rs:71:5
|
LL | with_default_type.unwrap_or_else(Vec::new);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `with_default_type.unwrap_or_default()`
error: aborting due to 5 previous errors