mirror of
https://github.com/rust-lang/rust.git
synced 2024-10-31 14:31:55 +00:00
Merge commit '7c21f91b15b7604f818565646b686d90f99d1baf' into clippyup
This commit is contained in:
parent
82f469f81b
commit
7cd86aa1be
2
.github/ISSUE_TEMPLATE/blank_issue.yml
vendored
2
.github/ISSUE_TEMPLATE/blank_issue.yml
vendored
@ -9,7 +9,7 @@ body:
|
||||
attributes:
|
||||
label: Description
|
||||
description: >
|
||||
Please provide a discription of the issue, along with any information
|
||||
Please provide a description of the issue, along with any information
|
||||
you feel relevant to replicate it.
|
||||
validations:
|
||||
required: true
|
||||
|
2
.github/ISSUE_TEMPLATE/false_negative.yml
vendored
2
.github/ISSUE_TEMPLATE/false_negative.yml
vendored
@ -23,7 +23,7 @@ body:
|
||||
id: reproducer
|
||||
attributes:
|
||||
label: Reproducer
|
||||
description: Please provide the code and steps to repoduce the bug
|
||||
description: Please provide the code and steps to reproduce the bug
|
||||
value: |
|
||||
I tried this code:
|
||||
|
||||
|
2
.github/ISSUE_TEMPLATE/false_positive.yml
vendored
2
.github/ISSUE_TEMPLATE/false_positive.yml
vendored
@ -24,7 +24,7 @@ body:
|
||||
attributes:
|
||||
label: Reproducer
|
||||
description: >
|
||||
Please provide the code and steps to repoduce the bug together with the
|
||||
Please provide the code and steps to reproduce the bug together with the
|
||||
output from Clippy.
|
||||
value: |
|
||||
I tried this code:
|
||||
|
6
.github/workflows/clippy.yml
vendored
6
.github/workflows/clippy.yml
vendored
@ -6,14 +6,14 @@ on:
|
||||
branches-ignore:
|
||||
- auto
|
||||
- try
|
||||
# Don't run Clippy tests, when only textfiles were modified
|
||||
# Don't run Clippy tests, when only text files were modified
|
||||
paths-ignore:
|
||||
- 'COPYRIGHT'
|
||||
- 'LICENSE-*'
|
||||
- '**.md'
|
||||
- '**.txt'
|
||||
pull_request:
|
||||
# Don't run Clippy tests, when only textfiles were modified
|
||||
# Don't run Clippy tests, when only text files were modified
|
||||
paths-ignore:
|
||||
- 'COPYRIGHT'
|
||||
- 'LICENSE-*'
|
||||
@ -37,7 +37,7 @@ jobs:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
uses: actions/checkout@v3.0.2
|
||||
|
||||
- name: Install toolchain
|
||||
run: rustup show active-toolchain
|
||||
|
8
.github/workflows/clippy_bors.yml
vendored
8
.github/workflows/clippy_bors.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
uses: actions/checkout@v3.0.2
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
@ -88,7 +88,7 @@ jobs:
|
||||
if: matrix.host == 'i686-unknown-linux-gnu'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
uses: actions/checkout@v3.0.2
|
||||
|
||||
- name: Install toolchain
|
||||
run: rustup show active-toolchain
|
||||
@ -154,7 +154,7 @@ jobs:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
uses: actions/checkout@v3.0.2
|
||||
|
||||
- name: Install toolchain
|
||||
run: rustup show active-toolchain
|
||||
@ -212,7 +212,7 @@ jobs:
|
||||
github_token: "${{ secrets.github_token }}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
uses: actions/checkout@v3.0.2
|
||||
|
||||
- name: Install toolchain
|
||||
run: rustup show active-toolchain
|
||||
|
2
.github/workflows/clippy_dev.yml
vendored
2
.github/workflows/clippy_dev.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
steps:
|
||||
# Setup
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
uses: actions/checkout@v3.0.2
|
||||
|
||||
# Run
|
||||
- name: Build
|
||||
|
4
.github/workflows/deploy.yml
vendored
4
.github/workflows/deploy.yml
vendored
@ -21,10 +21,10 @@ jobs:
|
||||
steps:
|
||||
# Setup
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
uses: actions/checkout@v3.0.2
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
uses: actions/checkout@v3.0.2
|
||||
with:
|
||||
ref: ${{ env.TARGET_BRANCH }}
|
||||
path: 'out'
|
||||
|
2
.github/workflows/remark.yml
vendored
2
.github/workflows/remark.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
steps:
|
||||
# Setup
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2.3.3
|
||||
uses: actions/checkout@v3.0.2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v1.4.4
|
||||
|
113
CHANGELOG.md
113
CHANGELOG.md
@ -6,7 +6,108 @@ document.
|
||||
|
||||
## Unreleased / In Rust Nightly
|
||||
|
||||
[57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master)
|
||||
[d0cf3481...master](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...master)
|
||||
|
||||
## Rust 1.61 (beta)
|
||||
|
||||
Current beta, released 2022-05-19
|
||||
|
||||
[57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481)
|
||||
|
||||
### New Lints
|
||||
|
||||
* [`only_used_in_recursion`]
|
||||
[#8422](https://github.com/rust-lang/rust-clippy/pull/8422)
|
||||
* [`cast_enum_truncation`]
|
||||
[#8381](https://github.com/rust-lang/rust-clippy/pull/8381)
|
||||
* [`missing_spin_loop`]
|
||||
[#8174](https://github.com/rust-lang/rust-clippy/pull/8174)
|
||||
* [`deref_by_slicing`]
|
||||
[#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
|
||||
* [`needless_match`]
|
||||
[#8471](https://github.com/rust-lang/rust-clippy/pull/8471)
|
||||
* [`allow_attributes_without_reason`] (Requires `#![feature(lint_reasons)]`)
|
||||
[#8504](https://github.com/rust-lang/rust-clippy/pull/8504)
|
||||
* [`print_in_format_impl`]
|
||||
[#8253](https://github.com/rust-lang/rust-clippy/pull/8253)
|
||||
* [`unnecessary_find_map`]
|
||||
[#8489](https://github.com/rust-lang/rust-clippy/pull/8489)
|
||||
* [`or_then_unwrap`]
|
||||
[#8561](https://github.com/rust-lang/rust-clippy/pull/8561)
|
||||
* [`unnecessary_join`]
|
||||
[#8579](https://github.com/rust-lang/rust-clippy/pull/8579)
|
||||
* [`iter_with_drain`]
|
||||
[#8483](https://github.com/rust-lang/rust-clippy/pull/8483)
|
||||
* [`cast_enum_constructor`]
|
||||
[#8562](https://github.com/rust-lang/rust-clippy/pull/8562)
|
||||
* [`cast_slice_different_sizes`]
|
||||
[#8445](https://github.com/rust-lang/rust-clippy/pull/8445)
|
||||
|
||||
### Moves and Deprecations
|
||||
|
||||
* Moved [`transmute_undefined_repr`] to `nursery` (now allow-by-default)
|
||||
[#8432](https://github.com/rust-lang/rust-clippy/pull/8432)
|
||||
* Moved [`try_err`] to `restriction`
|
||||
[#8544](https://github.com/rust-lang/rust-clippy/pull/8544)
|
||||
* Move [`iter_with_drain`] to `nursery`
|
||||
[#8541](https://github.com/rust-lang/rust-clippy/pull/8541)
|
||||
* Renamed `to_string_in_display` to [`recursive_format_impl`]
|
||||
[#8188](https://github.com/rust-lang/rust-clippy/pull/8188)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [`dbg_macro`]: The lint level can now be set with crate attributes and works inside macros
|
||||
[#8411](https://github.com/rust-lang/rust-clippy/pull/8411)
|
||||
* [`ptr_as_ptr`]: Now works inside macros
|
||||
[#8442](https://github.com/rust-lang/rust-clippy/pull/8442)
|
||||
* [`use_self`]: Now works for variants in match expressions
|
||||
[#8456](https://github.com/rust-lang/rust-clippy/pull/8456)
|
||||
* [`await_holding_lock`]: Now lints for `parking_lot::{Mutex, RwLock}`
|
||||
[#8419](https://github.com/rust-lang/rust-clippy/pull/8419)
|
||||
* [`recursive_format_impl`]: Now checks for format calls on `self`
|
||||
[#8188](https://github.com/rust-lang/rust-clippy/pull/8188)
|
||||
|
||||
### False Positive Fixes
|
||||
|
||||
* [`new_without_default`]: No longer lints for `new()` methods with `#[doc(hidden)]`
|
||||
[#8472](https://github.com/rust-lang/rust-clippy/pull/8472)
|
||||
* [`transmute_undefined_repr`]: No longer lints for single field structs with `#[repr(C)]`,
|
||||
generic parameters, wide pointers, unions, tuples and allow several forms of type erasure
|
||||
[#8425](https://github.com/rust-lang/rust-clippy/pull/8425)
|
||||
[#8553](https://github.com/rust-lang/rust-clippy/pull/8553)
|
||||
[#8440](https://github.com/rust-lang/rust-clippy/pull/8440)
|
||||
[#8547](https://github.com/rust-lang/rust-clippy/pull/8547)
|
||||
* [`match_single_binding`], [`match_same_arms`], [`match_as_ref`], [`match_bool`]: No longer
|
||||
lint `match` expressions with `cfg`ed arms
|
||||
[#8443](https://github.com/rust-lang/rust-clippy/pull/8443)
|
||||
* [`single_component_path_imports`]: No longer lint on macros
|
||||
[#8537](https://github.com/rust-lang/rust-clippy/pull/8537)
|
||||
* [`ptr_arg`]: Allow `&mut` arguments for `Cow<_>`
|
||||
[#8552](https://github.com/rust-lang/rust-clippy/pull/8552)
|
||||
* [`needless_borrow`]: No longer lints for method calls
|
||||
[#8441](https://github.com/rust-lang/rust-clippy/pull/8441)
|
||||
* [`match_same_arms`]: Now ensures that interposing arm patterns don't overlap
|
||||
[#8232](https://github.com/rust-lang/rust-clippy/pull/8232)
|
||||
* [`default_trait_access`]: Now allows `Default::default` in update expressions
|
||||
[#8433](https://github.com/rust-lang/rust-clippy/pull/8433)
|
||||
|
||||
### Suggestion Fixes/Improvements
|
||||
|
||||
* [`redundant_slicing`]: Fixed suggestion for a method calls
|
||||
[#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
|
||||
* [`map_flatten`]: Long suggestions will now be split up into two help messages
|
||||
[#8520](https://github.com/rust-lang/rust-clippy/pull/8520)
|
||||
* [`unnecessary_lazy_evaluations`]: Now shows suggestions for longer code snippets
|
||||
[#8543](https://github.com/rust-lang/rust-clippy/pull/8543)
|
||||
* [`unnecessary_sort_by`]: Now suggests `Reverse` including the path
|
||||
[#8462](https://github.com/rust-lang/rust-clippy/pull/8462)
|
||||
* [`search_is_some`]: More suggestions are now `MachineApplicable`
|
||||
[#8536](https://github.com/rust-lang/rust-clippy/pull/8536)
|
||||
|
||||
### Documentation Improvements
|
||||
|
||||
* [`new_without_default`]: Document `pub` requirement for the struct and fields
|
||||
[#8429](https://github.com/rust-lang/rust-clippy/pull/8429)
|
||||
|
||||
## Rust 1.60
|
||||
|
||||
@ -3182,6 +3283,7 @@ Released 2018-09-13
|
||||
[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
|
||||
[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
|
||||
[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
|
||||
[`await_holding_invalid_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type
|
||||
[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
|
||||
[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
|
||||
[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
|
||||
@ -3198,6 +3300,7 @@ Released 2018-09-13
|
||||
[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
|
||||
[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
|
||||
[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
|
||||
[`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len
|
||||
[`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
|
||||
[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
|
||||
[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
|
||||
@ -3262,6 +3365,7 @@ Released 2018-09-13
|
||||
[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
|
||||
[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
|
||||
[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
|
||||
[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop
|
||||
[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
|
||||
[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
|
||||
[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
|
||||
@ -3314,6 +3418,7 @@ Released 2018-09-13
|
||||
[`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop
|
||||
[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
|
||||
[`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args
|
||||
[`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string
|
||||
[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
|
||||
[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
|
||||
[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
|
||||
@ -3356,6 +3461,7 @@ Released 2018-09-13
|
||||
[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
|
||||
[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
|
||||
[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
|
||||
[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
|
||||
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
|
||||
[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
|
||||
[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
|
||||
@ -3372,6 +3478,7 @@ Released 2018-09-13
|
||||
[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
|
||||
[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
|
||||
[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
|
||||
[`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file
|
||||
[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
|
||||
[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
|
||||
[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
|
||||
@ -3472,6 +3579,7 @@ Released 2018-09-13
|
||||
[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
|
||||
[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
|
||||
[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
|
||||
[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
|
||||
[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
|
||||
[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
|
||||
[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
|
||||
@ -3527,6 +3635,7 @@ Released 2018-09-13
|
||||
[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
|
||||
[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
|
||||
[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
|
||||
[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
|
||||
[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
|
||||
[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
|
||||
[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
|
||||
@ -3627,6 +3736,7 @@ Released 2018-09-13
|
||||
[`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr
|
||||
[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
|
||||
[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
|
||||
[`trim_split_whitespace`]: https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace
|
||||
[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
|
||||
[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
|
||||
[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
|
||||
@ -3650,6 +3760,7 @@ Released 2018-09-13
|
||||
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
|
||||
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
|
||||
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
|
||||
[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
|
||||
[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
|
||||
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
|
||||
[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned
|
||||
|
@ -4,6 +4,7 @@ version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
aho-corasick = "0.7"
|
||||
clap = "2.33"
|
||||
indoc = "1.0"
|
||||
itertools = "0.10.1"
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![feature(let_chains)]
|
||||
#![feature(let_else)]
|
||||
#![feature(once_cell)]
|
||||
#![feature(rustc_private)]
|
||||
|
@ -19,9 +19,9 @@ fn main() {
|
||||
if matches.is_present("print-only") {
|
||||
update_lints::print_lints();
|
||||
} else if matches.is_present("check") {
|
||||
update_lints::run(update_lints::UpdateMode::Check);
|
||||
update_lints::update(update_lints::UpdateMode::Check);
|
||||
} else {
|
||||
update_lints::run(update_lints::UpdateMode::Change);
|
||||
update_lints::update(update_lints::UpdateMode::Change);
|
||||
}
|
||||
},
|
||||
("new_lint", Some(matches)) => {
|
||||
@ -31,18 +31,36 @@ fn main() {
|
||||
matches.value_of("category"),
|
||||
matches.is_present("msrv"),
|
||||
) {
|
||||
Ok(_) => update_lints::run(update_lints::UpdateMode::Change),
|
||||
Ok(_) => update_lints::update(update_lints::UpdateMode::Change),
|
||||
Err(e) => eprintln!("Unable to create lint: {}", e),
|
||||
}
|
||||
},
|
||||
("setup", Some(sub_command)) => match sub_command.subcommand() {
|
||||
("intellij", Some(matches)) => setup::intellij::setup_rustc_src(
|
||||
matches
|
||||
.value_of("rustc-repo-path")
|
||||
.expect("this field is mandatory and therefore always valid"),
|
||||
),
|
||||
("git-hook", Some(matches)) => setup::git_hook::install_hook(matches.is_present("force-override")),
|
||||
("vscode-tasks", Some(matches)) => setup::vscode::install_tasks(matches.is_present("force-override")),
|
||||
("intellij", Some(matches)) => {
|
||||
if matches.is_present("remove") {
|
||||
setup::intellij::remove_rustc_src();
|
||||
} else {
|
||||
setup::intellij::setup_rustc_src(
|
||||
matches
|
||||
.value_of("rustc-repo-path")
|
||||
.expect("this field is mandatory and therefore always valid"),
|
||||
);
|
||||
}
|
||||
},
|
||||
("git-hook", Some(matches)) => {
|
||||
if matches.is_present("remove") {
|
||||
setup::git_hook::remove_hook();
|
||||
} else {
|
||||
setup::git_hook::install_hook(matches.is_present("force-override"));
|
||||
}
|
||||
},
|
||||
("vscode-tasks", Some(matches)) => {
|
||||
if matches.is_present("remove") {
|
||||
setup::vscode::remove_tasks();
|
||||
} else {
|
||||
setup::vscode::install_tasks(matches.is_present("force-override"));
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
("remove", Some(sub_command)) => match sub_command.subcommand() {
|
||||
@ -60,6 +78,12 @@ fn main() {
|
||||
let path = matches.value_of("path").unwrap();
|
||||
lint::run(path);
|
||||
},
|
||||
("rename_lint", Some(matches)) => {
|
||||
let old_name = matches.value_of("old_name").unwrap();
|
||||
let new_name = matches.value_of("new_name").unwrap_or(old_name);
|
||||
let uplift = matches.is_present("uplift");
|
||||
update_lints::rename(old_name, new_name, uplift);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
@ -167,6 +191,12 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
|
||||
.subcommand(
|
||||
SubCommand::with_name("intellij")
|
||||
.about("Alter dependencies so Intellij Rust can find rustc internals")
|
||||
.arg(
|
||||
Arg::with_name("remove")
|
||||
.long("remove")
|
||||
.help("Remove the dependencies added with 'cargo dev setup intellij'")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("rustc-repo-path")
|
||||
.long("repo-path")
|
||||
@ -174,12 +204,19 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
|
||||
.help("The path to a rustc repo that will be used for setting the dependencies")
|
||||
.takes_value(true)
|
||||
.value_name("path")
|
||||
.conflicts_with("remove")
|
||||
.required(true),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("git-hook")
|
||||
.about("Add a pre-commit git hook that formats your code to make it look pretty")
|
||||
.arg(
|
||||
Arg::with_name("remove")
|
||||
.long("remove")
|
||||
.help("Remove the pre-commit hook added with 'cargo dev setup git-hook'")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("force-override")
|
||||
.long("force-override")
|
||||
@ -191,6 +228,12 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
|
||||
.subcommand(
|
||||
SubCommand::with_name("vscode-tasks")
|
||||
.about("Add several tasks to vscode for formatting, validation and testing")
|
||||
.arg(
|
||||
Arg::with_name("remove")
|
||||
.long("remove")
|
||||
.help("Remove the tasks added with 'cargo dev setup vscode-tasks'")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("force-override")
|
||||
.long("force-override")
|
||||
@ -242,5 +285,26 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
|
||||
.help("The path to a file or package directory to lint"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("rename_lint")
|
||||
.about("Renames the given lint")
|
||||
.arg(
|
||||
Arg::with_name("old_name")
|
||||
.index(1)
|
||||
.required(true)
|
||||
.help("The name of the lint to rename"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("new_name")
|
||||
.index(2)
|
||||
.required_unless("uplift")
|
||||
.help("The new name of the lint"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("uplift")
|
||||
.long("uplift")
|
||||
.help("This lint will be uplifted into rustc"),
|
||||
),
|
||||
)
|
||||
.get_matches()
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::clippy_project_root;
|
||||
use indoc::indoc;
|
||||
use std::fmt::Write as _;
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::prelude::*;
|
||||
use std::io::{self, ErrorKind};
|
||||
@ -232,7 +233,8 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
|
||||
)
|
||||
});
|
||||
|
||||
result.push_str(&format!(
|
||||
let _ = write!(
|
||||
result,
|
||||
indoc! {r#"
|
||||
declare_clippy_lint! {{
|
||||
/// ### What it does
|
||||
@ -256,7 +258,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
|
||||
version = version,
|
||||
name_upper = name_upper,
|
||||
category = category,
|
||||
));
|
||||
);
|
||||
|
||||
result.push_str(&if enable_msrv {
|
||||
format!(
|
||||
|
@ -7,7 +7,7 @@ use std::path::Path;
|
||||
const CLIPPY_DEV_DIR: &str = "clippy_dev";
|
||||
|
||||
/// This function verifies that the tool is being executed in the clippy directory.
|
||||
/// This is useful to ensure that setups only modify Clippys resources. The verification
|
||||
/// This is useful to ensure that setups only modify Clippy's resources. The verification
|
||||
/// is done by checking that `clippy_dev` is a sub directory of the current directory.
|
||||
///
|
||||
/// It will print an error message and return `false` if the directory could not be
|
||||
@ -17,7 +17,7 @@ fn verify_inside_clippy_dir() -> bool {
|
||||
if path.exists() && path.is_dir() {
|
||||
true
|
||||
} else {
|
||||
eprintln!("error: unable to verify that the working directory is clippys directory");
|
||||
eprintln!("error: unable to verify that the working directory is clippy's directory");
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
use core::fmt::Write;
|
||||
use aho_corasick::AhoCorasickBuilder;
|
||||
use core::fmt::Write as _;
|
||||
use itertools::Itertools;
|
||||
use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use walkdir::WalkDir;
|
||||
use std::io::{self, Read as _, Seek as _, Write as _};
|
||||
use std::path::{Path, PathBuf};
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
use crate::clippy_project_root;
|
||||
|
||||
@ -30,12 +32,19 @@ pub enum UpdateMode {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if a file path could not read from or then written to
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn run(update_mode: UpdateMode) {
|
||||
let (lints, deprecated_lints) = gather_all();
|
||||
pub fn update(update_mode: UpdateMode) {
|
||||
let (lints, deprecated_lints, renamed_lints) = gather_all();
|
||||
generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints);
|
||||
}
|
||||
|
||||
let internal_lints = Lint::internal_lints(&lints);
|
||||
let usable_lints = Lint::usable_lints(&lints);
|
||||
fn generate_lint_files(
|
||||
update_mode: UpdateMode,
|
||||
lints: &[Lint],
|
||||
deprecated_lints: &[DeprecatedLint],
|
||||
renamed_lints: &[RenamedLint],
|
||||
) {
|
||||
let internal_lints = Lint::internal_lints(lints);
|
||||
let usable_lints = Lint::usable_lints(lints);
|
||||
let mut sorted_usable_lints = usable_lints.clone();
|
||||
sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
|
||||
|
||||
@ -87,7 +96,7 @@ pub fn run(update_mode: UpdateMode) {
|
||||
process_file(
|
||||
"clippy_lints/src/lib.deprecated.rs",
|
||||
update_mode,
|
||||
&gen_deprecated(&deprecated_lints),
|
||||
&gen_deprecated(deprecated_lints),
|
||||
);
|
||||
|
||||
let all_group_lints = usable_lints.iter().filter(|l| {
|
||||
@ -107,10 +116,16 @@ pub fn run(update_mode: UpdateMode) {
|
||||
&content,
|
||||
);
|
||||
}
|
||||
|
||||
let content = gen_deprecated_lints_test(deprecated_lints);
|
||||
process_file("tests/ui/deprecated.rs", update_mode, &content);
|
||||
|
||||
let content = gen_renamed_lints_test(renamed_lints);
|
||||
process_file("tests/ui/rename.rs", update_mode, &content);
|
||||
}
|
||||
|
||||
pub fn print_lints() {
|
||||
let (lint_list, _) = gather_all();
|
||||
let (lint_list, _, _) = gather_all();
|
||||
let usable_lints = Lint::usable_lints(&lint_list);
|
||||
let usable_lint_count = usable_lints.len();
|
||||
let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
|
||||
@ -128,6 +143,209 @@ pub fn print_lints() {
|
||||
println!("there are {} lints", usable_lint_count);
|
||||
}
|
||||
|
||||
/// Runs the `rename_lint` command.
|
||||
///
|
||||
/// This does the following:
|
||||
/// * Adds an entry to `renamed_lints.rs`.
|
||||
/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`).
|
||||
/// * Renames the lint struct to the new name.
|
||||
/// * Renames the module containing the lint struct to the new name if it shares a name with the
|
||||
/// lint.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics for the following conditions:
|
||||
/// * If a file path could not read from or then written to
|
||||
/// * If either lint name has a prefix
|
||||
/// * If `old_name` doesn't name an existing lint.
|
||||
/// * If `old_name` names a deprecated or renamed lint.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
|
||||
if let Some((prefix, _)) = old_name.split_once("::") {
|
||||
panic!("`{}` should not contain the `{}` prefix", old_name, prefix);
|
||||
}
|
||||
if let Some((prefix, _)) = new_name.split_once("::") {
|
||||
panic!("`{}` should not contain the `{}` prefix", new_name, prefix);
|
||||
}
|
||||
|
||||
let (mut lints, deprecated_lints, mut renamed_lints) = gather_all();
|
||||
let mut old_lint_index = None;
|
||||
let mut found_new_name = false;
|
||||
for (i, lint) in lints.iter().enumerate() {
|
||||
if lint.name == old_name {
|
||||
old_lint_index = Some(i);
|
||||
} else if lint.name == new_name {
|
||||
found_new_name = true;
|
||||
}
|
||||
}
|
||||
let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{}`", old_name));
|
||||
|
||||
let lint = RenamedLint {
|
||||
old_name: format!("clippy::{}", old_name),
|
||||
new_name: if uplift {
|
||||
new_name.into()
|
||||
} else {
|
||||
format!("clippy::{}", new_name)
|
||||
},
|
||||
};
|
||||
|
||||
// Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in
|
||||
// case.
|
||||
assert!(
|
||||
!renamed_lints.iter().any(|l| lint.old_name == l.old_name),
|
||||
"`{}` has already been renamed",
|
||||
old_name
|
||||
);
|
||||
assert!(
|
||||
!deprecated_lints.iter().any(|l| lint.old_name == l.name),
|
||||
"`{}` has already been deprecated",
|
||||
old_name
|
||||
);
|
||||
|
||||
// Update all lint level attributes. (`clippy::lint_name`)
|
||||
for file in WalkDir::new(clippy_project_root())
|
||||
.into_iter()
|
||||
.map(Result::unwrap)
|
||||
.filter(|f| {
|
||||
let name = f.path().file_name();
|
||||
let ext = f.path().extension();
|
||||
(ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed")))
|
||||
&& name != Some(OsStr::new("rename.rs"))
|
||||
&& name != Some(OsStr::new("renamed_lints.rs"))
|
||||
})
|
||||
{
|
||||
rewrite_file(file.path(), |s| {
|
||||
replace_ident_like(s, &[(&lint.old_name, &lint.new_name)])
|
||||
});
|
||||
}
|
||||
|
||||
renamed_lints.push(lint);
|
||||
renamed_lints.sort_by(|lhs, rhs| {
|
||||
lhs.new_name
|
||||
.starts_with("clippy::")
|
||||
.cmp(&rhs.new_name.starts_with("clippy::"))
|
||||
.reverse()
|
||||
.then_with(|| lhs.old_name.cmp(&rhs.old_name))
|
||||
});
|
||||
|
||||
write_file(
|
||||
Path::new("clippy_lints/src/renamed_lints.rs"),
|
||||
&gen_renamed_lints_list(&renamed_lints),
|
||||
);
|
||||
|
||||
if uplift {
|
||||
write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
|
||||
println!(
|
||||
"`{}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually.",
|
||||
old_name
|
||||
);
|
||||
} else if found_new_name {
|
||||
write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
|
||||
println!(
|
||||
"`{}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually.",
|
||||
new_name
|
||||
);
|
||||
} else {
|
||||
// Rename the lint struct and source files sharing a name with the lint.
|
||||
let lint = &mut lints[old_lint_index];
|
||||
let old_name_upper = old_name.to_uppercase();
|
||||
let new_name_upper = new_name.to_uppercase();
|
||||
lint.name = new_name.into();
|
||||
|
||||
// Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist.
|
||||
if try_rename_file(
|
||||
Path::new(&format!("tests/ui/{}.rs", old_name)),
|
||||
Path::new(&format!("tests/ui/{}.rs", new_name)),
|
||||
) {
|
||||
try_rename_file(
|
||||
Path::new(&format!("tests/ui/{}.stderr", old_name)),
|
||||
Path::new(&format!("tests/ui/{}.stderr", new_name)),
|
||||
);
|
||||
try_rename_file(
|
||||
Path::new(&format!("tests/ui/{}.fixed", old_name)),
|
||||
Path::new(&format!("tests/ui/{}.fixed", new_name)),
|
||||
);
|
||||
}
|
||||
|
||||
// Try to rename the file containing the lint if the file name matches the lint's name.
|
||||
let replacements;
|
||||
let replacements = if lint.module == old_name
|
||||
&& try_rename_file(
|
||||
Path::new(&format!("clippy_lints/src/{}.rs", old_name)),
|
||||
Path::new(&format!("clippy_lints/src/{}.rs", new_name)),
|
||||
) {
|
||||
// Edit the module name in the lint list. Note there could be multiple lints.
|
||||
for lint in lints.iter_mut().filter(|l| l.module == old_name) {
|
||||
lint.module = new_name.into();
|
||||
}
|
||||
replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)];
|
||||
replacements.as_slice()
|
||||
} else if !lint.module.contains("::")
|
||||
// Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs`
|
||||
&& try_rename_file(
|
||||
Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, old_name)),
|
||||
Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, new_name)),
|
||||
)
|
||||
{
|
||||
// Edit the module name in the lint list. Note there could be multiple lints, or none.
|
||||
let renamed_mod = format!("{}::{}", lint.module, old_name);
|
||||
for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) {
|
||||
lint.module = format!("{}::{}", lint.module, new_name);
|
||||
}
|
||||
replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)];
|
||||
replacements.as_slice()
|
||||
} else {
|
||||
replacements = [(&*old_name_upper, &*new_name_upper), ("", "")];
|
||||
&replacements[0..1]
|
||||
};
|
||||
|
||||
// Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being
|
||||
// renamed.
|
||||
for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("renamed_lints.rs")) {
|
||||
rewrite_file(file.path(), |s| replace_ident_like(s, replacements));
|
||||
}
|
||||
|
||||
generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
|
||||
println!("{} has been successfully renamed", old_name);
|
||||
}
|
||||
|
||||
println!("note: `cargo uitest` still needs to be run to update the test results");
|
||||
}
|
||||
|
||||
/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there
|
||||
/// were no replacements.
|
||||
fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<String> {
|
||||
fn is_ident_char(c: u8) -> bool {
|
||||
matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_')
|
||||
}
|
||||
|
||||
let searcher = AhoCorasickBuilder::new()
|
||||
.dfa(true)
|
||||
.match_kind(aho_corasick::MatchKind::LeftmostLongest)
|
||||
.build_with_size::<u16, _, _>(replacements.iter().map(|&(x, _)| x.as_bytes()))
|
||||
.unwrap();
|
||||
|
||||
let mut result = String::with_capacity(contents.len() + 1024);
|
||||
let mut pos = 0;
|
||||
let mut edited = false;
|
||||
for m in searcher.find_iter(contents) {
|
||||
let (old, new) = replacements[m.pattern()];
|
||||
result.push_str(&contents[pos..m.start()]);
|
||||
result.push_str(
|
||||
if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0))
|
||||
&& !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0))
|
||||
{
|
||||
edited = true;
|
||||
new
|
||||
} else {
|
||||
old
|
||||
},
|
||||
);
|
||||
pos = m.end();
|
||||
}
|
||||
result.push_str(&contents[pos..]);
|
||||
edited.then(|| result)
|
||||
}
|
||||
|
||||
fn round_to_fifty(count: usize) -> usize {
|
||||
count / 50 * 50
|
||||
}
|
||||
@ -210,6 +428,19 @@ impl DeprecatedLint {
|
||||
}
|
||||
}
|
||||
|
||||
struct RenamedLint {
|
||||
old_name: String,
|
||||
new_name: String,
|
||||
}
|
||||
impl RenamedLint {
|
||||
fn new(old_name: &str, new_name: &str) -> Self {
|
||||
Self {
|
||||
old_name: remove_line_splices(old_name),
|
||||
new_name: remove_line_splices(new_name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the code for registering a group
|
||||
fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lint>) -> String {
|
||||
let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
|
||||
@ -217,12 +448,13 @@ fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lin
|
||||
|
||||
let mut output = GENERATED_FILE_COMMENT.to_string();
|
||||
|
||||
output.push_str(&format!(
|
||||
"store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![\n",
|
||||
let _ = writeln!(
|
||||
output,
|
||||
"store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![",
|
||||
group_name
|
||||
));
|
||||
);
|
||||
for (module, name) in details {
|
||||
output.push_str(&format!(" LintId::of({}::{}),\n", module, name));
|
||||
let _ = writeln!(output, " LintId::of({}::{}),", module, name);
|
||||
}
|
||||
output.push_str("])\n");
|
||||
|
||||
@ -235,7 +467,8 @@ fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
|
||||
let mut output = GENERATED_FILE_COMMENT.to_string();
|
||||
output.push_str("{\n");
|
||||
for lint in lints {
|
||||
output.push_str(&format!(
|
||||
let _ = write!(
|
||||
output,
|
||||
concat!(
|
||||
" store.register_removed(\n",
|
||||
" \"clippy::{}\",\n",
|
||||
@ -243,7 +476,7 @@ fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
|
||||
" );\n"
|
||||
),
|
||||
lint.name, lint.reason,
|
||||
));
|
||||
);
|
||||
}
|
||||
output.push_str("}\n");
|
||||
|
||||
@ -269,25 +502,62 @@ fn gen_register_lint_list<'a>(
|
||||
if !is_public {
|
||||
output.push_str(" #[cfg(feature = \"internal\")]\n");
|
||||
}
|
||||
output.push_str(&format!(" {}::{},\n", module_name, lint_name));
|
||||
let _ = writeln!(output, " {}::{},", module_name, lint_name);
|
||||
}
|
||||
output.push_str("])\n");
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String {
|
||||
let mut res: String = GENERATED_FILE_COMMENT.into();
|
||||
for lint in lints {
|
||||
writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap();
|
||||
}
|
||||
res.push_str("\nfn main() {}\n");
|
||||
res
|
||||
}
|
||||
|
||||
fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String {
|
||||
let mut seen_lints = HashSet::new();
|
||||
let mut res: String = GENERATED_FILE_COMMENT.into();
|
||||
res.push_str("// run-rustfix\n\n");
|
||||
for lint in lints {
|
||||
if seen_lints.insert(&lint.new_name) {
|
||||
writeln!(res, "#![allow({})]", lint.new_name).unwrap();
|
||||
}
|
||||
}
|
||||
seen_lints.clear();
|
||||
for lint in lints {
|
||||
if seen_lints.insert(&lint.old_name) {
|
||||
writeln!(res, "#![warn({})]", lint.old_name).unwrap();
|
||||
}
|
||||
}
|
||||
res.push_str("\nfn main() {}\n");
|
||||
res
|
||||
}
|
||||
|
||||
fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String {
|
||||
const HEADER: &str = "\
|
||||
// This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\
|
||||
#[rustfmt::skip]\n\
|
||||
pub static RENAMED_LINTS: &[(&str, &str)] = &[\n";
|
||||
|
||||
let mut res = String::from(HEADER);
|
||||
for lint in lints {
|
||||
writeln!(res, " (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap();
|
||||
}
|
||||
res.push_str("];\n");
|
||||
res
|
||||
}
|
||||
|
||||
/// Gathers all lints defined in `clippy_lints/src`
|
||||
fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>) {
|
||||
fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) {
|
||||
let mut lints = Vec::with_capacity(1000);
|
||||
let mut deprecated_lints = Vec::with_capacity(50);
|
||||
let root_path = clippy_project_root().join("clippy_lints/src");
|
||||
let mut renamed_lints = Vec::with_capacity(50);
|
||||
|
||||
for (rel_path, file) in WalkDir::new(&root_path)
|
||||
.into_iter()
|
||||
.map(Result::unwrap)
|
||||
.filter(|f| f.path().extension() == Some(OsStr::new("rs")))
|
||||
.map(|f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f))
|
||||
{
|
||||
for (rel_path, file) in clippy_lints_src_files() {
|
||||
let path = file.path();
|
||||
let contents =
|
||||
fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e));
|
||||
@ -305,13 +575,21 @@ fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>) {
|
||||
module.strip_suffix(".rs").unwrap_or(&module)
|
||||
};
|
||||
|
||||
if module == "deprecated_lints" {
|
||||
parse_deprecated_contents(&contents, &mut deprecated_lints);
|
||||
} else {
|
||||
parse_contents(&contents, module, &mut lints);
|
||||
match module {
|
||||
"deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints),
|
||||
"renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints),
|
||||
_ => parse_contents(&contents, module, &mut lints),
|
||||
}
|
||||
}
|
||||
(lints, deprecated_lints)
|
||||
(lints, deprecated_lints, renamed_lints)
|
||||
}
|
||||
|
||||
fn clippy_lints_src_files() -> impl Iterator<Item = (PathBuf, DirEntry)> {
|
||||
let root_path = clippy_project_root().join("clippy_lints/src");
|
||||
let iter = WalkDir::new(&root_path).into_iter();
|
||||
iter.map(Result::unwrap)
|
||||
.filter(|f| f.path().extension() == Some(OsStr::new("rs")))
|
||||
.map(move |f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f))
|
||||
}
|
||||
|
||||
macro_rules! match_tokens {
|
||||
@ -394,6 +672,25 @@ fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) {
|
||||
for line in contents.lines() {
|
||||
let mut offset = 0usize;
|
||||
let mut iter = tokenize(line).map(|t| {
|
||||
let range = offset..offset + t.len;
|
||||
offset = range.end;
|
||||
(t.kind, &line[range])
|
||||
});
|
||||
let (old_name, new_name) = match_tokens!(
|
||||
iter,
|
||||
// ("old_name",
|
||||
Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma
|
||||
// "new_name"),
|
||||
Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma
|
||||
);
|
||||
lints.push(RenamedLint::new(old_name, new_name));
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the line splices and surrounding quotes from a string literal
|
||||
fn remove_line_splices(s: &str) -> String {
|
||||
let s = s
|
||||
@ -462,6 +759,52 @@ fn replace_region_in_text<'a>(
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
|
||||
match fs::OpenOptions::new().create_new(true).write(true).open(new_name) {
|
||||
Ok(file) => drop(file),
|
||||
Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false,
|
||||
Err(e) => panic_file(e, new_name, "create"),
|
||||
};
|
||||
match fs::rename(old_name, new_name) {
|
||||
Ok(()) => true,
|
||||
Err(e) => {
|
||||
drop(fs::remove_file(new_name));
|
||||
if e.kind() == io::ErrorKind::NotFound {
|
||||
false
|
||||
} else {
|
||||
panic_file(e, old_name, "rename");
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
fn panic_file(error: io::Error, name: &Path, action: &str) -> ! {
|
||||
panic!("failed to {} file `{}`: {}", action, name.display(), error)
|
||||
}
|
||||
|
||||
fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) {
|
||||
let mut file = fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.read(true)
|
||||
.open(path)
|
||||
.unwrap_or_else(|e| panic_file(e, path, "open"));
|
||||
let mut buf = String::new();
|
||||
file.read_to_string(&mut buf)
|
||||
.unwrap_or_else(|e| panic_file(e, path, "read"));
|
||||
if let Some(new_contents) = f(&buf) {
|
||||
file.rewind().unwrap_or_else(|e| panic_file(e, path, "write"));
|
||||
file.write_all(new_contents.as_bytes())
|
||||
.unwrap_or_else(|e| panic_file(e, path, "write"));
|
||||
file.set_len(new_contents.len() as u64)
|
||||
.unwrap_or_else(|e| panic_file(e, path, "write"));
|
||||
}
|
||||
}
|
||||
|
||||
fn write_file(path: &Path, contents: &str) {
|
||||
fs::write(path, contents).unwrap_or_else(|e| panic_file(e, path, "write"));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -6,7 +6,7 @@ use clippy_utils::msrvs;
|
||||
use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
|
||||
use clippy_utils::{extract_msrv_attr, meets_msrv};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
|
||||
use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MacArgs, MacArgsEq, MetaItemKind, NestedMetaItem};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{
|
||||
Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
|
||||
@ -335,9 +335,6 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
|
||||
}
|
||||
if let Some(lint_list) = &attr.meta_item_list() {
|
||||
if attr.ident().map_or(false, |ident| is_lint_level(ident.name)) {
|
||||
// permit `unused_imports`, `deprecated`, `unreachable_pub`,
|
||||
// `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items
|
||||
// and `unused_imports` for `extern crate` items with `macro_use`
|
||||
for lint in lint_list {
|
||||
match item.kind {
|
||||
ItemKind::Use(..) => {
|
||||
@ -345,10 +342,12 @@ 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| {
|
||||
matches!(
|
||||
s.as_str(),
|
||||
"wildcard_imports" | "enum_glob_use" | "redundant_pub_crate",
|
||||
)
|
||||
})
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -594,6 +593,10 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It
|
||||
};
|
||||
|
||||
if attr.style == AttrStyle::Outer {
|
||||
if let MacArgs::Eq(_, MacArgsEq::Ast(expr)) = &attr_item.args
|
||||
&& !matches!(expr.kind, rustc_ast::ExprKind::Lit(..)) {
|
||||
return;
|
||||
}
|
||||
if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) {
|
||||
return;
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{match_def_path, paths};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
|
||||
use rustc_hir::{def::Res, AsyncGeneratorKind, Body, BodyId, GeneratorKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::GeneratorInteriorTypeCause;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::utils::conf::DisallowedType;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for calls to await while holding a non-async-aware MutexGuard.
|
||||
@ -127,9 +130,75 @@ declare_clippy_lint! {
|
||||
"inside an async function, holding a `RefCell` ref while calling `await`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]);
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Allows users to configure types which should not be held across `await`
|
||||
/// suspension points.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// There are some types which are perfectly "safe" to be used concurrently
|
||||
/// from a memory access perspective but will cause bugs at runtime if they
|
||||
/// are held in such a way.
|
||||
///
|
||||
/// ### Known problems
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```toml
|
||||
/// await-holding-invalid-types = [
|
||||
/// # You can specify a type name
|
||||
/// "CustomLockType",
|
||||
/// # You can (optionally) specify a reason
|
||||
/// { path = "OtherCustomLockType", reason = "Relies on a thread local" }
|
||||
/// ]
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// # async fn baz() {}
|
||||
/// struct CustomLockType;
|
||||
/// struct OtherCustomLockType;
|
||||
/// async fn foo() {
|
||||
/// let _x = CustomLockType;
|
||||
/// let _y = OtherCustomLockType;
|
||||
/// baz().await; // Lint violation
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub AWAIT_HOLDING_INVALID_TYPE,
|
||||
suspicious,
|
||||
"holding a type across an await point which is not allowed to be held as per the configuration"
|
||||
}
|
||||
|
||||
impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AwaitHolding {
|
||||
conf_invalid_types: Vec<DisallowedType>,
|
||||
def_ids: FxHashMap<DefId, DisallowedType>,
|
||||
}
|
||||
|
||||
impl AwaitHolding {
|
||||
pub(crate) fn new(conf_invalid_types: Vec<DisallowedType>) -> Self {
|
||||
Self {
|
||||
conf_invalid_types,
|
||||
def_ids: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for AwaitHolding {
|
||||
fn check_crate(&mut self, cx: &LateContext<'_>) {
|
||||
for conf in &self.conf_invalid_types {
|
||||
let path = match conf {
|
||||
DisallowedType::Simple(path) | DisallowedType::WithReason { path, .. } => path,
|
||||
};
|
||||
let segs: Vec<_> = path.split("::").collect();
|
||||
if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) {
|
||||
self.def_ids.insert(id, conf.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
|
||||
use AsyncGeneratorKind::{Block, Closure, Fn};
|
||||
if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind {
|
||||
@ -137,7 +206,7 @@ impl LateLintPass<'_> for AwaitHolding {
|
||||
hir_id: body.value.hir_id,
|
||||
};
|
||||
let typeck_results = cx.tcx.typeck_body(body_id);
|
||||
check_interior_types(
|
||||
self.check_interior_types(
|
||||
cx,
|
||||
typeck_results.generator_interior_types.as_ref().skip_binder(),
|
||||
body.value.span,
|
||||
@ -146,46 +215,68 @@ impl LateLintPass<'_> for AwaitHolding {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
|
||||
for ty_cause in ty_causes {
|
||||
if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
|
||||
if is_mutex_guard(cx, adt.did()) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
AWAIT_HOLDING_LOCK,
|
||||
ty_cause.span,
|
||||
"this `MutexGuard` is held across an `await` point",
|
||||
|diag| {
|
||||
diag.help(
|
||||
"consider using an async-aware `Mutex` type or ensuring the \
|
||||
impl AwaitHolding {
|
||||
fn check_interior_types(&self, cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
|
||||
for ty_cause in ty_causes {
|
||||
if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
|
||||
if is_mutex_guard(cx, adt.did()) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
AWAIT_HOLDING_LOCK,
|
||||
ty_cause.span,
|
||||
"this `MutexGuard` is held across an `await` point",
|
||||
|diag| {
|
||||
diag.help(
|
||||
"consider using an async-aware `Mutex` type or ensuring the \
|
||||
`MutexGuard` is dropped before calling await",
|
||||
);
|
||||
diag.span_note(
|
||||
ty_cause.scope_span.unwrap_or(span),
|
||||
"these are all the `await` points this lock is held through",
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
if is_refcell_ref(cx, adt.did()) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
AWAIT_HOLDING_REFCELL_REF,
|
||||
ty_cause.span,
|
||||
"this `RefCell` reference is held across an `await` point",
|
||||
|diag| {
|
||||
diag.help("ensure the reference is dropped before calling `await`");
|
||||
diag.span_note(
|
||||
ty_cause.scope_span.unwrap_or(span),
|
||||
"these are all the `await` points this reference is held through",
|
||||
);
|
||||
},
|
||||
);
|
||||
);
|
||||
diag.span_note(
|
||||
ty_cause.scope_span.unwrap_or(span),
|
||||
"these are all the `await` points this lock is held through",
|
||||
);
|
||||
},
|
||||
);
|
||||
} else if is_refcell_ref(cx, adt.did()) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
AWAIT_HOLDING_REFCELL_REF,
|
||||
ty_cause.span,
|
||||
"this `RefCell` reference is held across an `await` point",
|
||||
|diag| {
|
||||
diag.help("ensure the reference is dropped before calling `await`");
|
||||
diag.span_note(
|
||||
ty_cause.scope_span.unwrap_or(span),
|
||||
"these are all the `await` points this reference is held through",
|
||||
);
|
||||
},
|
||||
);
|
||||
} else if let Some(disallowed) = self.def_ids.get(&adt.did()) {
|
||||
emit_invalid_type(cx, ty_cause.span, disallowed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedType) {
|
||||
let (type_name, reason) = match disallowed {
|
||||
DisallowedType::Simple(path) => (path, &None),
|
||||
DisallowedType::WithReason { path, reason } => (path, reason),
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
AWAIT_HOLDING_INVALID_TYPE,
|
||||
span,
|
||||
&format!("`{type_name}` may not be held across an `await` point per `clippy.toml`",),
|
||||
|diag| {
|
||||
if let Some(reason) = reason {
|
||||
diag.note(reason.clone());
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
|
||||
match_def_path(cx, def_id, &paths::MUTEX_GUARD)
|
||||
|| match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD)
|
||||
|
70
clippy_lints/src/bytes_count_to_len.rs
Normal file
70
clippy_lints/src/bytes_count_to_len.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// It checks for `str::bytes().count()` and suggests replacing it with
|
||||
/// `str::len()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `str::bytes().count()` is longer and may not be as performant as using
|
||||
/// `str::len()`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// "hello".bytes().count();
|
||||
/// String::from("hello").bytes().count();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// "hello".len();
|
||||
/// String::from("hello").len();
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub BYTES_COUNT_TO_LEN,
|
||||
complexity,
|
||||
"Using `bytes().count()` when `len()` performs the same functionality"
|
||||
}
|
||||
|
||||
declare_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BytesCountToLen {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind;
|
||||
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if match_def_path(cx, expr_def_id, &paths::ITER_COUNT);
|
||||
|
||||
if let [bytes_expr] = &**expr_args;
|
||||
if let hir::ExprKind::MethodCall(_, bytes_args, _) = &bytes_expr.kind;
|
||||
if let Some(bytes_def_id) = cx.typeck_results().type_dependent_def_id(bytes_expr.hir_id);
|
||||
if match_def_path(cx, bytes_def_id, &paths::STR_BYTES);
|
||||
|
||||
if let [str_expr] = &**bytes_args;
|
||||
let ty = cx.typeck_results().expr_ty(str_expr).peel_refs();
|
||||
|
||||
if is_type_diagnostic_item(cx, ty, sym::String) || ty.kind() == &ty::Str;
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BYTES_COUNT_TO_LEN,
|
||||
expr.span,
|
||||
"using long and hard to read `.bytes().count()`",
|
||||
"consider calling `.len()` instead",
|
||||
format!("{}.len()", snippet_with_applicability(cx, str_expr.span, "..", &mut applicability)),
|
||||
applicability
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -42,8 +42,8 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind;
|
||||
if (2..=6).contains(&ext_literal.as_str().len());
|
||||
if ext_literal.as_str().starts_with('.');
|
||||
if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_digit(10))
|
||||
|| ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_digit(10));
|
||||
if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
|
||||
|| ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
|
||||
then {
|
||||
let mut ty = ctx.typeck_results().expr_ty(obj);
|
||||
ty = match ty.kind() {
|
||||
|
@ -29,21 +29,19 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
|
||||
ExprKind::Block(block, _) => block.expr.map_or(nbits, |e| apply_reductions(cx, nbits, e, signed)),
|
||||
ExprKind::Binary(op, left, right) => match op.node {
|
||||
BinOpKind::Div => {
|
||||
apply_reductions(cx, nbits, left, signed)
|
||||
- (if signed {
|
||||
0 // let's be conservative here
|
||||
} else {
|
||||
// by dividing by 1, we remove 0 bits, etc.
|
||||
get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
|
||||
})
|
||||
apply_reductions(cx, nbits, left, signed).saturating_sub(if signed {
|
||||
// let's be conservative here
|
||||
0
|
||||
} else {
|
||||
// by dividing by 1, we remove 0 bits, etc.
|
||||
get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
|
||||
})
|
||||
},
|
||||
BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right)
|
||||
.unwrap_or(u64::max_value())
|
||||
.min(apply_reductions(cx, nbits, left, signed)),
|
||||
BinOpKind::Shr => {
|
||||
apply_reductions(cx, nbits, left, signed)
|
||||
- constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))
|
||||
},
|
||||
BinOpKind::Shr => apply_reductions(cx, nbits, left, signed)
|
||||
.saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))),
|
||||
_ => nbits,
|
||||
},
|
||||
ExprKind::MethodCall(method, [left, right], _) => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source::snippet_opt};
|
||||
use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_hir::{Expr, ExprKind, Node};
|
||||
@ -8,6 +8,63 @@ use rustc_semver::RustcVersion;
|
||||
|
||||
use super::CAST_SLICE_DIFFERENT_SIZES;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Option<RustcVersion>) {
|
||||
// suggestion is invalid if `ptr::slice_from_raw_parts` does not exist
|
||||
if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if this cast is the child of another cast expression then don't emit something for it, the full
|
||||
// chain will be analyzed
|
||||
if is_child_of_cast(cx, expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(CastChainInfo {
|
||||
left_cast,
|
||||
start_ty,
|
||||
end_ty,
|
||||
}) = expr_cast_chain_tys(cx, expr)
|
||||
{
|
||||
if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) {
|
||||
let from_size = from_layout.size.bytes();
|
||||
let to_size = to_layout.size.bytes();
|
||||
if from_size != to_size && from_size != 0 && to_size != 0 {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
CAST_SLICE_DIFFERENT_SIZES,
|
||||
expr.span,
|
||||
&format!(
|
||||
"casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count",
|
||||
start_ty.ty, from_size, end_ty.ty, to_size,
|
||||
),
|
||||
|diag| {
|
||||
let ptr_snippet = source::snippet(cx, left_cast.span, "..");
|
||||
|
||||
let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl {
|
||||
Mutability::Mut => ("_mut", "mut"),
|
||||
Mutability::Not => ("", "const"),
|
||||
};
|
||||
let sugg = format!(
|
||||
"core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)",
|
||||
// get just the ty from the TypeAndMut so that the printed type isn't something like `mut
|
||||
// T`, extract just the `T`
|
||||
end_ty.ty
|
||||
);
|
||||
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
&format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
|
||||
sugg,
|
||||
rustc_errors::Applicability::HasPlaceholders,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let map = cx.tcx.hir();
|
||||
if_chain! {
|
||||
@ -33,59 +90,6 @@ fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVersion>) {
|
||||
// suggestion is invalid if `ptr::slice_from_raw_parts` does not exist
|
||||
if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if this cast is the child of another cast expression then don't emit something for it, the full
|
||||
// chain will be analyzed
|
||||
if is_child_of_cast(cx, expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some((from_slice_ty, to_slice_ty)) = expr_cast_chain_tys(cx, expr) {
|
||||
if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(from_slice_ty.ty), cx.layout_of(to_slice_ty.ty)) {
|
||||
let from_size = from_layout.size.bytes();
|
||||
let to_size = to_layout.size.bytes();
|
||||
if from_size != to_size && from_size != 0 && to_size != 0 {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
CAST_SLICE_DIFFERENT_SIZES,
|
||||
expr.span,
|
||||
&format!(
|
||||
"casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count",
|
||||
from_slice_ty, from_size, to_slice_ty, to_size,
|
||||
),
|
||||
|diag| {
|
||||
let cast_expr = match expr.kind {
|
||||
ExprKind::Cast(cast_expr, ..) => cast_expr,
|
||||
_ => unreachable!("expr should be a cast as checked by expr_cast_chain_tys"),
|
||||
};
|
||||
let ptr_snippet = snippet_opt(cx, cast_expr.span).unwrap();
|
||||
|
||||
let (mutbl_fn_str, mutbl_ptr_str) = match to_slice_ty.mutbl {
|
||||
Mutability::Mut => ("_mut", "mut"),
|
||||
Mutability::Not => ("", "const"),
|
||||
};
|
||||
let sugg = format!(
|
||||
"core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {to_slice_ty}, ..)"
|
||||
);
|
||||
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
&format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
|
||||
sugg,
|
||||
rustc_errors::Applicability::HasPlaceholders,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if
|
||||
/// the type is one of those slices
|
||||
fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option<TypeAndMut<'_>> {
|
||||
@ -98,18 +102,40 @@ fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option<TypeAndMut<'_>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the pair (original ptr T, final ptr U) if the expression is composed of casts
|
||||
struct CastChainInfo<'tcx> {
|
||||
/// The left most part of the cast chain, or in other words, the first cast in the chain
|
||||
/// Used for diagnostics
|
||||
left_cast: &'tcx Expr<'tcx>,
|
||||
/// The starting type of the cast chain
|
||||
start_ty: TypeAndMut<'tcx>,
|
||||
/// The final type of the cast chain
|
||||
end_ty: TypeAndMut<'tcx>,
|
||||
}
|
||||
|
||||
/// Returns a `CastChainInfo` with the left-most cast in the chain and the original ptr T and final
|
||||
/// ptr U if the expression is composed of casts.
|
||||
/// Returns None if the expr is not a Cast
|
||||
fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<(TypeAndMut<'tcx>, TypeAndMut<'tcx>)> {
|
||||
fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<CastChainInfo<'tcx>> {
|
||||
if let ExprKind::Cast(cast_expr, _cast_to_hir_ty) = expr.peel_blocks().kind {
|
||||
let cast_to = cx.typeck_results().expr_ty(expr);
|
||||
let to_slice_ty = get_raw_slice_ty_mut(cast_to)?;
|
||||
if let Some((inner_from_ty, _inner_to_ty)) = expr_cast_chain_tys(cx, cast_expr) {
|
||||
Some((inner_from_ty, to_slice_ty))
|
||||
|
||||
// If the expression that makes up the source of this cast is itself a cast, recursively
|
||||
// call `expr_cast_chain_tys` and update the end type with the final tartet type.
|
||||
// Otherwise, this cast is not immediately nested, just construct the info for this cast
|
||||
if let Some(prev_info) = expr_cast_chain_tys(cx, cast_expr) {
|
||||
Some(CastChainInfo {
|
||||
end_ty: to_slice_ty,
|
||||
..prev_info
|
||||
})
|
||||
} else {
|
||||
let cast_from = cx.typeck_results().expr_ty(cast_expr);
|
||||
let from_slice_ty = get_raw_slice_ty_mut(cast_from)?;
|
||||
Some((from_slice_ty, to_slice_ty))
|
||||
Some(CastChainInfo {
|
||||
left_cast: cast_expr,
|
||||
start_ty: from_slice_ty,
|
||||
end_ty: to_slice_ty,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
None
|
||||
|
@ -270,7 +270,7 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Casting a function pointer to an integer can have surprising results and can occur
|
||||
/// accidentally if parantheses are omitted from a function call. If you aren't doing anything
|
||||
/// accidentally if parentheses are omitted from a function call. If you aren't doing anything
|
||||
/// low-level with function pointers then you can opt-out of casting functions to integers in
|
||||
/// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
|
||||
/// pointer casts in your code.
|
||||
@ -487,6 +487,7 @@ declare_clippy_lint! {
|
||||
/// let y: u32 = x.abs() as u32;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x: i32 = -42;
|
||||
/// let y: u32 = x.unsigned_abs();
|
||||
/// ```
|
||||
|
@ -13,13 +13,14 @@
|
||||
//! This lint is **warn** by default
|
||||
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::{snippet_block, snippet_block_with_applicability};
|
||||
use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -102,7 +103,7 @@ impl EarlyLintPass for CollapsibleIf {
|
||||
fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
|
||||
if let ast::ExprKind::If(check, then, else_) = &expr.kind {
|
||||
if let Some(else_) = else_ {
|
||||
check_collapsible_maybe_if_let(cx, else_);
|
||||
check_collapsible_maybe_if_let(cx, then.span, else_);
|
||||
} else if let ast::ExprKind::Let(..) = check.kind {
|
||||
// Prevent triggering on `if let a = b { if c { .. } }`.
|
||||
} else {
|
||||
@ -119,7 +120,7 @@ fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool {
|
||||
trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*")
|
||||
}
|
||||
|
||||
fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
|
||||
fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) {
|
||||
if_chain! {
|
||||
if let ast::ExprKind::Block(ref block, _) = else_.kind;
|
||||
if !block_starts_with_comment(cx, block);
|
||||
@ -128,6 +129,11 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
|
||||
if !else_.span.from_expansion();
|
||||
if let ast::ExprKind::If(..) = else_.kind;
|
||||
then {
|
||||
// Prevent "elseif"
|
||||
// Check that the "else" is followed by whitespace
|
||||
let up_to_else = then_span.between(block.span);
|
||||
let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { !c.is_whitespace() } else { false };
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
@ -135,7 +141,11 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
|
||||
block.span,
|
||||
"this `else { if .. }` block can be collapsed",
|
||||
"collapse nested if block",
|
||||
snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(),
|
||||
format!(
|
||||
"{}{}",
|
||||
if requires_space { " " } else { "" },
|
||||
snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
|
||||
lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
|
||||
}
|
||||
},
|
||||
hir::ItemKind::Impl(ref impl_) => {
|
||||
hir::ItemKind::Impl(impl_) => {
|
||||
self.in_trait_impl = impl_.of_trait.is_some();
|
||||
},
|
||||
hir::ItemKind::Trait(_, unsafety, ..) => {
|
||||
@ -621,10 +621,8 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
|
||||
let filename = FileName::anon_source_code(&code);
|
||||
|
||||
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let fallback_bundle = rustc_errors::fallback_fluent_bundle(
|
||||
rustc_errors::DEFAULT_LOCALE_RESOURCES,
|
||||
false
|
||||
);
|
||||
let fallback_bundle =
|
||||
rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
|
||||
let emitter = EmitterWriter::new(
|
||||
Box::new(io::sink()),
|
||||
None,
|
||||
@ -741,7 +739,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
|
||||
/// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok).
|
||||
/// Plurals are also excluded (`IDs` is ok).
|
||||
fn is_camel_case(s: &str) -> bool {
|
||||
if s.starts_with(|c: char| c.is_digit(10)) {
|
||||
if s.starts_with(|c: char| c.is_ascii_digit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -180,7 +180,7 @@ const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that imp
|
||||
const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \
|
||||
Forgetting a copy leaves the original intact";
|
||||
const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \
|
||||
Dropping such a type only extends it's contained lifetimes";
|
||||
Dropping such a type only extends its contained lifetimes";
|
||||
const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \
|
||||
Forgetting such a type is the same as dropping it";
|
||||
|
||||
|
65
clippy_lints/src/empty_drop.rs
Normal file
65
clippy_lints/src/empty_drop.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use clippy_utils::{diagnostics::span_lint_and_sugg, peel_blocks};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for empty `Drop` implementations.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Empty `Drop` implementations have no effect when dropping an instance of the type. They are
|
||||
/// most likely useless. However, an empty `Drop` implementation prevents a type from being
|
||||
/// destructured, which might be the intention behind adding the implementation as a marker.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// struct S;
|
||||
///
|
||||
/// impl Drop for S {
|
||||
/// fn drop(&mut self) {}
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// struct S;
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub EMPTY_DROP,
|
||||
restriction,
|
||||
"empty `Drop` implementations"
|
||||
}
|
||||
declare_lint_pass!(EmptyDrop => [EMPTY_DROP]);
|
||||
|
||||
impl LateLintPass<'_> for EmptyDrop {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
if_chain! {
|
||||
if let ItemKind::Impl(Impl {
|
||||
of_trait: Some(ref trait_ref),
|
||||
items: [child],
|
||||
..
|
||||
}) = item.kind;
|
||||
if trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait();
|
||||
if let impl_item_hir = child.id.hir_id();
|
||||
if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
|
||||
if let ImplItemKind::Fn(_, b) = &impl_item.kind;
|
||||
if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
|
||||
let func_expr = peel_blocks(func_expr);
|
||||
if let ExprKind::Block(block, _) = func_expr.kind;
|
||||
if block.stmts.is_empty() && block.expr.is_none();
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
EMPTY_DROP,
|
||||
item.span,
|
||||
"empty drop implementation",
|
||||
"try removing this impl",
|
||||
String::new(),
|
||||
Applicability::MaybeIncorrect
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -66,7 +66,7 @@ fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Spa
|
||||
}
|
||||
|
||||
// there might still be field declarations hidden from the AST
|
||||
// (conditionaly compiled code using #[cfg(..)])
|
||||
// (conditionally compiled code using #[cfg(..)])
|
||||
|
||||
let Some(braces_span_str) = snippet_opt(cx, braces_span) else {
|
||||
return false;
|
||||
|
@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::usage::local_used_after_expr;
|
||||
use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id};
|
||||
use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
@ -103,6 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
||||
let closure_ty = cx.typeck_results().expr_ty(expr);
|
||||
|
||||
if_chain!(
|
||||
if !is_adjusted(cx, &body.value);
|
||||
if let ExprKind::Call(callee, args) = body.value.kind;
|
||||
if let ExprKind::Path(_) = callee.kind;
|
||||
if check_inputs(cx, body.params, args);
|
||||
@ -125,8 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
||||
if_chain! {
|
||||
if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
|
||||
if substs.as_closure().kind() == ClosureKind::FnMut;
|
||||
if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
|
||||
|| path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, callee));
|
||||
if path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr));
|
||||
|
||||
then {
|
||||
// Mutable closure is used after current expr; we cannot consume it.
|
||||
@ -145,6 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
||||
);
|
||||
|
||||
if_chain!(
|
||||
if !is_adjusted(cx, &body.value);
|
||||
if let ExprKind::MethodCall(path, args, _) = body.value.kind;
|
||||
if check_inputs(cx, body.params, args);
|
||||
let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
|
||||
|
77
clippy_lints/src/format_push_string.rs
Normal file
77
clippy_lints/src/format_push_string.rs
Normal file
@ -0,0 +1,77 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{match_def_path, paths, peel_hir_expr_refs};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects cases where the result of a `format!` call is
|
||||
/// appended to an existing `String`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Introduces an extra, avoidable heap allocation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let mut s = String::new();
|
||||
/// s += &format!("0x{:X}", 1024);
|
||||
/// s.push_str(&format!("0x{:X}", 1024));
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// use std::fmt::Write as _; // import without risk of name clashing
|
||||
///
|
||||
/// let mut s = String::new();
|
||||
/// let _ = write!(s, "0x{:X}", 1024);
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub FORMAT_PUSH_STRING,
|
||||
perf,
|
||||
"`format!(..)` appended to existing `String`"
|
||||
}
|
||||
declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]);
|
||||
|
||||
fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||
is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String)
|
||||
}
|
||||
fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||
if let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id {
|
||||
cx.tcx.get_diagnostic_name(macro_def_id) == Some(sym::format_macro)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for FormatPushString {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let arg = match expr.kind {
|
||||
ExprKind::MethodCall(_, [_, arg], _) => {
|
||||
if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) &&
|
||||
match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
|
||||
arg
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ExprKind::AssignOp(op, left, arg)
|
||||
if op.node == BinOpKind::Add && is_string(cx, left) => {
|
||||
arg
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
let (arg, _) = peel_hir_expr_refs(arg);
|
||||
if is_format(cx, arg) {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
FORMAT_PUSH_STRING,
|
||||
expr.span,
|
||||
"`format!(..)` appended to existing `String`",
|
||||
None,
|
||||
"consider using `write!` to avoid the extra allocation",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
|
||||
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||
let attr = must_use_attr(attrs);
|
||||
if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind {
|
||||
if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind {
|
||||
let is_public = cx.access_levels.is_exported(item.def_id);
|
||||
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
|
||||
if let Some(attr) = attr {
|
||||
@ -105,12 +105,7 @@ fn check_needless_must_use(
|
||||
fn_header_span,
|
||||
"this unit-returning function has a `#[must_use]` attribute",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
attr.span,
|
||||
"remove the attribute",
|
||||
"",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable);
|
||||
},
|
||||
);
|
||||
} else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
|
||||
|
@ -14,7 +14,7 @@ use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use super::RESULT_UNIT_ERR;
|
||||
|
||||
pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
||||
if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind {
|
||||
if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind {
|
||||
let is_public = cx.access_levels.is_exported(item.def_id);
|
||||
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
|
||||
if is_public {
|
||||
|
@ -1,3 +1,4 @@
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::source::snippet;
|
||||
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
@ -22,6 +23,11 @@ declare_clippy_lint! {
|
||||
/// # let x = 1;
|
||||
/// x / 1 + 0 * 1 - 0 | 0;
|
||||
/// ```
|
||||
///
|
||||
/// ### Known problems
|
||||
/// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to
|
||||
/// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code.
|
||||
/// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724)
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub IDENTITY_OP,
|
||||
complexity,
|
||||
@ -31,36 +37,66 @@ declare_clippy_lint! {
|
||||
declare_lint_pass!(IdentityOp => [IDENTITY_OP]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for IdentityOp {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if e.span.from_expansion() {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
if let ExprKind::Binary(cmp, left, right) = e.kind {
|
||||
if is_allowed(cx, cmp, left, right) {
|
||||
return;
|
||||
}
|
||||
match cmp.node {
|
||||
BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
|
||||
check(cx, left, 0, e.span, right.span);
|
||||
check(cx, right, 0, e.span, left.span);
|
||||
},
|
||||
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => check(cx, right, 0, e.span, left.span),
|
||||
BinOpKind::Mul => {
|
||||
check(cx, left, 1, e.span, right.span);
|
||||
check(cx, right, 1, e.span, left.span);
|
||||
},
|
||||
BinOpKind::Div => check(cx, right, 1, e.span, left.span),
|
||||
BinOpKind::BitAnd => {
|
||||
check(cx, left, -1, e.span, right.span);
|
||||
check(cx, right, -1, e.span, left.span);
|
||||
},
|
||||
BinOpKind::Rem => check_remainder(cx, left, right, e.span, left.span),
|
||||
_ => (),
|
||||
if let ExprKind::Binary(cmp, left, right) = &expr.kind {
|
||||
if !is_allowed(cx, *cmp, left, right) {
|
||||
match cmp.node {
|
||||
BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
|
||||
if reducible_to_right(cx, expr, right) {
|
||||
check(cx, left, 0, expr.span, right.span);
|
||||
}
|
||||
check(cx, right, 0, expr.span, left.span);
|
||||
},
|
||||
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
|
||||
check(cx, right, 0, expr.span, left.span);
|
||||
},
|
||||
BinOpKind::Mul => {
|
||||
if reducible_to_right(cx, expr, right) {
|
||||
check(cx, left, 1, expr.span, right.span);
|
||||
}
|
||||
check(cx, right, 1, expr.span, left.span);
|
||||
},
|
||||
BinOpKind::Div => check(cx, right, 1, expr.span, left.span),
|
||||
BinOpKind::BitAnd => {
|
||||
if reducible_to_right(cx, expr, right) {
|
||||
check(cx, left, -1, expr.span, right.span);
|
||||
}
|
||||
check(cx, right, -1, expr.span, left.span);
|
||||
},
|
||||
BinOpKind::Rem => {
|
||||
// Don't call reducible_to_right because N % N is always reducible to 1
|
||||
check_remainder(cx, left, right, expr.span, left.span);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if `left op ..right` can be actually reduced to `right`
|
||||
/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }`
|
||||
/// cannot be reduced to `if b { 1 } else { 2 } + if b { 3 } else { 4 }`
|
||||
/// See #8724
|
||||
fn reducible_to_right(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> bool {
|
||||
if let ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) = right.kind {
|
||||
is_toplevel_binary(cx, binary)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn is_toplevel_binary(cx: &LateContext<'_>, must_be_binary: &Expr<'_>) -> bool {
|
||||
if let Some(parent) = get_parent_expr(cx, must_be_binary) && let ExprKind::Binary(..) = &parent.kind {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool {
|
||||
// This lint applies to integers
|
||||
!cx.typeck_results().expr_ty(left).peel_refs().is_integral()
|
||||
|
@ -117,7 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
|
||||
}
|
||||
|
||||
match item.kind {
|
||||
ItemKind::Impl(ref impl_) => {
|
||||
ItemKind::Impl(impl_) => {
|
||||
let mut vis = ImplicitHasherTypeVisitor::new(cx);
|
||||
vis.visit_ty(impl_.self_ty);
|
||||
|
||||
@ -155,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
|
||||
);
|
||||
}
|
||||
},
|
||||
ItemKind::Fn(ref sig, ref generics, body_id) => {
|
||||
ItemKind::Fn(ref sig, generics, body_id) => {
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
|
||||
for ty in sig.decl.inputs {
|
||||
|
@ -7,6 +7,7 @@ use rustc_hir::{self as hir, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use std::fmt::Write as _;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -89,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
|
||||
let mut fields_snippet = String::new();
|
||||
let (last_ident, idents) = ordered_fields.split_last().unwrap();
|
||||
for ident in idents {
|
||||
fields_snippet.push_str(&format!("{}, ", ident));
|
||||
let _ = write!(fields_snippet, "{}, ", ident);
|
||||
}
|
||||
fields_snippet.push_str(&last_ident.to_string());
|
||||
|
||||
|
@ -116,7 +116,7 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap<hir
|
||||
let bound_ty = cx.typeck_results().node_type(pat.hir_id);
|
||||
if let ty::Slice(inner_ty) | ty::Array(inner_ty, _) = bound_ty.peel_refs().kind() {
|
||||
// The values need to use the `ref` keyword if they can't be copied.
|
||||
// This will need to be adjusted if the lint want to support multable access in the future
|
||||
// This will need to be adjusted if the lint want to support mutable access in the future
|
||||
let src_is_ref = bound_ty.is_ref() && binding != hir::BindingAnnotation::Ref;
|
||||
let needs_ref = !(src_is_ref || is_copy(cx, *inner_ty));
|
||||
|
||||
|
@ -119,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl {
|
||||
fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option<Span> {
|
||||
let id = cx.tcx.hir().local_def_id_to_hir_id(id);
|
||||
if let Node::Item(&Item {
|
||||
kind: ItemKind::Impl(ref impl_item),
|
||||
kind: ItemKind::Impl(impl_item),
|
||||
span,
|
||||
..
|
||||
}) = cx.tcx.hir().get(id)
|
||||
|
@ -1,6 +1,7 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
@ -49,6 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields {
|
||||
&& fields
|
||||
.iter()
|
||||
.all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit))
|
||||
&& !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..))
|
||||
{
|
||||
let expr_spans = fields
|
||||
.iter()
|
||||
|
86
clippy_lints/src/large_include_file.rs
Normal file
86
clippy_lints/src/large_include_file.rs
Normal file
@ -0,0 +1,86 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_note;
|
||||
use clippy_utils::is_lint_allowed;
|
||||
use clippy_utils::macros::root_macro_call_first_node;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_hir::ExprKind;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the inclusion of large files via `include_bytes!()`
|
||||
/// and `include_str!()`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Including large files can increase the size of the binary
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let included_str = include_str!("very_large_file.txt");
|
||||
/// let included_bytes = include_bytes!("very_large_file.txt");
|
||||
/// ```
|
||||
///
|
||||
/// Instead, you can load the file at runtime:
|
||||
/// ```rust,ignore
|
||||
/// use std::fs;
|
||||
///
|
||||
/// let string = fs::read_to_string("very_large_file.txt")?;
|
||||
/// let bytes = fs::read("very_large_file.txt")?;
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub LARGE_INCLUDE_FILE,
|
||||
restriction,
|
||||
"including a large file"
|
||||
}
|
||||
|
||||
pub struct LargeIncludeFile {
|
||||
max_file_size: u64,
|
||||
}
|
||||
|
||||
impl LargeIncludeFile {
|
||||
#[must_use]
|
||||
pub fn new(max_file_size: u64) -> Self {
|
||||
Self { max_file_size }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);
|
||||
|
||||
impl LateLintPass<'_> for LargeIncludeFile {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
|
||||
if_chain! {
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr);
|
||||
if !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id);
|
||||
if cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id);
|
||||
if let ExprKind::Lit(lit) = &expr.kind;
|
||||
then {
|
||||
let len = match &lit.node {
|
||||
// include_bytes
|
||||
LitKind::ByteStr(bstr) => bstr.len(),
|
||||
// include_str
|
||||
LitKind::Str(sym, _) => sym.as_str().len(),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if len as u64 <= self.max_file_size {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
LARGE_INCLUDE_FILE,
|
||||
expr.span,
|
||||
"attempted to include a large file",
|
||||
None,
|
||||
&format!(
|
||||
"the configuration allows a maximum size of {} bytes",
|
||||
self.max_file_size
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(attrs::DEPRECATED_SEMVER),
|
||||
LintId::of(attrs::MISMATCHED_TARGET_OS),
|
||||
LintId::of(attrs::USELESS_ATTRIBUTE),
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
|
||||
LintId::of(bit_mask::BAD_BIT_MASK),
|
||||
@ -23,6 +24,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
|
||||
LintId::of(booleans::LOGIC_BUG),
|
||||
LintId::of(booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
|
||||
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
|
||||
LintId::of(casts::CAST_ENUM_TRUNCATION),
|
||||
@ -77,6 +79,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
|
||||
LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
|
||||
LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
|
||||
LintId::of(format_push_string::FORMAT_PUSH_STRING),
|
||||
LintId::of(formatting::POSSIBLE_MISSING_COMMA),
|
||||
LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
|
||||
LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
|
||||
@ -165,6 +168,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(methods::FLAT_MAP_IDENTITY),
|
||||
LintId::of(methods::INSPECT_FOR_EACH),
|
||||
LintId::of(methods::INTO_ITER_ON_REF),
|
||||
LintId::of(methods::IS_DIGIT_ASCII_RADIX),
|
||||
LintId::of(methods::ITERATOR_STEP_BY_ZERO),
|
||||
LintId::of(methods::ITER_CLONED_COLLECT),
|
||||
LintId::of(methods::ITER_COUNT),
|
||||
@ -182,6 +186,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(methods::MAP_FLATTEN),
|
||||
LintId::of(methods::MAP_IDENTITY),
|
||||
LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
|
||||
LintId::of(methods::NEEDLESS_OPTION_TAKE),
|
||||
LintId::of(methods::NEEDLESS_SPLITN),
|
||||
LintId::of(methods::NEW_RET_NO_SELF),
|
||||
LintId::of(methods::OK_EXPECT),
|
||||
@ -243,7 +248,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
|
||||
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
|
||||
LintId::of(octal_escapes::OCTAL_ESCAPES),
|
||||
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
|
||||
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
|
||||
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
|
||||
@ -275,8 +279,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
|
||||
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
|
||||
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
|
||||
LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
|
||||
LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
|
||||
LintId::of(strings::TRIM_SPLIT_WHITESPACE),
|
||||
LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
|
||||
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
|
||||
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
|
||||
@ -306,10 +310,12 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(uninit_vec::UNINIT_VEC),
|
||||
LintId::of(unit_hash::UNIT_HASH),
|
||||
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
|
||||
LintId::of(unit_types::LET_UNIT_VALUE),
|
||||
LintId::of(unit_types::UNIT_ARG),
|
||||
LintId::of(unit_types::UNIT_CMP),
|
||||
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
|
||||
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
|
||||
LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
|
||||
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
|
||||
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
|
||||
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
|
||||
|
@ -5,6 +5,7 @@
|
||||
store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
|
||||
LintId::of(attrs::DEPRECATED_CFG_ATTR),
|
||||
LintId::of(booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(casts::CHAR_LIT_AS_U8),
|
||||
LintId::of(casts::UNNECESSARY_CAST),
|
||||
LintId::of(derivable_impls::DERIVABLE_IMPLS),
|
||||
@ -45,6 +46,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
||||
LintId::of(methods::MAP_FLATTEN),
|
||||
LintId::of(methods::MAP_IDENTITY),
|
||||
LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
|
||||
LintId::of(methods::NEEDLESS_OPTION_TAKE),
|
||||
LintId::of(methods::NEEDLESS_SPLITN),
|
||||
LintId::of(methods::OPTION_AS_REF_DEREF),
|
||||
LintId::of(methods::OPTION_FILTER_MAP),
|
||||
@ -66,7 +68,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
||||
LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
|
||||
LintId::of(no_effect::NO_EFFECT),
|
||||
LintId::of(no_effect::UNNECESSARY_OPERATION),
|
||||
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
|
||||
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
|
||||
LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
|
||||
LintId::of(precedence::PRECEDENCE),
|
||||
|
@ -52,6 +52,7 @@ store.register_lints(&[
|
||||
attrs::INLINE_ALWAYS,
|
||||
attrs::MISMATCHED_TARGET_OS,
|
||||
attrs::USELESS_ATTRIBUTE,
|
||||
await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE,
|
||||
await_holding_invalid::AWAIT_HOLDING_LOCK,
|
||||
await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
|
||||
bit_mask::BAD_BIT_MASK,
|
||||
@ -64,6 +65,7 @@ store.register_lints(&[
|
||||
booleans::NONMINIMAL_BOOL,
|
||||
borrow_as_ptr::BORROW_AS_PTR,
|
||||
bytecount::NAIVE_BYTECOUNT,
|
||||
bytes_count_to_len::BYTES_COUNT_TO_LEN,
|
||||
cargo::CARGO_COMMON_METADATA,
|
||||
cargo::MULTIPLE_CRATE_VERSIONS,
|
||||
cargo::NEGATIVE_FEATURE_NAMES,
|
||||
@ -132,6 +134,7 @@ store.register_lints(&[
|
||||
drop_forget_ref::UNDROPPED_MANUALLY_DROPS,
|
||||
duration_subsec::DURATION_SUBSEC,
|
||||
else_if_without_else::ELSE_IF_WITHOUT_ELSE,
|
||||
empty_drop::EMPTY_DROP,
|
||||
empty_enum::EMPTY_ENUM,
|
||||
empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS,
|
||||
entry::MAP_ENTRY,
|
||||
@ -165,6 +168,7 @@ store.register_lints(&[
|
||||
format_args::TO_STRING_IN_FORMAT_ARGS,
|
||||
format_impl::PRINT_IN_FORMAT_IMPL,
|
||||
format_impl::RECURSIVE_FORMAT_IMPL,
|
||||
format_push_string::FORMAT_PUSH_STRING,
|
||||
formatting::POSSIBLE_MISSING_COMMA,
|
||||
formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
|
||||
formatting::SUSPICIOUS_ELSE_FORMATTING,
|
||||
@ -205,6 +209,7 @@ store.register_lints(&[
|
||||
iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
|
||||
large_const_arrays::LARGE_CONST_ARRAYS,
|
||||
large_enum_variant::LARGE_ENUM_VARIANT,
|
||||
large_include_file::LARGE_INCLUDE_FILE,
|
||||
large_stack_arrays::LARGE_STACK_ARRAYS,
|
||||
len_zero::COMPARISON_TO_EMPTY,
|
||||
len_zero::LEN_WITHOUT_IS_EMPTY,
|
||||
@ -302,6 +307,7 @@ store.register_lints(&[
|
||||
methods::INEFFICIENT_TO_STRING,
|
||||
methods::INSPECT_FOR_EACH,
|
||||
methods::INTO_ITER_ON_REF,
|
||||
methods::IS_DIGIT_ASCII_RADIX,
|
||||
methods::ITERATOR_STEP_BY_ZERO,
|
||||
methods::ITER_CLONED_COLLECT,
|
||||
methods::ITER_COUNT,
|
||||
@ -321,6 +327,7 @@ store.register_lints(&[
|
||||
methods::MAP_IDENTITY,
|
||||
methods::MAP_UNWRAP_OR,
|
||||
methods::NEEDLESS_OPTION_AS_DEREF,
|
||||
methods::NEEDLESS_OPTION_TAKE,
|
||||
methods::NEEDLESS_SPLITN,
|
||||
methods::NEW_RET_NO_SELF,
|
||||
methods::OK_EXPECT,
|
||||
@ -432,6 +439,7 @@ store.register_lints(&[
|
||||
ptr::PTR_ARG,
|
||||
ptr_eq::PTR_EQ,
|
||||
ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
|
||||
pub_use::PUB_USE,
|
||||
question_mark::QUESTION_MARK,
|
||||
ranges::MANUAL_RANGE_CONTAINS,
|
||||
ranges::RANGE_MINUS_ONE,
|
||||
@ -474,6 +482,7 @@ store.register_lints(&[
|
||||
strings::STRING_SLICE,
|
||||
strings::STRING_TO_STRING,
|
||||
strings::STR_TO_STRING,
|
||||
strings::TRIM_SPLIT_WHITESPACE,
|
||||
strlen_on_c_strings::STRLEN_ON_C_STRINGS,
|
||||
suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS,
|
||||
suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
|
||||
@ -523,6 +532,7 @@ store.register_lints(&[
|
||||
unit_types::UNIT_CMP,
|
||||
unnamed_address::FN_ADDRESS_COMPARISONS,
|
||||
unnamed_address::VTABLE_ADDRESS_COMPARISONS,
|
||||
unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
|
||||
unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
|
||||
unnecessary_sort_by::UNNECESSARY_SORT_BY,
|
||||
unnecessary_wraps::UNNECESSARY_WRAPS,
|
||||
|
@ -20,6 +20,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
|
||||
LintId::of(mutex_atomic::MUTEX_INTEGER),
|
||||
LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
|
||||
LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
|
||||
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
|
||||
LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
|
||||
LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
|
||||
LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
|
||||
@ -27,6 +28,8 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
|
||||
LintId::of(strings::STRING_LIT_AS_BYTES),
|
||||
LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
|
||||
LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY),
|
||||
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
|
||||
LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
|
||||
LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
|
||||
LintId::of(transmute::USELESS_TRANSMUTE),
|
||||
LintId::of(use_self::USE_SELF),
|
||||
|
@ -82,14 +82,12 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
||||
LintId::of(ref_option_ref::REF_OPTION_REF),
|
||||
LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
|
||||
LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
|
||||
LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
|
||||
LintId::of(strings::STRING_ADD_ASSIGN),
|
||||
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
|
||||
LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
|
||||
LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
|
||||
LintId::of(types::LINKEDLIST),
|
||||
LintId::of(types::OPTION_OPTION),
|
||||
LintId::of(unicode::UNICODE_NOT_NFC),
|
||||
LintId::of(unit_types::LET_UNIT_VALUE),
|
||||
LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS),
|
||||
LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS),
|
||||
LintId::of(unused_async::UNUSED_ASYNC),
|
||||
|
@ -7,6 +7,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
|
||||
LintId::of(escape::BOXED_LOCAL),
|
||||
LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
|
||||
LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
|
||||
LintId::of(format_push_string::FORMAT_PUSH_STRING),
|
||||
LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
|
||||
LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
|
||||
LintId::of(loops::MANUAL_MEMCPY),
|
||||
@ -23,7 +24,6 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
|
||||
LintId::of(misc::CMP_OWNED),
|
||||
LintId::of(redundant_clone::REDUNDANT_CLONE),
|
||||
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
|
||||
LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
|
||||
LintId::of(types::BOX_COLLECTION),
|
||||
LintId::of(types::REDUNDANT_ALLOCATION),
|
||||
LintId::of(vec::USELESS_VEC),
|
||||
|
@ -16,6 +16,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
||||
LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION),
|
||||
LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
|
||||
LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
|
||||
LintId::of(empty_drop::EMPTY_DROP),
|
||||
LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS),
|
||||
LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
|
||||
LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
|
||||
@ -26,6 +27,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
||||
LintId::of(indexing_slicing::INDEXING_SLICING),
|
||||
LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL),
|
||||
LintId::of(integer_division::INTEGER_DIVISION),
|
||||
LintId::of(large_include_file::LARGE_INCLUDE_FILE),
|
||||
LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
|
||||
LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
|
||||
LintId::of(map_err_ignore::MAP_ERR_IGNORE),
|
||||
@ -53,6 +55,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
||||
LintId::of(panic_unimplemented::UNIMPLEMENTED),
|
||||
LintId::of(panic_unimplemented::UNREACHABLE),
|
||||
LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
|
||||
LintId::of(pub_use::PUB_USE),
|
||||
LintId::of(redundant_slicing::DEREF_BY_SLICING),
|
||||
LintId::of(same_name_method::SAME_NAME_METHOD),
|
||||
LintId::of(shadow::SHADOW_REUSE),
|
||||
|
@ -61,6 +61,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(methods::CHARS_NEXT_CMP),
|
||||
LintId::of(methods::ERR_EXPECT),
|
||||
LintId::of(methods::INTO_ITER_ON_REF),
|
||||
LintId::of(methods::IS_DIGIT_ASCII_RADIX),
|
||||
LintId::of(methods::ITER_CLONED_COLLECT),
|
||||
LintId::of(methods::ITER_NEXT_SLICE),
|
||||
LintId::of(methods::ITER_NTH_ZERO),
|
||||
@ -104,8 +105,11 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(returns::NEEDLESS_RETURN),
|
||||
LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
|
||||
LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
|
||||
LintId::of(strings::TRIM_SPLIT_WHITESPACE),
|
||||
LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
|
||||
LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
|
||||
LintId::of(unit_types::LET_UNIT_VALUE),
|
||||
LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
|
||||
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
|
||||
LintId::of(unused_unit::UNUSED_UNIT),
|
||||
LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
|
||||
|
@ -5,6 +5,7 @@
|
||||
store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![
|
||||
LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
|
||||
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
|
||||
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
|
||||
|
@ -163,6 +163,8 @@ mod deprecated_lints;
|
||||
#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
|
||||
mod utils;
|
||||
|
||||
mod renamed_lints;
|
||||
|
||||
// begin lints modules, do not remove this comment, it’s used in `update_lints`
|
||||
mod absurd_extreme_comparisons;
|
||||
mod approx_const;
|
||||
@ -181,6 +183,7 @@ mod bool_assert_comparison;
|
||||
mod booleans;
|
||||
mod borrow_as_ptr;
|
||||
mod bytecount;
|
||||
mod bytes_count_to_len;
|
||||
mod cargo;
|
||||
mod case_sensitive_file_extension_comparisons;
|
||||
mod casts;
|
||||
@ -209,6 +212,7 @@ mod double_parens;
|
||||
mod drop_forget_ref;
|
||||
mod duration_subsec;
|
||||
mod else_if_without_else;
|
||||
mod empty_drop;
|
||||
mod empty_enum;
|
||||
mod empty_structs_with_brackets;
|
||||
mod entry;
|
||||
@ -231,6 +235,7 @@ mod floating_point_arithmetic;
|
||||
mod format;
|
||||
mod format_args;
|
||||
mod format_impl;
|
||||
mod format_push_string;
|
||||
mod formatting;
|
||||
mod from_over_into;
|
||||
mod from_str_radix_10;
|
||||
@ -259,6 +264,7 @@ mod items_after_statements;
|
||||
mod iter_not_returning_iterator;
|
||||
mod large_const_arrays;
|
||||
mod large_enum_variant;
|
||||
mod large_include_file;
|
||||
mod large_stack_arrays;
|
||||
mod len_zero;
|
||||
mod let_if_seq;
|
||||
@ -336,6 +342,7 @@ mod precedence;
|
||||
mod ptr;
|
||||
mod ptr_eq;
|
||||
mod ptr_offset_with_cast;
|
||||
mod pub_use;
|
||||
mod question_mark;
|
||||
mod ranges;
|
||||
mod redundant_clone;
|
||||
@ -383,6 +390,7 @@ mod unit_hash;
|
||||
mod unit_return_expecting_ord;
|
||||
mod unit_types;
|
||||
mod unnamed_address;
|
||||
mod unnecessary_owned_empty_strings;
|
||||
mod unnecessary_self_imports;
|
||||
mod unnecessary_sort_by;
|
||||
mod unnecessary_wraps;
|
||||
@ -499,7 +507,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
{
|
||||
store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal));
|
||||
store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce));
|
||||
store.register_late_pass(|| Box::new(utils::inspector::DeepCodeInspector));
|
||||
store.register_late_pass(|| Box::new(utils::internal_lints::CollapsibleCalls));
|
||||
store.register_late_pass(|| Box::new(utils::internal_lints::CompilerLintFunctions::new()));
|
||||
store.register_late_pass(|| Box::new(utils::internal_lints::IfChainStyle));
|
||||
@ -511,8 +518,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl));
|
||||
}
|
||||
|
||||
store.register_late_pass(|| Box::new(utils::dump_hir::DumpHir));
|
||||
store.register_late_pass(|| Box::new(utils::author::Author));
|
||||
store.register_late_pass(|| Box::new(await_holding_invalid::AwaitHolding));
|
||||
let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
|
||||
store.register_late_pass(move || {
|
||||
Box::new(await_holding_invalid::AwaitHolding::new(
|
||||
await_holding_invalid_types.clone(),
|
||||
))
|
||||
});
|
||||
store.register_late_pass(|| Box::new(serde_api::SerdeApi));
|
||||
let vec_box_size_threshold = conf.vec_box_size_threshold;
|
||||
let type_complexity_threshold = conf.type_complexity_threshold;
|
||||
@ -572,7 +585,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv)));
|
||||
store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv)));
|
||||
store.register_late_pass(move || Box::new(matches::Matches::new(msrv)));
|
||||
store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::new(msrv)));
|
||||
store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
|
||||
store.register_late_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));
|
||||
store.register_late_pass(move || Box::new(manual_strip::ManualStrip::new(msrv)));
|
||||
store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
|
||||
store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
|
||||
@ -812,6 +826,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone())));
|
||||
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
|
||||
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
|
||||
store.register_late_pass(|| Box::new(empty_drop::EmptyDrop));
|
||||
store.register_late_pass(|| Box::new(strings::StrToString));
|
||||
store.register_late_pass(|| Box::new(strings::StringToString));
|
||||
store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
|
||||
@ -868,6 +883,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
});
|
||||
store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
|
||||
store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
|
||||
store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
|
||||
store.register_early_pass(|| Box::new(pub_use::PubUse));
|
||||
store.register_late_pass(|| Box::new(format_push_string::FormatPushString));
|
||||
store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen));
|
||||
let max_include_file_size = conf.max_include_file_size;
|
||||
store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
|
||||
store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
@ -919,43 +941,9 @@ fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) {
|
||||
///
|
||||
/// Used in `./src/driver.rs`.
|
||||
pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
|
||||
// NOTE: when renaming a lint, add a corresponding test to tests/ui/rename.rs
|
||||
ls.register_renamed("clippy::stutter", "clippy::module_name_repetitions");
|
||||
ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default");
|
||||
ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity");
|
||||
ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes");
|
||||
ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map");
|
||||
ls.register_renamed("clippy::box_vec", "clippy::box_collection");
|
||||
ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions");
|
||||
ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions");
|
||||
ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or");
|
||||
ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or");
|
||||
ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or");
|
||||
ls.register_renamed("clippy::option_unwrap_used", "clippy::unwrap_used");
|
||||
ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used");
|
||||
ls.register_renamed("clippy::option_expect_used", "clippy::expect_used");
|
||||
ls.register_renamed("clippy::result_expect_used", "clippy::expect_used");
|
||||
ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles");
|
||||
ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles");
|
||||
ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
|
||||
ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
|
||||
ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
|
||||
ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok");
|
||||
ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types");
|
||||
ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods");
|
||||
ls.register_renamed("clippy::ref_in_deref", "clippy::needless_borrow");
|
||||
ls.register_renamed("clippy::to_string_in_display", "clippy::recursive_format_impl");
|
||||
|
||||
// uplifted lints
|
||||
ls.register_renamed("clippy::invalid_ref", "invalid_value");
|
||||
ls.register_renamed("clippy::into_iter_on_array", "array_into_iter");
|
||||
ls.register_renamed("clippy::unused_label", "unused_labels");
|
||||
ls.register_renamed("clippy::drop_bounds", "drop_bounds");
|
||||
ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr");
|
||||
ls.register_renamed("clippy::panic_params", "non_fmt_panics");
|
||||
ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints");
|
||||
ls.register_renamed("clippy::invalid_atomic_ordering", "invalid_atomic_ordering");
|
||||
ls.register_renamed("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums");
|
||||
for (old_name, new_name) in renamed_lints::RENAMED_LINTS {
|
||||
ls.register_renamed(old_name, new_name);
|
||||
}
|
||||
}
|
||||
|
||||
// only exists to let the dogfood integration test works.
|
||||
|
@ -1,16 +1,19 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::trait_ref_of_method;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
|
||||
use rustc_hir::intravisit::{
|
||||
walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, Visitor,
|
||||
walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
|
||||
walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor,
|
||||
};
|
||||
use rustc_hir::FnRetTy::Return;
|
||||
use rustc_hir::{
|
||||
BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem,
|
||||
BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem,
|
||||
ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier,
|
||||
TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter as middle_nested_filter;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::{kw, Ident, Symbol};
|
||||
@ -82,8 +85,10 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if let ItemKind::Fn(ref sig, ref generics, id) = item.kind {
|
||||
if let ItemKind::Fn(ref sig, generics, id) = item.kind {
|
||||
check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true);
|
||||
} else if let ItemKind::Impl(impl_) = item.kind {
|
||||
report_extra_impl_lifetimes(cx, impl_);
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
||||
sig.decl,
|
||||
Some(id),
|
||||
None,
|
||||
&item.generics,
|
||||
item.generics,
|
||||
item.span,
|
||||
report_extra_lifetimes,
|
||||
);
|
||||
@ -108,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
||||
TraitFn::Required(sig) => (None, Some(sig)),
|
||||
TraitFn::Provided(id) => (Some(id), None),
|
||||
};
|
||||
check_fn_inner(cx, sig.decl, body, trait_sig, &item.generics, item.span, true);
|
||||
check_fn_inner(cx, sig.decl, body, trait_sig, item.generics, item.span, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -201,8 +206,7 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident:
|
||||
visitor.visit_ty(self_ty);
|
||||
|
||||
!visitor.all_lts().is_empty()
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -486,11 +490,29 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_
|
||||
false
|
||||
}
|
||||
|
||||
struct LifetimeChecker {
|
||||
struct LifetimeChecker<'cx, 'tcx, F> {
|
||||
cx: &'cx LateContext<'tcx>,
|
||||
map: FxHashMap<Symbol, Span>,
|
||||
phantom: std::marker::PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for LifetimeChecker {
|
||||
impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> {
|
||||
fn new(cx: &'cx LateContext<'tcx>, map: FxHashMap<Symbol, Span>) -> LifetimeChecker<'cx, 'tcx, F> {
|
||||
Self {
|
||||
cx,
|
||||
map,
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx, F> Visitor<'tcx> for LifetimeChecker<'cx, 'tcx, F>
|
||||
where
|
||||
F: NestedFilter<'tcx>,
|
||||
{
|
||||
type Map = rustc_middle::hir::map::Map<'tcx>;
|
||||
type NestedFilter = F;
|
||||
|
||||
// for lifetimes as parameters of generics
|
||||
fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
|
||||
self.map.remove(&lifetime.name.ident().name);
|
||||
@ -506,6 +528,10 @@ impl<'tcx> Visitor<'tcx> for LifetimeChecker {
|
||||
walk_generic_param(self, param);
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.cx.tcx.hir()
|
||||
}
|
||||
}
|
||||
|
||||
fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) {
|
||||
@ -517,7 +543,7 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>,
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
let mut checker = LifetimeChecker { map: hs };
|
||||
let mut checker = LifetimeChecker::<hir_nested_filter::None>::new(cx, hs);
|
||||
|
||||
walk_generics(&mut checker, generics);
|
||||
walk_fn_decl(&mut checker, func);
|
||||
@ -532,6 +558,32 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>,
|
||||
}
|
||||
}
|
||||
|
||||
fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>) {
|
||||
let hs = impl_
|
||||
.generics
|
||||
.params
|
||||
.iter()
|
||||
.filter_map(|par| match par.kind {
|
||||
GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
let mut checker = LifetimeChecker::<middle_nested_filter::All>::new(cx, hs);
|
||||
|
||||
walk_generics(&mut checker, impl_.generics);
|
||||
if let Some(ref trait_ref) = impl_.of_trait {
|
||||
walk_trait_ref(&mut checker, trait_ref);
|
||||
}
|
||||
walk_ty(&mut checker, impl_.self_ty);
|
||||
for item in impl_.items {
|
||||
walk_impl_item_ref(&mut checker, item);
|
||||
}
|
||||
|
||||
for &v in checker.map.values() {
|
||||
span_lint(cx, EXTRA_UNUSED_LIFETIMES, v, "this lifetime isn't used in the impl");
|
||||
}
|
||||
}
|
||||
|
||||
struct BodyLifetimeChecker {
|
||||
lifetimes_used_in_body: bool,
|
||||
}
|
||||
|
@ -42,17 +42,12 @@ declare_clippy_lint! {
|
||||
/// This is most probably a typo
|
||||
///
|
||||
/// ### Known problems
|
||||
/// - Recommends a signed suffix, even though the number might be too big and an unsigned
|
||||
/// suffix is required
|
||||
/// - Does not match on integers too large to fit in the corresponding unsigned type
|
||||
/// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// // Probably mistyped
|
||||
/// 2_32;
|
||||
///
|
||||
/// // Good
|
||||
/// 2_i32;
|
||||
/// `2_32` => `2_i32`
|
||||
/// `250_8 => `250_u8`
|
||||
/// ```
|
||||
#[clippy::version = "1.30.0"]
|
||||
pub MISTYPED_LITERAL_SUFFIXES,
|
||||
@ -310,18 +305,47 @@ impl LiteralDigitGrouping {
|
||||
return true;
|
||||
}
|
||||
|
||||
let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent {
|
||||
(exponent, &["32", "64"][..], 'f')
|
||||
let (part, mistyped_suffixes, is_float) = if let Some((_, exponent)) = &mut num_lit.exponent {
|
||||
(exponent, &["32", "64"][..], true)
|
||||
} else if num_lit.fraction.is_some() {
|
||||
(&mut num_lit.integer, &["32", "64"][..], 'f')
|
||||
return true;
|
||||
} else {
|
||||
(&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i')
|
||||
(&mut num_lit.integer, &["8", "16", "32", "64"][..], false)
|
||||
};
|
||||
|
||||
let mut split = part.rsplit('_');
|
||||
let last_group = split.next().expect("At least one group");
|
||||
if split.next().is_some() && mistyped_suffixes.contains(&last_group) {
|
||||
*part = &part[..part.len() - last_group.len()];
|
||||
let main_part = &part[..part.len() - last_group.len()];
|
||||
let missing_char;
|
||||
if is_float {
|
||||
missing_char = 'f';
|
||||
} else {
|
||||
let radix = match num_lit.radix {
|
||||
Radix::Binary => 2,
|
||||
Radix::Octal => 8,
|
||||
Radix::Decimal => 10,
|
||||
Radix::Hexadecimal => 16,
|
||||
};
|
||||
if let Ok(int) = u64::from_str_radix(&main_part.replace('_', ""), radix) {
|
||||
missing_char = match (last_group, int) {
|
||||
("8", i) if i8::try_from(i).is_ok() => 'i',
|
||||
("16", i) if i16::try_from(i).is_ok() => 'i',
|
||||
("32", i) if i32::try_from(i).is_ok() => 'i',
|
||||
("64", i) if i64::try_from(i).is_ok() => 'i',
|
||||
("8", u) if u8::try_from(u).is_ok() => 'u',
|
||||
("16", u) if u16::try_from(u).is_ok() => 'u',
|
||||
("32", u) if u32::try_from(u).is_ok() => 'u',
|
||||
("64", _) => 'u',
|
||||
_ => {
|
||||
return true;
|
||||
},
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
*part = main_part;
|
||||
let mut sugg = num_lit.format();
|
||||
sugg.push('_');
|
||||
sugg.push(missing_char);
|
||||
|
@ -168,8 +168,9 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
|
||||
.operands
|
||||
.iter()
|
||||
.map(|(o, _)| match o {
|
||||
InlineAsmOperand::In { expr, .. }
|
||||
| InlineAsmOperand::InOut { expr, .. } => never_loop_expr(expr, main_loop_id),
|
||||
InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
|
||||
never_loop_expr(expr, main_loop_id)
|
||||
},
|
||||
InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter(), main_loop_id),
|
||||
InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||
never_loop_expr_all(&mut once(in_expr).chain(out_expr.iter()), main_loop_id)
|
||||
|
@ -1,6 +1,6 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{get_parent_expr, meets_msrv, msrvs};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
|
||||
@ -24,7 +24,7 @@ declare_clippy_lint! {
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// usize::BITS;
|
||||
/// usize::BITS as usize;
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub MANUAL_BITS,
|
||||
@ -59,16 +59,19 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
|
||||
if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_));
|
||||
if let ExprKind::Lit(lit) = &other_expr.kind;
|
||||
if let LitKind::Int(8, _) = lit.node;
|
||||
|
||||
then {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let ty_snip = snippet_with_applicability(cx, real_ty.span, "..", &mut app);
|
||||
let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS"));
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_BITS,
|
||||
expr.span,
|
||||
"usage of `mem::size_of::<T>()` to obtain the size of `T` in bits",
|
||||
"consider using",
|
||||
format!("{}::BITS", snippet_opt(cx, real_ty.span).unwrap()),
|
||||
Applicability::MachineApplicable,
|
||||
sugg,
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -108,3 +111,36 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, base_sugg: String) -> String {
|
||||
if let Some(parent_expr) = get_parent_expr(cx, expr) {
|
||||
if is_ty_conversion(parent_expr) {
|
||||
return base_sugg;
|
||||
}
|
||||
|
||||
// These expressions have precedence over casts, the suggestion therefore
|
||||
// needs to be wrapped into parentheses
|
||||
match parent_expr.kind {
|
||||
ExprKind::Unary(..) | ExprKind::AddrOf(..) | ExprKind::MethodCall(..) => {
|
||||
return format!("({base_sugg} as usize)");
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
format!("{base_sugg} as usize")
|
||||
}
|
||||
|
||||
fn is_ty_conversion(expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Cast(..) = expr.kind {
|
||||
true
|
||||
} else if let ExprKind::MethodCall(path, [_], _) = expr.kind
|
||||
&& path.ident.name == rustc_span::sym::try_into
|
||||
{
|
||||
// This is only called for `usize` which implements `TryInto`. Therefore,
|
||||
// we don't have to check here if `self` implements the `TryInto` trait.
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,17 @@
|
||||
use clippy_utils::attrs::is_doc_hidden;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind};
|
||||
use clippy_utils::{is_lint_allowed, meets_msrv, msrvs};
|
||||
use rustc_ast::ast::{self, VisibilityKind};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_hir::{self as hir, Expr, ExprKind, QPath};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty::DefIdTree;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::def_id::{DefId, LocalDefId};
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -58,129 +62,160 @@ declare_clippy_lint! {
|
||||
"manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ManualNonExhaustive {
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub struct ManualNonExhaustiveStruct {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl ManualNonExhaustive {
|
||||
impl ManualNonExhaustiveStruct {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
|
||||
impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]);
|
||||
|
||||
impl EarlyLintPass for ManualNonExhaustive {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub struct ManualNonExhaustiveEnum {
|
||||
msrv: Option<RustcVersion>,
|
||||
constructed_enum_variants: FxHashSet<(DefId, DefId)>,
|
||||
potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>,
|
||||
}
|
||||
|
||||
impl ManualNonExhaustiveEnum {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self {
|
||||
msrv,
|
||||
constructed_enum_variants: FxHashSet::default(),
|
||||
potential_enums: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]);
|
||||
|
||||
impl EarlyLintPass for ManualNonExhaustiveStruct {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
match &item.kind {
|
||||
ItemKind::Enum(def, _) => {
|
||||
check_manual_non_exhaustive_enum(cx, item, &def.variants);
|
||||
},
|
||||
ItemKind::Struct(variant_data, _) => {
|
||||
if let VariantData::Unit(..) = variant_data {
|
||||
return;
|
||||
}
|
||||
|
||||
check_manual_non_exhaustive_struct(cx, item, variant_data);
|
||||
},
|
||||
_ => {},
|
||||
if let ast::ItemKind::Struct(variant_data, _) = &item.kind {
|
||||
let (fields, delimiter) = match variant_data {
|
||||
ast::VariantData::Struct(fields, _) => (&**fields, '{'),
|
||||
ast::VariantData::Tuple(fields, _) => (&**fields, '('),
|
||||
ast::VariantData::Unit(_) => return,
|
||||
};
|
||||
if fields.len() <= 1 {
|
||||
return;
|
||||
}
|
||||
let mut iter = fields.iter().filter_map(|f| match f.vis.kind {
|
||||
VisibilityKind::Public => None,
|
||||
VisibilityKind::Inherited => Some(Ok(f)),
|
||||
_ => Some(Err(())),
|
||||
});
|
||||
if let Some(Ok(field)) = iter.next()
|
||||
&& iter.next().is_none()
|
||||
&& field.ty.kind.is_unit()
|
||||
&& field.ident.map_or(true, |name| name.as_str().starts_with('_'))
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MANUAL_NON_EXHAUSTIVE,
|
||||
item.span,
|
||||
"this seems like a manual implementation of the non-exhaustive pattern",
|
||||
|diag| {
|
||||
if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive))
|
||||
&& let header_span = cx.sess().source_map().span_until_char(item.span, delimiter)
|
||||
&& let Some(snippet) = snippet_opt(cx, header_span)
|
||||
{
|
||||
diag.span_suggestion(
|
||||
header_span,
|
||||
"add the attribute",
|
||||
format!("#[non_exhaustive] {}", snippet),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
}
|
||||
diag.span_help(field.span, "remove this field");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(EarlyContext);
|
||||
}
|
||||
|
||||
fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) {
|
||||
fn is_non_exhaustive_marker(variant: &Variant) -> bool {
|
||||
matches!(variant.data, VariantData::Unit(_))
|
||||
&& variant.ident.as_str().starts_with('_')
|
||||
&& is_doc_hidden(&variant.attrs)
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let hir::ItemKind::Enum(def, _) = &item.kind
|
||||
&& def.variants.len() > 1
|
||||
{
|
||||
let mut iter = def.variants.iter().filter_map(|v| {
|
||||
let id = cx.tcx.hir().local_def_id(v.id);
|
||||
(matches!(v.data, hir::VariantData::Unit(_))
|
||||
&& v.ident.as_str().starts_with('_')
|
||||
&& is_doc_hidden(cx.tcx.get_attrs(id.to_def_id())))
|
||||
.then(|| (id, v.span))
|
||||
});
|
||||
if let Some((id, span)) = iter.next()
|
||||
&& iter.next().is_none()
|
||||
{
|
||||
self.potential_enums.push((item.def_id, id, item.span, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v));
|
||||
if_chain! {
|
||||
if let Some(marker) = markers.next();
|
||||
if markers.count() == 0 && variants.len() > 1;
|
||||
then {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind
|
||||
&& let [.., name] = p.segments
|
||||
&& let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res
|
||||
&& name.ident.as_str().starts_with('_')
|
||||
{
|
||||
let variant_id = cx.tcx.parent(id);
|
||||
let enum_id = cx.tcx.parent(variant_id);
|
||||
|
||||
self.constructed_enum_variants.insert((enum_id, variant_id));
|
||||
}
|
||||
}
|
||||
|
||||
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
|
||||
for &(enum_id, _, enum_span, variant_span) in
|
||||
self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| {
|
||||
!self
|
||||
.constructed_enum_variants
|
||||
.contains(&(enum_id.to_def_id(), variant_id.to_def_id()))
|
||||
&& !is_lint_allowed(cx, MANUAL_NON_EXHAUSTIVE, cx.tcx.hir().local_def_id_to_hir_id(enum_id))
|
||||
})
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MANUAL_NON_EXHAUSTIVE,
|
||||
item.span,
|
||||
enum_span,
|
||||
"this seems like a manual implementation of the non-exhaustive pattern",
|
||||
|diag| {
|
||||
if_chain! {
|
||||
if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive));
|
||||
let header_span = cx.sess().source_map().span_until_char(item.span, '{');
|
||||
if let Some(snippet) = snippet_opt(cx, header_span);
|
||||
then {
|
||||
if !cx.tcx.adt_def(enum_id).is_variant_list_non_exhaustive()
|
||||
&& let header_span = cx.sess().source_map().span_until_char(enum_span, '{')
|
||||
&& let Some(snippet) = snippet_opt(cx, header_span)
|
||||
{
|
||||
diag.span_suggestion(
|
||||
header_span,
|
||||
"add the attribute",
|
||||
format!("#[non_exhaustive] {}", snippet),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
}
|
||||
}
|
||||
diag.span_help(marker.span, "remove this variant");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) {
|
||||
fn is_private(field: &FieldDef) -> bool {
|
||||
matches!(field.vis.kind, VisibilityKind::Inherited)
|
||||
}
|
||||
|
||||
fn is_non_exhaustive_marker(field: &FieldDef) -> bool {
|
||||
is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_'))
|
||||
}
|
||||
|
||||
fn find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span {
|
||||
let delimiter = match data {
|
||||
VariantData::Struct(..) => '{',
|
||||
VariantData::Tuple(..) => '(',
|
||||
VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"),
|
||||
};
|
||||
|
||||
cx.sess().source_map().span_until_char(item.span, delimiter)
|
||||
}
|
||||
|
||||
let fields = data.fields();
|
||||
let private_fields = fields.iter().filter(|f| is_private(f)).count();
|
||||
let public_fields = fields.iter().filter(|f| f.vis.kind.is_pub()).count();
|
||||
|
||||
if_chain! {
|
||||
if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1;
|
||||
if let Some(marker) = fields.iter().find(|f| is_non_exhaustive_marker(f));
|
||||
then {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MANUAL_NON_EXHAUSTIVE,
|
||||
item.span,
|
||||
"this seems like a manual implementation of the non-exhaustive pattern",
|
||||
|diag| {
|
||||
if_chain! {
|
||||
if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive));
|
||||
let header_span = find_header_span(cx, item, data);
|
||||
if let Some(snippet) = snippet_opt(cx, header_span);
|
||||
then {
|
||||
diag.span_suggestion(
|
||||
header_span,
|
||||
"add the attribute",
|
||||
format!("#[non_exhaustive] {}", snippet),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
}
|
||||
}
|
||||
diag.span_help(marker.span, "remove this field");
|
||||
});
|
||||
diag.span_help(variant_span, "remove this variant");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
@ -143,15 +143,11 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
|
||||
impl MapClone {
|
||||
fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let message = if is_copy {
|
||||
"you are using an explicit closure for copying elements"
|
||||
|
||||
let (message, sugg_method) = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) {
|
||||
("you are using an explicit closure for copying elements", "copied")
|
||||
} else {
|
||||
"you are using an explicit closure for cloning elements"
|
||||
};
|
||||
let sugg_method = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) {
|
||||
"copied"
|
||||
} else {
|
||||
"cloned"
|
||||
("you are using an explicit closure for cloning elements", "cloned")
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
|
@ -24,7 +24,7 @@ declare_clippy_lint! {
|
||||
/// vec.push(value)
|
||||
/// }
|
||||
///
|
||||
/// if let Some(valie) = iter.next().ok() {
|
||||
/// if let Some(value) = iter.next().ok() {
|
||||
/// vec.push(value)
|
||||
/// }
|
||||
/// ```
|
||||
@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(ok_path, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
|
||||
if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation
|
||||
if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
|
||||
if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() method use std::marker::Sized;
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result);
|
||||
if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
|
||||
|
||||
|
@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
|
||||
.map(|a| NormalizedPat::from_pat(cx, &arena, a.pat))
|
||||
.collect();
|
||||
|
||||
// The furthast forwards a pattern can move without semantic changes
|
||||
// The furthest forwards a pattern can move without semantic changes
|
||||
let forwards_blocking_idxs: Vec<_> = normalized_pats
|
||||
.iter()
|
||||
.enumerate()
|
||||
@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
|
||||
})
|
||||
.collect();
|
||||
|
||||
// The furthast backwards a pattern can move without semantic changes
|
||||
// The furthest backwards a pattern can move without semantic changes
|
||||
let backwards_blocking_idxs: Vec<_> = normalized_pats
|
||||
.iter()
|
||||
.enumerate()
|
||||
|
@ -1,4 +1,4 @@
|
||||
use clippy_utils::source::{snippet_opt, walk_span_to_context};
|
||||
use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context};
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
|
||||
use rustc_lexer::{tokenize, TokenKind};
|
||||
@ -7,7 +7,7 @@ use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{Span, SpanData, SyntaxContext};
|
||||
|
||||
mod infalliable_detructuring_match;
|
||||
mod infallible_destructuring_match;
|
||||
mod match_as_ref;
|
||||
mod match_bool;
|
||||
mod match_like_matches;
|
||||
@ -653,6 +653,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||
}
|
||||
|
||||
if let ExprKind::Match(ex, arms, source) = expr.kind {
|
||||
if !span_starts_with(cx, expr.span, "match") {
|
||||
return;
|
||||
}
|
||||
if !contains_cfg_arm(cx, expr, ex, arms) {
|
||||
if source == MatchSource::Normal {
|
||||
if !(meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO)
|
||||
@ -691,7 +694,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||
}
|
||||
|
||||
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
|
||||
self.infallible_destructuring_match_linted |= infalliable_detructuring_match::check(cx, local);
|
||||
self.infallible_destructuring_match_linted |= infallible_destructuring_match::check(cx, local);
|
||||
}
|
||||
|
||||
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
|
||||
|
@ -83,7 +83,7 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Recurrsively check for each `else if let` phrase,
|
||||
// Recursively check for each `else if let` phrase,
|
||||
if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) {
|
||||
return check_if_let(cx, nested_if_let);
|
||||
}
|
||||
@ -99,7 +99,7 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
|
||||
if let ExprKind::Path(ref qpath) = ret.kind {
|
||||
return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret);
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
return eq_expr_value(cx, if_let.let_expr, ret);
|
||||
}
|
||||
@ -118,7 +118,7 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
|
||||
}
|
||||
|
||||
/// Manually check for coercion casting by checking if the type of the match operand or let expr
|
||||
/// differs with the assigned local variable or the funtion return type.
|
||||
/// differs with the assigned local variable or the function return type.
|
||||
fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool {
|
||||
if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) {
|
||||
match p_node {
|
||||
|
@ -2,17 +2,16 @@ use super::REDUNDANT_PATTERN_MATCHING;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
|
||||
use clippy_utils::ty::needs_ordered_drop;
|
||||
use clippy_utils::{higher, match_def_path};
|
||||
use clippy_utils::{is_lang_ctor, is_trait_method, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, PollPending};
|
||||
use rustc_hir::{
|
||||
intravisit::{walk_expr, Visitor},
|
||||
Arm, Block, Expr, ExprKind, LangItem, Node, Pat, PatKind, QPath, UnOp,
|
||||
Arm, Block, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp,
|
||||
};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty};
|
||||
@ -32,59 +31,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the drop order for a type matters. Some std types implement drop solely to
|
||||
/// deallocate memory. For these types, and composites containing them, changing the drop order
|
||||
/// won't result in any observable side effects.
|
||||
fn type_needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
|
||||
}
|
||||
|
||||
fn type_needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
|
||||
if !seen.insert(ty) {
|
||||
return false;
|
||||
}
|
||||
if !ty.needs_drop(cx.tcx, cx.param_env) {
|
||||
false
|
||||
} else if !cx
|
||||
.tcx
|
||||
.lang_items()
|
||||
.drop_trait()
|
||||
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
|
||||
{
|
||||
// This type doesn't implement drop, so no side effects here.
|
||||
// Check if any component type has any.
|
||||
match ty.kind() {
|
||||
ty::Tuple(fields) => fields.iter().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
|
||||
ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, *ty, seen),
|
||||
ty::Adt(adt, subs) => adt
|
||||
.all_fields()
|
||||
.map(|f| f.ty(cx.tcx, subs))
|
||||
.any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
// Check for std types which implement drop, but only for memory allocation.
|
||||
else if is_type_diagnostic_item(cx, ty, sym::Vec)
|
||||
|| is_type_lang_item(cx, ty, LangItem::OwnedBox)
|
||||
|| is_type_diagnostic_item(cx, ty, sym::Rc)
|
||||
|| is_type_diagnostic_item(cx, ty, sym::Arc)
|
||||
|| is_type_diagnostic_item(cx, ty, sym::cstring_type)
|
||||
|| is_type_diagnostic_item(cx, ty, sym::BTreeMap)
|
||||
|| is_type_diagnostic_item(cx, ty, sym::LinkedList)
|
||||
|| match_type(cx, ty, &paths::WEAK_RC)
|
||||
|| match_type(cx, ty, &paths::WEAK_ARC)
|
||||
{
|
||||
// Check all of the generic arguments.
|
||||
if let ty::Adt(_, subs) = ty.kind() {
|
||||
subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen))
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the generic arguments out of a type
|
||||
fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
|
||||
if_chain! {
|
||||
@ -115,7 +61,7 @@ fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<
|
||||
// e.g. In `(String::new(), 0).1` the string is a temporary value.
|
||||
ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
|
||||
if !matches!(expr.kind, ExprKind::Path(_)) {
|
||||
if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
|
||||
if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
|
||||
self.res = true;
|
||||
} else {
|
||||
self.visit_expr(expr);
|
||||
@ -126,7 +72,7 @@ fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<
|
||||
// e.g. In `(vec![0])[0]` the vector is a temporary value.
|
||||
ExprKind::Index(base, index) => {
|
||||
if !matches!(base.kind, ExprKind::Path(_)) {
|
||||
if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
|
||||
if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
|
||||
self.res = true;
|
||||
} else {
|
||||
self.visit_expr(base);
|
||||
@ -143,7 +89,7 @@ fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<
|
||||
.typeck_results()
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
.map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
|
||||
if self_by_ref && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) {
|
||||
if self_by_ref && needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) {
|
||||
self.res = true;
|
||||
} else {
|
||||
self.visit_expr(self_arg);
|
||||
@ -243,7 +189,7 @@ fn find_sugg_for_if_let<'tcx>(
|
||||
// scrutinee would be, so they have to be considered as well.
|
||||
// e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
|
||||
// for the duration if body.
|
||||
let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
|
||||
let needs_drop = needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
|
||||
|
||||
// check that `while_let_on_iterator` lint does not trigger
|
||||
if_chain! {
|
||||
|
@ -14,6 +14,7 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) {
|
||||
if let ty::Adt(def, _) = ty.kind();
|
||||
if def.is_struct() || def.is_union();
|
||||
if fields.len() == def.non_enum_variant().fields.len();
|
||||
if !def.non_enum_variant().is_field_list_non_exhaustive();
|
||||
|
||||
then {
|
||||
span_lint_and_help(
|
||||
|
50
clippy_lints/src/methods/is_digit_ascii_radix.rs
Normal file
50
clippy_lints/src/methods/is_digit_ascii_radix.rs
Normal file
@ -0,0 +1,50 @@
|
||||
//! Lint for `c.is_digit(10)`
|
||||
|
||||
use super::IS_DIGIT_ASCII_RADIX;
|
||||
use clippy_utils::{
|
||||
consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs,
|
||||
source::snippet_with_applicability,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_semver::RustcVersion;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
self_arg: &'tcx Expr<'_>,
|
||||
radix: &'tcx Expr<'_>,
|
||||
msrv: Option<&RustcVersion>,
|
||||
) {
|
||||
if !meets_msrv(msrv, &msrvs::IS_ASCII_DIGIT) {
|
||||
return;
|
||||
}
|
||||
|
||||
if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_char() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(radix_val) = constant_full_int(cx, cx.typeck_results(), radix) {
|
||||
let (num, replacement) = match radix_val {
|
||||
FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"),
|
||||
FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"),
|
||||
_ => return,
|
||||
};
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
IS_DIGIT_ASCII_RADIX,
|
||||
expr.span,
|
||||
&format!("use of `char::is_digit` with literal radix of {}", num),
|
||||
"try",
|
||||
format!(
|
||||
"{}.{}()",
|
||||
snippet_with_applicability(cx, self_arg.span, "..", &mut applicability),
|
||||
replacement
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
@ -1,72 +1,47 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher::Range;
|
||||
use clippy_utils::is_integer_const;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{
|
||||
higher::{self, Range},
|
||||
SpanlessEq,
|
||||
};
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Span;
|
||||
|
||||
use super::ITER_WITH_DRAIN;
|
||||
|
||||
const DRAIN_TYPES: &[Symbol] = &[sym::Vec, sym::VecDeque];
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: &Expr<'_>) {
|
||||
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
if let Some(drained_type) = DRAIN_TYPES.iter().find(|&&sym| is_type_diagnostic_item(cx, ty, sym)) {
|
||||
// Refuse to emit `into_iter` suggestion on draining struct fields due
|
||||
// to the strong possibility of processing unmovable field.
|
||||
if let ExprKind::Field(..) = recv.kind {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(range) = higher::Range::hir(arg) {
|
||||
let left_full = match range {
|
||||
Range { start: Some(start), .. } if is_integer_const(cx, start, 0) => true,
|
||||
Range { start: None, .. } => true,
|
||||
_ => false,
|
||||
};
|
||||
let full = left_full
|
||||
&& match range {
|
||||
Range {
|
||||
end: Some(end),
|
||||
limits: RangeLimits::HalfOpen,
|
||||
..
|
||||
} => {
|
||||
// `x.drain(..x.len())` call
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(len_path, len_args, _) = end.kind;
|
||||
if len_path.ident.name == sym::len && len_args.len() == 1;
|
||||
if let ExprKind::Path(QPath::Resolved(_, drain_path)) = recv.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;
|
||||
if SpanlessEq::new(cx).eq_path(drain_path, len_path);
|
||||
then { true }
|
||||
else { false }
|
||||
}
|
||||
},
|
||||
Range {
|
||||
end: None,
|
||||
limits: RangeLimits::HalfOpen,
|
||||
..
|
||||
} => true,
|
||||
_ => false,
|
||||
};
|
||||
if full {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ITER_WITH_DRAIN,
|
||||
span.with_hi(expr.span.hi()),
|
||||
&format!("`drain(..)` used on a `{}`", drained_type),
|
||||
"try this",
|
||||
"into_iter()".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !matches!(recv.kind, ExprKind::Field(..))
|
||||
&& let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
|
||||
&& let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did())
|
||||
&& matches!(ty_name, sym::Vec | sym::VecDeque)
|
||||
&& let Some(range) = Range::hir(arg)
|
||||
&& is_full_range(cx, recv, range)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ITER_WITH_DRAIN,
|
||||
span.with_hi(expr.span.hi()),
|
||||
&format!("`drain(..)` used on a `{}`", ty_name),
|
||||
"try this",
|
||||
"into_iter()".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
fn is_full_range(cx: &LateContext<'_>, container: &Expr<'_>, range: Range<'_>) -> bool {
|
||||
range.start.map_or(true, |e| is_integer_const(cx, e, 0))
|
||||
&& range.end.map_or(true, |e| {
|
||||
if range.limits == RangeLimits::HalfOpen
|
||||
&& let ExprKind::Path(QPath::Resolved(None, container_path)) = container.kind
|
||||
&& let ExprKind::MethodCall(name, [self_arg], _) = e.kind
|
||||
&& name.ident.name == sym::len
|
||||
&& let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
|
||||
{
|
||||
container_path.res == path.res
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ mod implicit_clone;
|
||||
mod inefficient_to_string;
|
||||
mod inspect_for_each;
|
||||
mod into_iter_on_ref;
|
||||
mod is_digit_ascii_radix;
|
||||
mod iter_cloned_collect;
|
||||
mod iter_count;
|
||||
mod iter_next_slice;
|
||||
@ -42,6 +43,7 @@ mod map_flatten;
|
||||
mod map_identity;
|
||||
mod map_unwrap_or;
|
||||
mod needless_option_as_deref;
|
||||
mod needless_option_take;
|
||||
mod ok_expect;
|
||||
mod option_as_ref_deref;
|
||||
mod option_map_or_none;
|
||||
@ -294,15 +296,15 @@ declare_clippy_lint! {
|
||||
/// Checks for methods with certain name prefixes and which
|
||||
/// doesn't match how self is taken. The actual rules are:
|
||||
///
|
||||
/// |Prefix |Postfix |`self` taken | `self` type |
|
||||
/// |-------|------------|-----------------------|--------------|
|
||||
/// |`as_` | none |`&self` or `&mut self` | any |
|
||||
/// |`from_`| none | none | any |
|
||||
/// |`into_`| none |`self` | any |
|
||||
/// |`is_` | none |`&self` or none | any |
|
||||
/// |`to_` | `_mut` |`&mut self` | any |
|
||||
/// |`to_` | not `_mut` |`self` | `Copy` |
|
||||
/// |`to_` | not `_mut` |`&self` | not `Copy` |
|
||||
/// |Prefix |Postfix |`self` taken | `self` type |
|
||||
/// |-------|------------|-------------------------------|--------------|
|
||||
/// |`as_` | none |`&self` or `&mut self` | any |
|
||||
/// |`from_`| none | none | any |
|
||||
/// |`into_`| none |`self` | any |
|
||||
/// |`is_` | none |`&mut self` or `&self` or none | any |
|
||||
/// |`to_` | `_mut` |`&mut self` | any |
|
||||
/// |`to_` | not `_mut` |`self` | `Copy` |
|
||||
/// |`to_` | not `_mut` |`&self` | not `Copy` |
|
||||
///
|
||||
/// Note: Clippy doesn't trigger methods with `to_` prefix in:
|
||||
/// - Traits definition.
|
||||
@ -1265,7 +1267,7 @@ declare_clippy_lint! {
|
||||
#[clippy::version = "1.55.0"]
|
||||
pub EXTEND_WITH_DRAIN,
|
||||
perf,
|
||||
"using vec.append(&mut vec) to move the full range of a vecor to another"
|
||||
"using vec.append(&mut vec) to move the full range of a vector to another"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -2007,13 +2009,27 @@ declare_clippy_lint! {
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// let (key, value) = _.splitn(2, '=').next_tuple()?;
|
||||
/// let value = _.splitn(2, '=').nth(1)?;
|
||||
/// let s = "key=value=add";
|
||||
/// let (key, value) = s.splitn(2, '=').next_tuple()?;
|
||||
/// let value = s.splitn(2, '=').nth(1)?;
|
||||
///
|
||||
/// // Good
|
||||
/// let (key, value) = _.split_once('=')?;
|
||||
/// let value = _.split_once('=')?.1;
|
||||
/// let mut parts = s.splitn(2, '=');
|
||||
/// let key = parts.next()?;
|
||||
/// let value = parts.next()?;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// // Good
|
||||
/// let s = "key=value=add";
|
||||
/// let (key, value) = s.split_once('=')?;
|
||||
/// let value = s.split_once('=')?.1;
|
||||
///
|
||||
/// let (key, value) = s.split_once('=')?;
|
||||
/// ```
|
||||
///
|
||||
/// ### Limitations
|
||||
/// The multiple statement variant currently only detects `iter.next()?`/`iter.next().unwrap()`
|
||||
/// in two separate `let` statements that immediately follow the `splitn()`
|
||||
#[clippy::version = "1.57.0"]
|
||||
pub MANUAL_SPLIT_ONCE,
|
||||
complexity,
|
||||
@ -2099,7 +2115,7 @@ declare_clippy_lint! {
|
||||
/// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
|
||||
/// will prevent loop unrolling and will result in a negative performance impact.
|
||||
///
|
||||
/// Additionlly, differences have been observed between aarch64 and x86_64 assembly output,
|
||||
/// Additionally, differences have been observed between aarch64 and x86_64 assembly output,
|
||||
/// with aarch64 tending to producing faster assembly in more cases when using `.collect::<String>()`
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub UNNECESSARY_JOIN,
|
||||
@ -2131,6 +2147,56 @@ declare_clippy_lint! {
|
||||
"no-op use of `deref` or `deref_mut` method to `Option`."
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Finds usages of [`char::is_digit`]
|
||||
/// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
|
||||
/// can be replaced with [`is_ascii_digit`]
|
||||
/// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
|
||||
/// [`is_ascii_hexdigit`]
|
||||
/// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `is_digit(..)` is slower and requires specifying the radix.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let c: char = '6';
|
||||
/// c.is_digit(10);
|
||||
/// c.is_digit(16);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let c: char = '6';
|
||||
/// c.is_ascii_digit();
|
||||
/// c.is_ascii_hexdigit();
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub IS_DIGIT_ASCII_RADIX,
|
||||
style,
|
||||
"use of `char::is_digit(..)` with literal radix of 10 or 16"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let x = Some(3);
|
||||
/// x.as_ref().take();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let x = Some(3);
|
||||
/// x.as_ref();
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub NEEDLESS_OPTION_TAKE,
|
||||
complexity,
|
||||
"using `.as_ref().take()` on a temporary value"
|
||||
}
|
||||
|
||||
pub struct Methods {
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Option<RustcVersion>,
|
||||
@ -2219,6 +2285,8 @@ impl_lint_pass!(Methods => [
|
||||
UNNECESSARY_JOIN,
|
||||
ERR_EXPECT,
|
||||
NEEDLESS_OPTION_AS_DEREF,
|
||||
IS_DIGIT_ASCII_RADIX,
|
||||
NEEDLESS_OPTION_TAKE,
|
||||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
@ -2254,7 +2322,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
single_char_add_str::check(cx, expr, args);
|
||||
into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args);
|
||||
single_char_pattern::check(cx, expr, method_call.ident.name, args);
|
||||
unnecessary_to_owned::check(cx, expr, method_call.ident.name, args);
|
||||
unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv.as_ref());
|
||||
},
|
||||
hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
|
||||
let mut info = BinaryExprInfo {
|
||||
@ -2516,6 +2584,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
||||
},
|
||||
("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
|
||||
("is_file", []) => filetype_is_file::check(cx, expr, recv),
|
||||
("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, msrv),
|
||||
("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
|
||||
("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
|
||||
("join", [join_arg]) => {
|
||||
@ -2574,12 +2643,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
||||
("splitn" | "rsplitn", [count_arg, pat_arg]) => {
|
||||
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
|
||||
suspicious_splitn::check(cx, name, expr, recv, count);
|
||||
if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) {
|
||||
str_splitn::check_manual_split_once(cx, name, expr, recv, pat_arg);
|
||||
}
|
||||
if count >= 2 {
|
||||
str_splitn::check_needless_splitn(cx, name, expr, recv, pat_arg, count);
|
||||
}
|
||||
str_splitn::check(cx, name, expr, recv, pat_arg, count, msrv);
|
||||
}
|
||||
},
|
||||
("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
|
||||
@ -2595,6 +2659,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
||||
}
|
||||
}
|
||||
},
|
||||
("take", []) => needless_option_take::check(cx, expr, recv),
|
||||
("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
|
||||
implicit_clone::check(cx, name, expr, recv);
|
||||
},
|
||||
|
41
clippy_lints/src/methods/needless_option_take.rs
Normal file
41
clippy_lints/src/methods/needless_option_take.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::match_def_path;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::NEEDLESS_OPTION_TAKE;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
|
||||
// Checks if expression type is equal to sym::Option and if the expr is not a syntactic place
|
||||
if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) && has_expr_as_ref_path(cx, recv) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_OPTION_TAKE,
|
||||
expr.span,
|
||||
"called `Option::take()` on a temporary value",
|
||||
"try",
|
||||
format!(
|
||||
"{}",
|
||||
snippet_with_applicability(cx, recv.span, "..", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let expr_type = cx.typeck_results().expr_ty(expr);
|
||||
is_type_diagnostic_item(cx, expr_type, sym::Option)
|
||||
}
|
||||
|
||||
fn has_expr_as_ref_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let Some(ref_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
|
||||
return match_def_path(cx, ref_id, &["core", "option", "Option", "as_ref"]);
|
||||
}
|
||||
false
|
||||
}
|
@ -1,36 +1,83 @@
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::{is_diag_item_method, match_def_path, paths};
|
||||
use clippy_utils::usage::local_used_after_expr;
|
||||
use clippy_utils::visitors::expr_visitor;
|
||||
use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::{
|
||||
BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind,
|
||||
};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, adjustment::Adjust};
|
||||
use rustc_span::{symbol::sym, Span, SyntaxContext};
|
||||
use rustc_middle::ty;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_span::{sym, Span, Symbol, SyntaxContext};
|
||||
|
||||
use super::MANUAL_SPLIT_ONCE;
|
||||
use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN};
|
||||
|
||||
pub(super) fn check_manual_split_once(
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
method_name: &str,
|
||||
expr: &Expr<'_>,
|
||||
self_arg: &Expr<'_>,
|
||||
pat_arg: &Expr<'_>,
|
||||
count: u128,
|
||||
msrv: Option<&RustcVersion>,
|
||||
) {
|
||||
if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
|
||||
if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
|
||||
return;
|
||||
}
|
||||
|
||||
let ctxt = expr.span.ctxt();
|
||||
let (method_name, msg, reverse) = if method_name == "splitn" {
|
||||
("split_once", "manual implementation of `split_once`", false)
|
||||
} else {
|
||||
("rsplit_once", "manual implementation of `rsplit_once`", true)
|
||||
let needless = |usage_kind| match usage_kind {
|
||||
IterUsageKind::Nth(n) => count > n + 1,
|
||||
IterUsageKind::NextTuple => count > 2,
|
||||
};
|
||||
let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) {
|
||||
Some(x) => x,
|
||||
None => return,
|
||||
let manual = count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE);
|
||||
|
||||
match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) {
|
||||
Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg),
|
||||
Some(usage) if manual => check_manual_split_once(cx, method_name, expr, self_arg, pat_arg, &usage),
|
||||
None if manual => {
|
||||
check_manual_split_once_indirect(cx, method_name, expr, self_arg, pat_arg);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let r = if method_name == "splitn" { "" } else { "r" };
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_SPLITN,
|
||||
expr.span,
|
||||
&format!("unnecessary use of `{r}splitn`"),
|
||||
"try this",
|
||||
format!(
|
||||
"{}.{r}split({})",
|
||||
snippet_with_context(cx, self_arg.span, expr.span.ctxt(), "..", &mut app).0,
|
||||
snippet_with_context(cx, pat_arg.span, expr.span.ctxt(), "..", &mut app).0,
|
||||
),
|
||||
app,
|
||||
);
|
||||
}
|
||||
|
||||
fn check_manual_split_once(
|
||||
cx: &LateContext<'_>,
|
||||
method_name: &str,
|
||||
expr: &Expr<'_>,
|
||||
self_arg: &Expr<'_>,
|
||||
pat_arg: &Expr<'_>,
|
||||
usage: &IterUsage,
|
||||
) {
|
||||
let ctxt = expr.span.ctxt();
|
||||
let (msg, reverse) = if method_name == "splitn" {
|
||||
("manual implementation of `split_once`", false)
|
||||
} else {
|
||||
("manual implementation of `rsplit_once`", true)
|
||||
};
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
@ -39,84 +86,198 @@ pub(super) fn check_manual_split_once(
|
||||
|
||||
let sugg = match usage.kind {
|
||||
IterUsageKind::NextTuple => {
|
||||
format!("{}.{}({})", self_snip, method_name, pat_snip)
|
||||
},
|
||||
IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip),
|
||||
IterUsageKind::Next | IterUsageKind::Second => {
|
||||
let self_deref = {
|
||||
let adjust = cx.typeck_results().expr_adjustments(self_arg);
|
||||
if adjust.len() < 2 {
|
||||
String::new()
|
||||
} else if cx.typeck_results().expr_ty(self_arg).is_box()
|
||||
|| adjust
|
||||
.iter()
|
||||
.any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box())
|
||||
{
|
||||
format!("&{}", "*".repeat(adjust.len().saturating_sub(1)))
|
||||
} else {
|
||||
"*".repeat(adjust.len().saturating_sub(2))
|
||||
}
|
||||
};
|
||||
if matches!(usage.kind, IterUsageKind::Next) {
|
||||
match usage.unwrap_kind {
|
||||
Some(UnwrapKind::Unwrap) => {
|
||||
if reverse {
|
||||
format!("{}.{}({}).unwrap().0", self_snip, method_name, pat_snip)
|
||||
} else {
|
||||
format!(
|
||||
"{}.{}({}).map_or({}{}, |x| x.0)",
|
||||
self_snip, method_name, pat_snip, self_deref, &self_snip
|
||||
)
|
||||
}
|
||||
},
|
||||
Some(UnwrapKind::QuestionMark) => {
|
||||
format!(
|
||||
"{}.{}({}).map_or({}{}, |x| x.0)",
|
||||
self_snip, method_name, pat_snip, self_deref, &self_snip
|
||||
)
|
||||
},
|
||||
None => {
|
||||
format!(
|
||||
"Some({}.{}({}).map_or({}{}, |x| x.0))",
|
||||
&self_snip, method_name, pat_snip, self_deref, &self_snip
|
||||
)
|
||||
},
|
||||
}
|
||||
if reverse {
|
||||
format!("{self_snip}.rsplit_once({pat_snip}).map(|(x, y)| (y, x))")
|
||||
} else {
|
||||
match usage.unwrap_kind {
|
||||
Some(UnwrapKind::Unwrap) => {
|
||||
if reverse {
|
||||
// In this case, no better suggestion is offered.
|
||||
return;
|
||||
}
|
||||
format!("{}.{}({}).unwrap().1", self_snip, method_name, pat_snip)
|
||||
},
|
||||
Some(UnwrapKind::QuestionMark) => {
|
||||
format!("{}.{}({})?.1", self_snip, method_name, pat_snip)
|
||||
},
|
||||
None => {
|
||||
format!("{}.{}({}).map(|x| x.1)", self_snip, method_name, pat_snip)
|
||||
},
|
||||
}
|
||||
format!("{self_snip}.split_once({pat_snip})")
|
||||
}
|
||||
},
|
||||
IterUsageKind::Nth(1) => {
|
||||
let (r, field) = if reverse { ("r", 0) } else { ("", 1) };
|
||||
|
||||
match usage.unwrap_kind {
|
||||
Some(UnwrapKind::Unwrap) => {
|
||||
format!("{self_snip}.{r}split_once({pat_snip}).unwrap().{field}")
|
||||
},
|
||||
Some(UnwrapKind::QuestionMark) => {
|
||||
format!("{self_snip}.{r}split_once({pat_snip})?.{field}")
|
||||
},
|
||||
None => {
|
||||
format!("{self_snip}.{r}split_once({pat_snip}).map(|x| x.{field})")
|
||||
},
|
||||
}
|
||||
},
|
||||
IterUsageKind::Nth(_) => return,
|
||||
};
|
||||
|
||||
span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
|
||||
}
|
||||
|
||||
enum IterUsageKind {
|
||||
Next,
|
||||
Second,
|
||||
NextTuple,
|
||||
RNextTuple,
|
||||
/// checks for
|
||||
///
|
||||
/// ```
|
||||
/// let mut iter = "a.b.c".splitn(2, '.');
|
||||
/// let a = iter.next();
|
||||
/// let b = iter.next();
|
||||
/// ```
|
||||
fn check_manual_split_once_indirect(
|
||||
cx: &LateContext<'_>,
|
||||
method_name: &str,
|
||||
expr: &Expr<'_>,
|
||||
self_arg: &Expr<'_>,
|
||||
pat_arg: &Expr<'_>,
|
||||
) -> Option<()> {
|
||||
let ctxt = expr.span.ctxt();
|
||||
let mut parents = cx.tcx.hir().parent_iter(expr.hir_id);
|
||||
if let (_, Node::Local(local)) = parents.next()?
|
||||
&& let PatKind::Binding(BindingAnnotation::Mutable, iter_binding_id, iter_ident, None) = local.pat.kind
|
||||
&& let (iter_stmt_id, Node::Stmt(_)) = parents.next()?
|
||||
&& let (_, Node::Block(enclosing_block)) = parents.next()?
|
||||
|
||||
&& let mut stmts = enclosing_block
|
||||
.stmts
|
||||
.iter()
|
||||
.skip_while(|stmt| stmt.hir_id != iter_stmt_id)
|
||||
.skip(1)
|
||||
|
||||
&& let first = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)?
|
||||
&& let second = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)?
|
||||
&& first.unwrap_kind == second.unwrap_kind
|
||||
&& first.name != second.name
|
||||
&& !local_used_after_expr(cx, iter_binding_id, second.init_expr)
|
||||
{
|
||||
let (r, lhs, rhs) = if method_name == "splitn" {
|
||||
("", first.name, second.name)
|
||||
} else {
|
||||
("r", second.name, first.name)
|
||||
};
|
||||
let msg = format!("manual implementation of `{r}split_once`");
|
||||
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
|
||||
let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
|
||||
|
||||
span_lint_and_then(cx, MANUAL_SPLIT_ONCE, local.span, &msg, |diag| {
|
||||
diag.span_label(first.span, "first usage here");
|
||||
diag.span_label(second.span, "second usage here");
|
||||
|
||||
let unwrap = match first.unwrap_kind {
|
||||
UnwrapKind::Unwrap => ".unwrap()",
|
||||
UnwrapKind::QuestionMark => "?",
|
||||
};
|
||||
diag.span_suggestion_verbose(
|
||||
local.span,
|
||||
&format!("try `{r}split_once`"),
|
||||
format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"),
|
||||
app,
|
||||
);
|
||||
|
||||
let remove_msg = format!("remove the `{iter_ident}` usages");
|
||||
diag.span_suggestion(
|
||||
first.span,
|
||||
&remove_msg,
|
||||
String::new(),
|
||||
app,
|
||||
);
|
||||
diag.span_suggestion(
|
||||
second.span,
|
||||
&remove_msg,
|
||||
String::new(),
|
||||
app,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct IndirectUsage<'a> {
|
||||
name: Symbol,
|
||||
span: Span,
|
||||
init_expr: &'a Expr<'a>,
|
||||
unwrap_kind: UnwrapKind,
|
||||
}
|
||||
|
||||
/// returns `Some(IndirectUsage)` for e.g.
|
||||
///
|
||||
/// ```ignore
|
||||
/// let name = binding.next()?;
|
||||
/// let name = binding.next().unwrap();
|
||||
/// ```
|
||||
fn indirect_usage<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
stmt: &Stmt<'tcx>,
|
||||
binding: HirId,
|
||||
ctxt: SyntaxContext,
|
||||
) -> Option<IndirectUsage<'tcx>> {
|
||||
if let StmtKind::Local(Local {
|
||||
pat:
|
||||
Pat {
|
||||
kind: PatKind::Binding(BindingAnnotation::Unannotated, _, ident, None),
|
||||
..
|
||||
},
|
||||
init: Some(init_expr),
|
||||
hir_id: local_hir_id,
|
||||
..
|
||||
}) = stmt.kind
|
||||
{
|
||||
let mut path_to_binding = None;
|
||||
expr_visitor(cx, |expr| {
|
||||
if path_to_local_id(expr, binding) {
|
||||
path_to_binding = Some(expr);
|
||||
}
|
||||
|
||||
path_to_binding.is_none()
|
||||
})
|
||||
.visit_expr(init_expr);
|
||||
|
||||
let mut parents = cx.tcx.hir().parent_iter(path_to_binding?.hir_id);
|
||||
let iter_usage = parse_iter_usage(cx, ctxt, &mut parents)?;
|
||||
|
||||
let (parent_id, _) = parents.find(|(_, node)| {
|
||||
!matches!(
|
||||
node,
|
||||
Node::Expr(Expr {
|
||||
kind: ExprKind::Match(.., MatchSource::TryDesugar),
|
||||
..
|
||||
})
|
||||
)
|
||||
})?;
|
||||
|
||||
if let IterUsage {
|
||||
kind: IterUsageKind::Nth(0),
|
||||
unwrap_kind: Some(unwrap_kind),
|
||||
..
|
||||
} = iter_usage
|
||||
{
|
||||
if parent_id == *local_hir_id {
|
||||
return Some(IndirectUsage {
|
||||
name: ident.name,
|
||||
span: stmt.span,
|
||||
init_expr,
|
||||
unwrap_kind,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum IterUsageKind {
|
||||
Nth(u128),
|
||||
NextTuple,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum UnwrapKind {
|
||||
Unwrap,
|
||||
QuestionMark,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct IterUsage {
|
||||
kind: IterUsageKind,
|
||||
unwrap_kind: Option<UnwrapKind>,
|
||||
@ -128,7 +289,6 @@ fn parse_iter_usage<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
|
||||
reverse: bool,
|
||||
) -> Option<IterUsage> {
|
||||
let (kind, span) = match iter.next() {
|
||||
Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
|
||||
@ -141,13 +301,7 @@ fn parse_iter_usage<'tcx>(
|
||||
let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
|
||||
|
||||
match (name.ident.as_str(), args) {
|
||||
("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
|
||||
if reverse {
|
||||
(IterUsageKind::Second, e.span)
|
||||
} else {
|
||||
(IterUsageKind::Next, e.span)
|
||||
}
|
||||
},
|
||||
("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span),
|
||||
("next_tuple", []) => {
|
||||
return if_chain! {
|
||||
if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
|
||||
@ -157,7 +311,7 @@ fn parse_iter_usage<'tcx>(
|
||||
if subs.len() == 2;
|
||||
then {
|
||||
Some(IterUsage {
|
||||
kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple },
|
||||
kind: IterUsageKind::NextTuple,
|
||||
span: e.span,
|
||||
unwrap_kind: None
|
||||
})
|
||||
@ -185,11 +339,7 @@ fn parse_iter_usage<'tcx>(
|
||||
}
|
||||
}
|
||||
};
|
||||
match if reverse { idx ^ 1 } else { idx } {
|
||||
0 => (IterUsageKind::Next, span),
|
||||
1 => (IterUsageKind::Second, span),
|
||||
_ => return None,
|
||||
}
|
||||
(IterUsageKind::Nth(idx), span)
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
@ -238,86 +388,3 @@ fn parse_iter_usage<'tcx>(
|
||||
span,
|
||||
})
|
||||
}
|
||||
|
||||
use super::NEEDLESS_SPLITN;
|
||||
|
||||
pub(super) fn check_needless_splitn(
|
||||
cx: &LateContext<'_>,
|
||||
method_name: &str,
|
||||
expr: &Expr<'_>,
|
||||
self_arg: &Expr<'_>,
|
||||
pat_arg: &Expr<'_>,
|
||||
count: u128,
|
||||
) {
|
||||
if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
|
||||
return;
|
||||
}
|
||||
let ctxt = expr.span.ctxt();
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let (reverse, message) = if method_name == "splitn" {
|
||||
(false, "unnecessary use of `splitn`")
|
||||
} else {
|
||||
(true, "unnecessary use of `rsplitn`")
|
||||
};
|
||||
if_chain! {
|
||||
if count >= 2;
|
||||
if check_iter(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), count);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_SPLITN,
|
||||
expr.span,
|
||||
message,
|
||||
"try this",
|
||||
format!(
|
||||
"{}.{}({})",
|
||||
snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0,
|
||||
if reverse {"rsplit"} else {"split"},
|
||||
snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0
|
||||
),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_iter<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
|
||||
count: u128,
|
||||
) -> bool {
|
||||
match iter.next() {
|
||||
Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
|
||||
let (name, args) = if let ExprKind::MethodCall(name, [_, args @ ..], _) = e.kind {
|
||||
(name, args)
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
if_chain! {
|
||||
if let Some(did) = cx.typeck_results().type_dependent_def_id(e.hir_id);
|
||||
if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
|
||||
then {
|
||||
match (name.ident.as_str(), args) {
|
||||
("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
|
||||
return true;
|
||||
},
|
||||
("next_tuple", []) if count > 2 => {
|
||||
return true;
|
||||
},
|
||||
("nth", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
|
||||
if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
|
||||
if count > idx + 1 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => return false,
|
||||
};
|
||||
false
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::{
|
||||
contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs,
|
||||
};
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
|
||||
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
|
||||
@ -13,12 +15,19 @@ use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
|
||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
|
||||
use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_span::{sym, Symbol};
|
||||
use std::cmp::max;
|
||||
|
||||
use super::UNNECESSARY_TO_OWNED;
|
||||
|
||||
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) {
|
||||
pub fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
method_name: Symbol,
|
||||
args: &'tcx [Expr<'tcx>],
|
||||
msrv: Option<&RustcVersion>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let [receiver] = args;
|
||||
@ -33,7 +42,7 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name:
|
||||
if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) {
|
||||
return;
|
||||
}
|
||||
if check_into_iter_call_arg(cx, expr, method_name, receiver) {
|
||||
if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) {
|
||||
return;
|
||||
}
|
||||
check_other_call_arg(cx, expr, method_name, receiver);
|
||||
@ -178,7 +187,13 @@ fn check_addr_of_expr(
|
||||
|
||||
/// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
|
||||
/// call of a `to_owned`-like function is unnecessary.
|
||||
fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool {
|
||||
fn check_into_iter_call_arg(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
method_name: Symbol,
|
||||
receiver: &Expr<'_>,
|
||||
msrv: Option<&RustcVersion>,
|
||||
) -> bool {
|
||||
if_chain! {
|
||||
if let Some(parent) = get_parent_expr(cx, expr);
|
||||
if let Some(callee_def_id) = fn_def_id(cx, parent);
|
||||
@ -192,7 +207,7 @@ fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name:
|
||||
if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
|
||||
return true;
|
||||
}
|
||||
let cloned_or_copied = if is_copy(cx, item_ty) { "copied" } else { "cloned" };
|
||||
let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) { "copied" } else { "cloned" };
|
||||
// The next suggestion may be incorrect because the removal of the `to_owned`-like
|
||||
// function could cause the iterator to hold a reference to a resource that is used
|
||||
// mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
|
||||
|
@ -14,7 +14,7 @@ const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [
|
||||
(&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]),
|
||||
(&[Convention::StartsWith("from_")], &[SelfKind::No]),
|
||||
(&[Convention::StartsWith("into_")], &[SelfKind::Value]),
|
||||
(&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]),
|
||||
(&[Convention::StartsWith("is_")], &[SelfKind::RefMut, SelfKind::Ref, SelfKind::No]),
|
||||
(&[Convention::Eq("to_mut")], &[SelfKind::RefMut]),
|
||||
(&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]),
|
||||
|
||||
|
@ -361,7 +361,7 @@ impl MiscEarlyLints {
|
||||
// See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
|
||||
// FIXME: Find a better way to detect those cases.
|
||||
let lit_snip = match snippet_opt(cx, lit.span) {
|
||||
Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip,
|
||||
Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
|
@ -44,7 +44,7 @@ declare_clippy_lint! {
|
||||
/// pub struct PubBaz;
|
||||
/// impl PubBaz {
|
||||
/// fn private() {} // ok
|
||||
/// pub fn not_ptrivate() {} // missing #[inline]
|
||||
/// pub fn not_private() {} // missing #[inline]
|
||||
/// }
|
||||
///
|
||||
/// impl Bar for PubBaz {
|
||||
@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
|
||||
let attrs = cx.tcx.hir().attrs(it.hir_id());
|
||||
check_missing_inline_attrs(cx, attrs, it.span, desc);
|
||||
},
|
||||
hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, _bounds, trait_items) => {
|
||||
hir::ItemKind::Trait(ref _is_auto, ref _unsafe, _generics, _bounds, trait_items) => {
|
||||
// note: we need to check if the trait is exported so we can't use
|
||||
// `LateLintPass::check_trait_item` here.
|
||||
for tit in trait_items {
|
||||
|
@ -53,7 +53,7 @@ fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn suggession_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
|
||||
fn suggesstion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
|
||||
if let ExprKind::Binary(ref op, left, right) = expr.kind {
|
||||
if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) {
|
||||
let op_snippet = match op.node {
|
||||
@ -75,7 +75,7 @@ impl LateLintPass<'_> for NeedlessBitwiseBool {
|
||||
expr.span,
|
||||
"use of bitwise operator instead of lazy operator between booleans",
|
||||
|diag| {
|
||||
if let Some(sugg) = suggession_snippet(cx, expr) {
|
||||
if let Some(sugg) = suggesstion_snippet(cx, expr) {
|
||||
diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable);
|
||||
}
|
||||
},
|
||||
|
@ -1,10 +1,14 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::path_to_local;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::visitors::{expr_visitor, is_local_used};
|
||||
use rustc_errors::Applicability;
|
||||
use clippy_utils::ty::needs_ordered_drop;
|
||||
use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used};
|
||||
use rustc_errors::{Applicability, MultiSpan};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::{Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind};
|
||||
use rustc_hir::{
|
||||
BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt,
|
||||
StmtKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Span;
|
||||
@ -73,6 +77,31 @@ fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) ->
|
||||
seen
|
||||
}
|
||||
|
||||
fn contains_let(cond: &Expr<'_>) -> bool {
|
||||
let mut seen = false;
|
||||
expr_visitor_no_bodies(|expr| {
|
||||
if let ExprKind::Let(_) = expr.kind {
|
||||
seen = true;
|
||||
}
|
||||
|
||||
!seen
|
||||
})
|
||||
.visit_expr(cond);
|
||||
|
||||
seen
|
||||
}
|
||||
|
||||
fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
||||
let StmtKind::Local(local) = stmt.kind else { return false };
|
||||
!local.pat.walk_short(|pat| {
|
||||
if let PatKind::Binding(.., None) = pat.kind {
|
||||
!needs_ordered_drop(cx, cx.typeck_results().pat_ty(pat))
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LocalAssign {
|
||||
lhs_id: HirId,
|
||||
@ -187,11 +216,14 @@ fn first_usage<'tcx>(
|
||||
local_stmt_id: HirId,
|
||||
block: &'tcx Block<'tcx>,
|
||||
) -> Option<Usage<'tcx>> {
|
||||
let significant_drop = needs_ordered_drop(cx, cx.typeck_results().node_type(binding_id));
|
||||
|
||||
block
|
||||
.stmts
|
||||
.iter()
|
||||
.skip_while(|stmt| stmt.hir_id != local_stmt_id)
|
||||
.skip(1)
|
||||
.take_while(|stmt| !significant_drop || !stmt_needs_ordered_drop(cx, stmt))
|
||||
.find(|&stmt| is_local_used(cx, stmt, binding_id))
|
||||
.and_then(|stmt| match stmt.kind {
|
||||
StmtKind::Expr(expr) => Some(Usage {
|
||||
@ -235,12 +267,15 @@ fn check<'tcx>(
|
||||
match usage.expr.kind {
|
||||
ExprKind::Assign(..) => {
|
||||
let assign = LocalAssign::new(cx, usage.expr, binding_id)?;
|
||||
let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]);
|
||||
msg_span.push_span_label(local_stmt.span, "created here");
|
||||
msg_span.push_span_label(assign.span, "initialised here");
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_LATE_INIT,
|
||||
local_stmt.span,
|
||||
"unneeded late initalization",
|
||||
msg_span,
|
||||
"unneeded late initialization",
|
||||
|diag| {
|
||||
diag.tool_only_span_suggestion(
|
||||
local_stmt.span,
|
||||
@ -258,14 +293,14 @@ fn check<'tcx>(
|
||||
},
|
||||
);
|
||||
},
|
||||
ExprKind::If(_, then_expr, Some(else_expr)) => {
|
||||
ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => {
|
||||
let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?;
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_LATE_INIT,
|
||||
local_stmt.span,
|
||||
"unneeded late initalization",
|
||||
"unneeded late initialization",
|
||||
|diag| {
|
||||
diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
|
||||
|
||||
@ -296,7 +331,7 @@ fn check<'tcx>(
|
||||
cx,
|
||||
NEEDLESS_LATE_INIT,
|
||||
local_stmt.span,
|
||||
"unneeded late initalization",
|
||||
"unneeded late initialization",
|
||||
|diag| {
|
||||
diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
|
||||
|
||||
@ -333,12 +368,11 @@ fn check<'tcx>(
|
||||
impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit {
|
||||
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
|
||||
let mut parents = cx.tcx.hir().parent_iter(local.hir_id);
|
||||
|
||||
if_chain! {
|
||||
if let Local {
|
||||
init: None,
|
||||
pat: &Pat {
|
||||
kind: PatKind::Binding(_, binding_id, _, None),
|
||||
kind: PatKind::Binding(BindingAnnotation::Unannotated, binding_id, _, None),
|
||||
..
|
||||
},
|
||||
source: LocalSource::Normal,
|
||||
|
@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
if let hir::ItemKind::Impl(hir::Impl {
|
||||
of_trait: None,
|
||||
ref generics,
|
||||
generics,
|
||||
self_ty: impl_self_ty,
|
||||
items,
|
||||
..
|
||||
|
@ -197,7 +197,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
|
||||
if interned_name.chars().any(char::is_uppercase) {
|
||||
return;
|
||||
}
|
||||
if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
|
||||
if interned_name.chars().all(|c| c.is_ascii_digit() || c == '_') {
|
||||
span_lint(
|
||||
self.0.cx,
|
||||
JUST_UNDERSCORES_AND_DIGITS,
|
||||
|
@ -25,7 +25,7 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The actual meaning can be the intended one. `\x00` can be used in these
|
||||
/// cases to be unambigious.
|
||||
/// cases to be unambiguous.
|
||||
///
|
||||
/// The lint does not trigger for format strings in `print!()`, `write!()`
|
||||
/// and friends since the string is already preprocessed when Clippy lints
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_lint_allowed;
|
||||
use itertools::{izip, Itertools};
|
||||
use rustc_ast::{walk_list, Label, Mutability};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
@ -8,7 +9,7 @@ use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
|
||||
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
|
||||
use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor};
|
||||
use rustc_hir::{
|
||||
Arm, Block, Body, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, PathSegment,
|
||||
QPath, Stmt, StmtKind, TyKind, UnOp,
|
||||
@ -33,6 +34,9 @@ declare_clippy_lint! {
|
||||
/// and the assigned variables are also only in recursion, it is useless.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Too many code paths in the linting code are currently untested and prone to produce false
|
||||
/// positives or are prone to have performance implications.
|
||||
///
|
||||
/// In some cases, this would not catch all useless arguments.
|
||||
///
|
||||
/// ```rust
|
||||
@ -85,7 +89,7 @@ declare_clippy_lint! {
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub ONLY_USED_IN_RECURSION,
|
||||
complexity,
|
||||
nursery,
|
||||
"arguments that is only used in recursion can be removed"
|
||||
}
|
||||
declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
|
||||
@ -100,6 +104,9 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
|
||||
_: Span,
|
||||
id: HirId,
|
||||
) {
|
||||
if is_lint_allowed(cx, ONLY_USED_IN_RECURSION, id) {
|
||||
return;
|
||||
}
|
||||
if let FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) = kind {
|
||||
let def_id = id.owner.to_def_id();
|
||||
let data = cx.tcx.def_path(def_id).data;
|
||||
@ -145,7 +152,8 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
|
||||
is_method: matches!(kind, FnKind::Method(..)),
|
||||
has_self,
|
||||
ty_res,
|
||||
ty_ctx: cx.tcx,
|
||||
tcx: cx.tcx,
|
||||
visited_exprs: FxHashSet::default(),
|
||||
};
|
||||
|
||||
visitor.visit_expr(&body.value);
|
||||
@ -206,19 +214,13 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
|
||||
}
|
||||
|
||||
pub fn is_primitive(ty: Ty<'_>) -> bool {
|
||||
match ty.kind() {
|
||||
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
|
||||
ty::Ref(_, t, _) => is_primitive(*t),
|
||||
_ => false,
|
||||
}
|
||||
let ty = ty.peel_refs();
|
||||
ty.is_primitive() || ty.is_str()
|
||||
}
|
||||
|
||||
pub fn is_array(ty: Ty<'_>) -> bool {
|
||||
match ty.kind() {
|
||||
ty::Array(..) | ty::Slice(..) => true,
|
||||
ty::Ref(_, t, _) => is_array(*t),
|
||||
_ => false,
|
||||
}
|
||||
let ty = ty.peel_refs();
|
||||
ty.is_array() || ty.is_array_slice()
|
||||
}
|
||||
|
||||
/// This builds the graph of side effect.
|
||||
@ -250,40 +252,30 @@ pub struct SideEffectVisit<'tcx> {
|
||||
is_method: bool,
|
||||
has_self: bool,
|
||||
ty_res: &'tcx TypeckResults<'tcx>,
|
||||
ty_ctx: TyCtxt<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
visited_exprs: FxHashSet<HirId>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
|
||||
fn visit_block(&mut self, b: &'tcx Block<'tcx>) {
|
||||
b.stmts.iter().for_each(|stmt| {
|
||||
self.visit_stmt(stmt);
|
||||
self.ret_vars.clear();
|
||||
});
|
||||
walk_list!(self, visit_expr, b.expr);
|
||||
}
|
||||
|
||||
fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) {
|
||||
match s.kind {
|
||||
StmtKind::Local(Local {
|
||||
pat, init: Some(init), ..
|
||||
}) => {
|
||||
self.visit_pat_expr(pat, init, false);
|
||||
self.ret_vars.clear();
|
||||
},
|
||||
StmtKind::Item(i) => {
|
||||
let item = self.ty_ctx.hir().item(i);
|
||||
self.visit_item(item);
|
||||
self.ret_vars.clear();
|
||||
},
|
||||
StmtKind::Expr(e) | StmtKind::Semi(e) => {
|
||||
self.visit_expr(e);
|
||||
self.ret_vars.clear();
|
||||
StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => {
|
||||
walk_stmt(self, s);
|
||||
},
|
||||
StmtKind::Local(_) => {},
|
||||
}
|
||||
self.ret_vars.clear();
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
|
||||
if !self.visited_exprs.insert(ex.hir_id) {
|
||||
return;
|
||||
}
|
||||
match ex.kind {
|
||||
ExprKind::Array(exprs) | ExprKind::Tup(exprs) => {
|
||||
self.ret_vars = exprs
|
||||
@ -307,7 +299,7 @@ impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
|
||||
ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms),
|
||||
// since analysing the closure is not easy, just set all variables in it to side-effect
|
||||
ExprKind::Closure(_, _, body_id, _, _) => {
|
||||
let body = self.ty_ctx.hir().body(body_id);
|
||||
let body = self.tcx.hir().body(body_id);
|
||||
self.visit_body(body);
|
||||
let vars = std::mem::take(&mut self.ret_vars);
|
||||
self.add_side_effect(vars);
|
||||
|
@ -78,7 +78,7 @@ fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
|
||||
|
||||
/// A struct containing information about occurrences of the
|
||||
/// `if let Some(..) = .. else` construct that this lint detects.
|
||||
struct OptionIfLetElseOccurence {
|
||||
struct OptionIfLetElseOccurrence {
|
||||
option: String,
|
||||
method_sugg: String,
|
||||
some_expr: String,
|
||||
@ -100,9 +100,9 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo
|
||||
}
|
||||
|
||||
/// If this expression is the option if let/else construct we're detecting, then
|
||||
/// this function returns an `OptionIfLetElseOccurence` struct with details if
|
||||
/// this function returns an `OptionIfLetElseOccurrence` struct with details if
|
||||
/// this construct is found, or None if this construct is not found.
|
||||
fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurence> {
|
||||
fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurrence> {
|
||||
if_chain! {
|
||||
if !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly
|
||||
if !in_constant(cx, expr.hir_id);
|
||||
@ -154,7 +154,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(OptionIfLetElseOccurence {
|
||||
Some(OptionIfLetElseOccurrence {
|
||||
option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
|
||||
method_sugg: method_sugg.to_string(),
|
||||
some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")),
|
||||
|
@ -3,6 +3,7 @@
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::expr_sig;
|
||||
use clippy_utils::visitors::contains_unsafe_block;
|
||||
use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::{Applicability, MultiSpan};
|
||||
@ -10,9 +11,9 @@ use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::hir_id::HirIdMap;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir::{
|
||||
self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArg,
|
||||
self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg,
|
||||
ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn,
|
||||
TraitItem, TraitItemKind, TyKind,
|
||||
TraitItem, TraitItemKind, TyKind, Unsafety,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
@ -88,19 +89,26 @@ declare_clippy_lint! {
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This lint checks for functions that take immutable
|
||||
/// references and return mutable ones.
|
||||
/// This lint checks for functions that take immutable references and return
|
||||
/// mutable ones. This will not trigger if no unsafe code exists as there
|
||||
/// are multiple safe functions which will do this transformation
|
||||
///
|
||||
/// To be on the conservative side, if there's at least one mutable
|
||||
/// reference with the output lifetime, this lint will not trigger.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is trivially unsound, as one can create two
|
||||
/// mutable references from the same (immutable!) source.
|
||||
/// This [error](https://github.com/rust-lang/rust/issues/39465)
|
||||
/// actually lead to an interim Rust release 1.15.1.
|
||||
/// Creating a mutable reference which can be repeatably derived from an
|
||||
/// immutable reference is unsound as it allows creating multiple live
|
||||
/// mutable references to the same object.
|
||||
///
|
||||
/// This [error](https://github.com/rust-lang/rust/issues/39465) actually
|
||||
/// lead to an interim Rust release 1.15.1.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// To be on the conservative side, if there's at least one
|
||||
/// mutable reference with the output lifetime, this lint will not trigger.
|
||||
/// In practice, this case is unlikely anyway.
|
||||
/// This pattern is used by memory allocators to allow allocating multiple
|
||||
/// objects while returning mutable references to each one. So long as
|
||||
/// different mutable references are returned each time such a function may
|
||||
/// be safe.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
@ -145,7 +153,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
|
||||
return;
|
||||
}
|
||||
|
||||
check_mut_from_ref(cx, sig.decl);
|
||||
check_mut_from_ref(cx, sig, None);
|
||||
for arg in check_fn_args(
|
||||
cx,
|
||||
cx.tcx.fn_sig(item.def_id).skip_binder().inputs(),
|
||||
@ -170,10 +178,10 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
|
||||
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
|
||||
let hir = cx.tcx.hir();
|
||||
let mut parents = hir.parent_iter(body.value.hir_id);
|
||||
let (item_id, decl, is_trait_item) = match parents.next() {
|
||||
let (item_id, sig, is_trait_item) = match parents.next() {
|
||||
Some((_, Node::Item(i))) => {
|
||||
if let ItemKind::Fn(sig, ..) = &i.kind {
|
||||
(i.def_id, sig.decl, false)
|
||||
(i.def_id, sig, false)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@ -185,14 +193,14 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
|
||||
return;
|
||||
}
|
||||
if let ImplItemKind::Fn(sig, _) = &i.kind {
|
||||
(i.def_id, sig.decl, false)
|
||||
(i.def_id, sig, false)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
Some((_, Node::TraitItem(i))) => {
|
||||
if let TraitItemKind::Fn(sig, _) = &i.kind {
|
||||
(i.def_id, sig.decl, true)
|
||||
(i.def_id, sig, true)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@ -200,7 +208,8 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
|
||||
_ => return,
|
||||
};
|
||||
|
||||
check_mut_from_ref(cx, decl);
|
||||
check_mut_from_ref(cx, sig, Some(body));
|
||||
let decl = sig.decl;
|
||||
let sig = cx.tcx.fn_sig(item_id).skip_binder();
|
||||
let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params)
|
||||
.filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
|
||||
@ -473,31 +482,31 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
|
||||
})
|
||||
}
|
||||
|
||||
fn check_mut_from_ref(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
|
||||
if let FnRetTy::Return(ty) = decl.output {
|
||||
if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) {
|
||||
let mut immutables = vec![];
|
||||
for (_, mutbl, argspan) in decl
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(get_rptr_lm)
|
||||
.filter(|&(lt, _, _)| lt.name == out.name)
|
||||
{
|
||||
if mutbl == Mutability::Mut {
|
||||
return;
|
||||
}
|
||||
immutables.push(argspan);
|
||||
}
|
||||
if immutables.is_empty() {
|
||||
return;
|
||||
}
|
||||
fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) {
|
||||
if let FnRetTy::Return(ty) = sig.decl.output
|
||||
&& let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty)
|
||||
{
|
||||
let args: Option<Vec<_>> = sig
|
||||
.decl
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(get_rptr_lm)
|
||||
.filter(|&(lt, _, _)| lt.name == out.name)
|
||||
.map(|(_, mutability, span)| (mutability == Mutability::Not).then(|| span))
|
||||
.collect();
|
||||
if let Some(args) = args
|
||||
&& !args.is_empty()
|
||||
&& body.map_or(true, |body| {
|
||||
sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, &body.value)
|
||||
})
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MUT_FROM_REF,
|
||||
ty.span,
|
||||
"mutable borrow from immutable input(s)",
|
||||
|diag| {
|
||||
let ms = MultiSpan::from_spans(immutables);
|
||||
let ms = MultiSpan::from_spans(args);
|
||||
diag.span_note(ms, "immutable borrow here");
|
||||
},
|
||||
);
|
||||
|
56
clippy_lints/src/pub_use.rs
Normal file
56
clippy_lints/src/pub_use.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use rustc_ast::ast::{Item, ItemKind, VisibilityKind};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Restricts the usage of `pub use ...`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// `pub use` is usually fine, but a project may wish to limit `pub use` instances to prevent
|
||||
/// unintentional exports or to encourage placing exported items directly in public modules
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// pub mod outer {
|
||||
/// mod inner {
|
||||
/// pub struct Test {}
|
||||
/// }
|
||||
/// pub use inner::Test;
|
||||
/// }
|
||||
///
|
||||
/// use outer::Test;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// pub mod outer {
|
||||
/// pub struct Test {}
|
||||
/// }
|
||||
///
|
||||
/// use outer::Test;
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub PUB_USE,
|
||||
restriction,
|
||||
"restricts the usage of `pub use`"
|
||||
}
|
||||
declare_lint_pass!(PubUse => [PUB_USE]);
|
||||
|
||||
impl EarlyLintPass for PubUse {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if let ItemKind::Use(_) = item.kind &&
|
||||
let VisibilityKind::Public = item.vis.kind {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
PUB_USE,
|
||||
item.span,
|
||||
"using `pub use`",
|
||||
None,
|
||||
"move the exported item to a public module instead",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::def_id::CRATE_DEF_ID;
|
||||
use rustc_span::hygiene::MacroKind;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -43,8 +45,11 @@ impl_lint_pass!(RedundantPubCrate => [REDUNDANT_PUB_CRATE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()) {
|
||||
if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false) {
|
||||
if_chain! {
|
||||
if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id());
|
||||
if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false);
|
||||
if is_not_macro_export(item);
|
||||
then {
|
||||
let span = item.span.with_hi(item.ident.span.hi());
|
||||
let descr = cx.tcx.def_kind(item.def_id).descr(item.def_id.to_def_id());
|
||||
span_lint_and_then(
|
||||
@ -75,3 +80,15 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool {
|
||||
if let ItemKind::Use(path, _) = item.kind {
|
||||
if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = path.res {
|
||||
return false;
|
||||
}
|
||||
} else if let ItemKind::Macro(..) = item.kind {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
39
clippy_lints/src/renamed_lints.rs
Normal file
39
clippy_lints/src/renamed_lints.rs
Normal file
@ -0,0 +1,39 @@
|
||||
// This file is managed by `cargo dev rename_lint`. Prefer using that when possible.
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub static RENAMED_LINTS: &[(&str, &str)] = &[
|
||||
("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"),
|
||||
("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"),
|
||||
("clippy::box_vec", "clippy::box_collection"),
|
||||
("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"),
|
||||
("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"),
|
||||
("clippy::disallowed_method", "clippy::disallowed_methods"),
|
||||
("clippy::disallowed_type", "clippy::disallowed_types"),
|
||||
("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"),
|
||||
("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"),
|
||||
("clippy::identity_conversion", "clippy::useless_conversion"),
|
||||
("clippy::if_let_some_result", "clippy::match_result_ok"),
|
||||
("clippy::new_without_default_derive", "clippy::new_without_default"),
|
||||
("clippy::option_and_then_some", "clippy::bind_instead_of_map"),
|
||||
("clippy::option_expect_used", "clippy::expect_used"),
|
||||
("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"),
|
||||
("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"),
|
||||
("clippy::option_unwrap_used", "clippy::unwrap_used"),
|
||||
("clippy::ref_in_deref", "clippy::needless_borrow"),
|
||||
("clippy::result_expect_used", "clippy::expect_used"),
|
||||
("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"),
|
||||
("clippy::result_unwrap_used", "clippy::unwrap_used"),
|
||||
("clippy::single_char_push_str", "clippy::single_char_add_str"),
|
||||
("clippy::stutter", "clippy::module_name_repetitions"),
|
||||
("clippy::to_string_in_display", "clippy::recursive_format_impl"),
|
||||
("clippy::zero_width_space", "clippy::invisible_characters"),
|
||||
("clippy::drop_bounds", "drop_bounds"),
|
||||
("clippy::into_iter_on_array", "array_into_iter"),
|
||||
("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"),
|
||||
("clippy::invalid_ref", "invalid_value"),
|
||||
("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"),
|
||||
("clippy::panic_params", "non_fmt_panics"),
|
||||
("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"),
|
||||
("clippy::unknown_clippy_lints", "unknown_lints"),
|
||||
("clippy::unused_label", "unused_labels"),
|
||||
];
|
@ -51,114 +51,110 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
|
||||
let mut map = FxHashMap::<Res, ExistingName>::default();
|
||||
|
||||
for id in cx.tcx.hir().items() {
|
||||
if !matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let item = cx.tcx.hir().item(id);
|
||||
if let ItemKind::Impl(Impl {
|
||||
items,
|
||||
of_trait,
|
||||
self_ty,
|
||||
..
|
||||
}) = &item.kind
|
||||
if matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl)
|
||||
&& let item = cx.tcx.hir().item(id)
|
||||
&& let ItemKind::Impl(Impl {
|
||||
items,
|
||||
of_trait,
|
||||
self_ty,
|
||||
..
|
||||
}) = &item.kind
|
||||
&& let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
|
||||
{
|
||||
if let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind {
|
||||
if !map.contains_key(res) {
|
||||
map.insert(
|
||||
*res,
|
||||
ExistingName {
|
||||
impl_methods: BTreeMap::new(),
|
||||
trait_methods: BTreeMap::new(),
|
||||
},
|
||||
);
|
||||
}
|
||||
let existing_name = map.get_mut(res).unwrap();
|
||||
|
||||
match of_trait {
|
||||
Some(trait_ref) => {
|
||||
let mut methods_in_trait: BTreeSet<Symbol> = if_chain! {
|
||||
if let Some(Node::TraitRef(TraitRef { path, .. })) =
|
||||
cx.tcx.hir().find(trait_ref.hir_ref_id);
|
||||
if let Res::Def(DefKind::Trait, did) = path.res;
|
||||
then{
|
||||
// FIXME: if
|
||||
// `rustc_middle::ty::assoc::AssocItems::items` is public,
|
||||
// we can iterate its keys instead of `in_definition_order`,
|
||||
// which's more efficient
|
||||
cx.tcx
|
||||
.associated_items(did)
|
||||
.in_definition_order()
|
||||
.filter(|assoc_item| {
|
||||
matches!(assoc_item.kind, AssocKind::Fn)
|
||||
})
|
||||
.map(|assoc_item| assoc_item.name)
|
||||
.collect()
|
||||
}else{
|
||||
BTreeSet::new()
|
||||
}
|
||||
};
|
||||
|
||||
let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| {
|
||||
if let Some(impl_span) = existing_name.impl_methods.get(&method_name) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SAME_NAME_METHOD,
|
||||
*impl_span,
|
||||
"method's name is the same as an existing method in a trait",
|
||||
|diag| {
|
||||
diag.span_note(
|
||||
trait_method_span,
|
||||
&format!("existing `{}` defined here", method_name),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
if let Some(v) = existing_name.trait_methods.get_mut(&method_name) {
|
||||
v.push(trait_method_span);
|
||||
} else {
|
||||
existing_name.trait_methods.insert(method_name, vec![trait_method_span]);
|
||||
}
|
||||
};
|
||||
|
||||
for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
|
||||
matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
|
||||
}) {
|
||||
let method_name = impl_item_ref.ident.name;
|
||||
methods_in_trait.remove(&method_name);
|
||||
check_trait_method(method_name, impl_item_ref.span);
|
||||
}
|
||||
|
||||
for method_name in methods_in_trait {
|
||||
check_trait_method(method_name, item.span);
|
||||
}
|
||||
if !map.contains_key(res) {
|
||||
map.insert(
|
||||
*res,
|
||||
ExistingName {
|
||||
impl_methods: BTreeMap::new(),
|
||||
trait_methods: BTreeMap::new(),
|
||||
},
|
||||
None => {
|
||||
for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
|
||||
matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
|
||||
}) {
|
||||
let method_name = impl_item_ref.ident.name;
|
||||
let impl_span = impl_item_ref.span;
|
||||
if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SAME_NAME_METHOD,
|
||||
impl_span,
|
||||
"method's name is the same as an existing method in a trait",
|
||||
|diag| {
|
||||
// TODO should we `span_note` on every trait?
|
||||
// iterate on trait_spans?
|
||||
diag.span_note(
|
||||
trait_spans[0],
|
||||
&format!("existing `{}` defined here", method_name),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
existing_name.impl_methods.insert(method_name, impl_span);
|
||||
);
|
||||
}
|
||||
let existing_name = map.get_mut(res).unwrap();
|
||||
|
||||
match of_trait {
|
||||
Some(trait_ref) => {
|
||||
let mut methods_in_trait: BTreeSet<Symbol> = if_chain! {
|
||||
if let Some(Node::TraitRef(TraitRef { path, .. })) =
|
||||
cx.tcx.hir().find(trait_ref.hir_ref_id);
|
||||
if let Res::Def(DefKind::Trait, did) = path.res;
|
||||
then{
|
||||
// FIXME: if
|
||||
// `rustc_middle::ty::assoc::AssocItems::items` is public,
|
||||
// we can iterate its keys instead of `in_definition_order`,
|
||||
// which's more efficient
|
||||
cx.tcx
|
||||
.associated_items(did)
|
||||
.in_definition_order()
|
||||
.filter(|assoc_item| {
|
||||
matches!(assoc_item.kind, AssocKind::Fn)
|
||||
})
|
||||
.map(|assoc_item| assoc_item.name)
|
||||
.collect()
|
||||
}else{
|
||||
BTreeSet::new()
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| {
|
||||
if let Some(impl_span) = existing_name.impl_methods.get(&method_name) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SAME_NAME_METHOD,
|
||||
*impl_span,
|
||||
"method's name is the same as an existing method in a trait",
|
||||
|diag| {
|
||||
diag.span_note(
|
||||
trait_method_span,
|
||||
&format!("existing `{}` defined here", method_name),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
if let Some(v) = existing_name.trait_methods.get_mut(&method_name) {
|
||||
v.push(trait_method_span);
|
||||
} else {
|
||||
existing_name.trait_methods.insert(method_name, vec![trait_method_span]);
|
||||
}
|
||||
};
|
||||
|
||||
for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
|
||||
matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
|
||||
}) {
|
||||
let method_name = impl_item_ref.ident.name;
|
||||
methods_in_trait.remove(&method_name);
|
||||
check_trait_method(method_name, impl_item_ref.span);
|
||||
}
|
||||
|
||||
for method_name in methods_in_trait {
|
||||
check_trait_method(method_name, item.span);
|
||||
}
|
||||
},
|
||||
None => {
|
||||
for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
|
||||
matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
|
||||
}) {
|
||||
let method_name = impl_item_ref.ident.name;
|
||||
let impl_span = impl_item_ref.span;
|
||||
if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SAME_NAME_METHOD,
|
||||
impl_span,
|
||||
"method's name is the same as an existing method in a trait",
|
||||
|diag| {
|
||||
// TODO should we `span_note` on every trait?
|
||||
// iterate on trait_spans?
|
||||
diag.span_note(
|
||||
trait_spans[0],
|
||||
&format!("existing `{}` defined here", method_name),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
existing_name.impl_methods.insert(method_name, impl_span);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,15 +9,25 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// When sorting primitive values (integers, bools, chars, as well
|
||||
/// as arrays, slices, and tuples of such items), it is better to
|
||||
/// as arrays, slices, and tuples of such items), it is typically better to
|
||||
/// use an unstable sort than a stable sort.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using a stable sort consumes more memory and cpu cycles. Because
|
||||
/// values which compare equal are identical, preserving their
|
||||
/// Typically, using a stable sort consumes more memory and cpu cycles.
|
||||
/// Because values which compare equal are identical, preserving their
|
||||
/// relative order (the guarantee that a stable sort provides) means
|
||||
/// nothing, while the extra costs still apply.
|
||||
///
|
||||
/// ### Known problems
|
||||
///
|
||||
/// As pointed out in
|
||||
/// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241),
|
||||
/// a stable sort can instead be significantly faster for certain scenarios
|
||||
/// (eg. when a sorted vector is extended with new data and resorted).
|
||||
///
|
||||
/// For more information and benchmarking results, please refer to the
|
||||
/// issue linked above.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let mut vec = vec![2, 1, 3];
|
||||
@ -30,7 +40,7 @@ declare_clippy_lint! {
|
||||
/// ```
|
||||
#[clippy::version = "1.47.0"]
|
||||
pub STABLE_SORT_PRIMITIVE,
|
||||
perf,
|
||||
pedantic,
|
||||
"use of sort() when sort_unstable() is equivalent"
|
||||
}
|
||||
|
||||
@ -126,7 +136,7 @@ impl LateLintPass<'_> for StableSortPrimitive {
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
diag.note(
|
||||
"an unstable sort would perform faster without any observable difference for this data type",
|
||||
"an unstable sort typically performs faster without any observable difference for this data type",
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -5,6 +5,7 @@ use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method
|
||||
use clippy_utils::{peel_blocks, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
@ -451,3 +452,58 @@ impl<'tcx> LateLintPass<'tcx> for StringToString {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Warns about calling `str::trim` (or variants) before `str::split_whitespace`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `split_whitespace` already ignores leading and trailing whitespace.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// " A B C ".trim().split_whitespace();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// " A B C ".split_whitespace();
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub TRIM_SPLIT_WHITESPACE,
|
||||
style,
|
||||
"using `str::trim()` or alike before `str::split_whitespace`"
|
||||
}
|
||||
declare_lint_pass!(TrimSplitWhitespace => [TRIM_SPLIT_WHITESPACE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
||||
let tyckres = cx.typeck_results();
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, [split_recv], split_ws_span) = expr.kind;
|
||||
if path.ident.name == sym!(split_whitespace);
|
||||
if let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id);
|
||||
if cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id);
|
||||
if let ExprKind::MethodCall(path, [_trim_recv], trim_span) = split_recv.kind;
|
||||
if let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str();
|
||||
if let Some(trim_def_id) = tyckres.type_dependent_def_id(split_recv.hir_id);
|
||||
if is_one_of_trim_diagnostic_items(cx, trim_def_id);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
TRIM_SPLIT_WHITESPACE,
|
||||
trim_span.with_hi(split_ws_span.lo()),
|
||||
&format!("found call to `str::{}` before `str::split_whitespace`", trim_fn_name),
|
||||
&format!("remove `{}()`", trim_fn_name),
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_one_of_trim_diagnostic_items(cx: &LateContext<'_>, trim_def_id: DefId) -> bool {
|
||||
cx.tcx.is_diagnostic_item(sym::str_trim, trim_def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::str_trim_start, trim_def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::str_trim_end, trim_def_id)
|
||||
}
|
||||
|
@ -550,7 +550,7 @@ fn ident_difference_expr_with_base_location(
|
||||
// IdentIter, then the output of this function will be almost always be correct
|
||||
// in practice.
|
||||
//
|
||||
// If it turns out that problematic cases are more prelavent than we assume,
|
||||
// If it turns out that problematic cases are more prevalent than we assume,
|
||||
// then we should be able to change this function to do the correct traversal,
|
||||
// without needing to change the rest of the code.
|
||||
|
||||
|
@ -13,6 +13,7 @@ use rustc_hir::{
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
use std::fmt::Write as _;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -34,7 +35,7 @@ declare_clippy_lint! {
|
||||
/// ```
|
||||
#[clippy::version = "1.38.0"]
|
||||
pub TYPE_REPETITION_IN_BOUNDS,
|
||||
pedantic,
|
||||
nursery,
|
||||
"Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
|
||||
}
|
||||
|
||||
@ -64,7 +65,7 @@ declare_clippy_lint! {
|
||||
/// ```
|
||||
#[clippy::version = "1.47.0"]
|
||||
pub TRAIT_DUPLICATION_IN_BOUNDS,
|
||||
pedantic,
|
||||
nursery,
|
||||
"Check if the same trait bounds are specified twice during a function declaration"
|
||||
}
|
||||
|
||||
@ -182,19 +183,19 @@ impl TraitBounds {
|
||||
for b in v.iter() {
|
||||
if let GenericBound::Trait(ref poly_trait_ref, _) = b {
|
||||
let path = &poly_trait_ref.trait_ref.path;
|
||||
hint_string.push_str(&format!(
|
||||
let _ = write!(hint_string,
|
||||
" {} +",
|
||||
snippet_with_applicability(cx, path.span, "..", &mut applicability)
|
||||
));
|
||||
);
|
||||
}
|
||||
}
|
||||
for b in p.bounds.iter() {
|
||||
if let GenericBound::Trait(ref poly_trait_ref, _) = b {
|
||||
let path = &poly_trait_ref.trait_ref.path;
|
||||
hint_string.push_str(&format!(
|
||||
let _ = write!(hint_string,
|
||||
" {} +",
|
||||
snippet_with_applicability(cx, path.span, "..", &mut applicability)
|
||||
));
|
||||
);
|
||||
}
|
||||
}
|
||||
hint_string.truncate(hint_string.len() - 2);
|
||||
@ -241,7 +242,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
|
||||
);
|
||||
}
|
||||
else {
|
||||
trait_resolutions_direct.push((res_where, span_where))
|
||||
trait_resolutions_direct.push((res_where, span_where));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
use clippy_utils::last_path_segment;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_normalizable;
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Expr, GenericArg, QPath, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, cast::CastKind, Ty};
|
||||
use rustc_middle::ty::{cast::CastKind, Ty};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited};
|
||||
|
||||
@ -34,15 +33,12 @@ pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty
|
||||
// check if the component types of the transmuted collection and the result have different ABI,
|
||||
// size or alignment
|
||||
pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
|
||||
let empty_param_env = ty::ParamEnv::empty();
|
||||
// check if `from` and `to` are normalizable to avoid ICE (#4968)
|
||||
if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) {
|
||||
return false;
|
||||
}
|
||||
let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from));
|
||||
let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to));
|
||||
if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) {
|
||||
from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi
|
||||
if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from)
|
||||
&& let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to)
|
||||
&& let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from))
|
||||
&& let Ok(to_layout) = cx.tcx.layout_of(cx.param_env.and(to))
|
||||
{
|
||||
from_layout.size != to_layout.size || from_layout.align.abi != to_layout.align.abi
|
||||
} else {
|
||||
// no idea about layout, so don't lint
|
||||
false
|
||||
@ -91,7 +87,7 @@ fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>
|
||||
let res = check.do_check(&fn_ctxt);
|
||||
|
||||
// do_check's documentation says that it might return Ok and create
|
||||
// errors in the fcx instead of returing Err in some cases. Those cases
|
||||
// errors in the fcx instead of returning Err in some cases. Those cases
|
||||
// should be filtered out before getting here.
|
||||
assert!(
|
||||
!fn_ctxt.errors_reported_since_creation(),
|
||||
|
@ -432,8 +432,8 @@ impl Types {
|
||||
fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, context: CheckTyContext) {
|
||||
// Ignore functions in trait implementations as they are usually forced by the trait definition.
|
||||
//
|
||||
// FIXME: idially we would like to warn *if the compicated type can be simplified*, but it's hard to
|
||||
// check.
|
||||
// FIXME: ideally we would like to warn *if the complicated type can be simplified*, but it's hard
|
||||
// to check.
|
||||
if context.is_in_trait_impl {
|
||||
return;
|
||||
}
|
||||
|
@ -156,8 +156,9 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) ->
|
||||
.array_windows::<2>()
|
||||
.rev()
|
||||
.map_while(|[start, end]| {
|
||||
src.get(start.to_usize() - offset..end.to_usize() - offset)
|
||||
.map(|text| (start.to_usize(), text.trim_start()))
|
||||
let start = start.to_usize() - offset;
|
||||
let end = end.to_usize() - offset;
|
||||
src.get(start..end).map(|text| (start, text.trim_start()))
|
||||
})
|
||||
.filter(|(_, text)| !text.is_empty());
|
||||
|
||||
@ -182,7 +183,7 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) ->
|
||||
let (mut line_start, mut line) = (line_start, line);
|
||||
loop {
|
||||
if line.starts_with("/*") {
|
||||
let src = src[line_start..line_starts.last().unwrap().to_usize()].trim_start();
|
||||
let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start();
|
||||
let mut tokens = tokenize(src);
|
||||
return src[..tokens.next().unwrap().len]
|
||||
.to_ascii_uppercase()
|
||||
|
@ -1,18 +1,46 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_with_macro_callsite;
|
||||
use clippy_utils::visitors::for_each_value_source;
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Stmt, StmtKind};
|
||||
use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, Ty, TypeFoldable, TypeVisitor};
|
||||
|
||||
use super::LET_UNIT_VALUE;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
|
||||
if let StmtKind::Local(local) = stmt.kind {
|
||||
if cx.typeck_results().pat_ty(local.pat).is_unit() {
|
||||
if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() {
|
||||
return;
|
||||
if let StmtKind::Local(local) = stmt.kind
|
||||
&& let Some(init) = local.init
|
||||
&& !local.pat.span.from_expansion()
|
||||
&& !in_external_macro(cx.sess(), stmt.span)
|
||||
&& cx.typeck_results().pat_ty(local.pat).is_unit()
|
||||
{
|
||||
let needs_inferred = for_each_value_source(init, &mut |e| if needs_inferred_result_ty(cx, e) {
|
||||
ControlFlow::Continue(())
|
||||
} else {
|
||||
ControlFlow::Break(())
|
||||
}).is_continue();
|
||||
|
||||
if needs_inferred {
|
||||
if !matches!(local.pat.kind, PatKind::Wild) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
LET_UNIT_VALUE,
|
||||
stmt.span,
|
||||
"this let-binding has unit value",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
local.pat.span,
|
||||
"use a wild (`_`) binding",
|
||||
"_",
|
||||
Applicability::MaybeIncorrect, // snippet
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
LET_UNIT_VALUE,
|
||||
@ -33,3 +61,45 @@ pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn needs_inferred_result_ty(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||
let id = match e.kind {
|
||||
ExprKind::Call(
|
||||
Expr {
|
||||
kind: ExprKind::Path(ref path),
|
||||
hir_id,
|
||||
..
|
||||
},
|
||||
_,
|
||||
) => cx.qpath_res(path, *hir_id).opt_def_id(),
|
||||
ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(e.hir_id),
|
||||
_ => return false,
|
||||
};
|
||||
if let Some(id) = id
|
||||
&& let sig = cx.tcx.fn_sig(id).skip_binder()
|
||||
&& let ty::Param(output_ty) = *sig.output().kind()
|
||||
{
|
||||
sig.inputs().iter().all(|&ty| !ty_contains_param(ty, output_ty.index))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn ty_contains_param(ty: Ty<'_>, index: u32) -> bool {
|
||||
struct Visitor(u32);
|
||||
impl<'tcx> TypeVisitor<'tcx> for Visitor {
|
||||
type BreakTy = ();
|
||||
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
if let ty::Param(ty) = *ty.kind() {
|
||||
if ty.index == self.0 {
|
||||
ControlFlow::BREAK
|
||||
} else {
|
||||
ControlFlow::CONTINUE
|
||||
}
|
||||
} else {
|
||||
ty.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
ty.visit_with(&mut Visitor(index)).is_break()
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ declare_clippy_lint! {
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub LET_UNIT_VALUE,
|
||||
pedantic,
|
||||
style,
|
||||
"creating a `let` binding to a value of unit type, which usually can't be used afterwards"
|
||||
}
|
||||
|
||||
|
81
clippy_lints/src/unnecessary_owned_empty_strings.rs
Normal file
81
clippy_lints/src/unnecessary_owned_empty_strings.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item};
|
||||
use clippy_utils::{match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Detects cases of owned empty strings being passed as an argument to a function expecting `&str`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// This results in longer and less readable code
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// vec!["1", "2", "3"].join(&String::new());
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// vec!["1", "2", "3"].join("");
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub UNNECESSARY_OWNED_EMPTY_STRINGS,
|
||||
style,
|
||||
"detects cases of references to owned empty strings being passed as an argument to a function expecting `&str`"
|
||||
}
|
||||
declare_lint_pass!(UnnecessaryOwnedEmptyStrings => [UNNECESSARY_OWNED_EMPTY_STRINGS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind;
|
||||
if let ExprKind::Call(fun, args) = inner_expr.kind;
|
||||
if let ExprKind::Path(ref qpath) = fun.kind;
|
||||
if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
|
||||
if let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
|
||||
if inner_str.is_str();
|
||||
then {
|
||||
if match_def_path(cx, fun_def_id, &paths::STRING_NEW) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_OWNED_EMPTY_STRINGS,
|
||||
expr.span,
|
||||
"usage of `&String::new()` for a function expecting a `&str` argument",
|
||||
"try",
|
||||
"\"\"".to_owned(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
if_chain! {
|
||||
if match_def_path(cx, fun_def_id, &paths::FROM_FROM);
|
||||
if let [.., last_arg] = args;
|
||||
if let ExprKind::Lit(spanned) = &last_arg.kind;
|
||||
if let LitKind::Str(symbol, _) = spanned.node;
|
||||
if symbol.is_empty();
|
||||
let inner_expr_type = cx.typeck_results().expr_ty(inner_expr);
|
||||
if is_type_diagnostic_item(cx, inner_expr_type, sym::String);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_OWNED_EMPTY_STRINGS,
|
||||
expr.span,
|
||||
"usage of `&String::from(\"\")` for a function expecting a `&str` argument",
|
||||
"try",
|
||||
"\"\"".to_owned(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{meets_msrv, msrvs, over};
|
||||
use rustc_ast::mut_visit::*;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
|
||||
use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
@ -25,7 +25,7 @@ declare_clippy_lint! {
|
||||
/// *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// In the example above, `Some` is repeated, which unncessarily complicates the pattern.
|
||||
/// In the example above, `Some` is repeated, which unnecessarily complicates the pattern.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
@ -230,6 +230,10 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize)
|
||||
// with which a pattern `C(p_0)` may be formed,
|
||||
// which we would want to join with other `C(p_j)`s.
|
||||
Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_)
|
||||
// Skip immutable refs, as grouping them saves few characters,
|
||||
// and almost always requires adding parens (increasing noisiness).
|
||||
// In the case of only two patterns, replacement adds net characters.
|
||||
| Ref(_, Mutability::Not)
|
||||
// Dealt with elsewhere.
|
||||
| Or(_) | Paren(_) => false,
|
||||
// Transform `box x | ... | box y` into `box (x | y)`.
|
||||
@ -241,10 +245,10 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize)
|
||||
|k| matches!(k, Box(_)),
|
||||
|k| always_pat!(k, Box(p) => p),
|
||||
),
|
||||
// Transform `&m x | ... | &m y` into `&m (x | y)`.
|
||||
Ref(target, m1) => extend_with_matching(
|
||||
// Transform `&mut x | ... | &mut y` into `&mut (x | y)`.
|
||||
Ref(target, Mutability::Mut) => extend_with_matching(
|
||||
target, start, alternatives,
|
||||
|k| matches!(k, Ref(_, m2) if m1 == m2), // Mutabilities must match.
|
||||
|k| matches!(k, Ref(_, Mutability::Mut)),
|
||||
|k| always_pat!(k, Ref(p, _) => p),
|
||||
),
|
||||
// Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`.
|
||||
|
@ -30,7 +30,7 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Known problems
|
||||
/// - Unaddressed false negative in fn bodies of trait implementations
|
||||
/// - False positive with assotiated types in traits (#4140)
|
||||
/// - False positive with associated types in traits (#4140)
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
|
@ -70,7 +70,7 @@ macro_rules! bind {
|
||||
};
|
||||
}
|
||||
|
||||
/// Transforms the given `Option<T>` varibles into `OptionPat<Binding<T>>`.
|
||||
/// Transforms the given `Option<T>` variables into `OptionPat<Binding<T>>`.
|
||||
/// This displays as `Some($name)` or `None` when printed. The name of the inner binding
|
||||
/// is set to the name of the variable passed to the macro.
|
||||
macro_rules! opt_bind {
|
||||
|
@ -310,6 +310,12 @@ define_Conf! {
|
||||
/// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed.
|
||||
/// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
|
||||
(max_suggested_slice_pattern_length: u64 = 3),
|
||||
/// Lint: AWAIT_HOLDING_INVALID_TYPE
|
||||
(await_holding_invalid_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()),
|
||||
/// Lint: LARGE_INCLUDE_FILE.
|
||||
///
|
||||
/// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
|
||||
(max_include_file_size: u64 = 1_000_000),
|
||||
}
|
||||
|
||||
/// Search for the configuration file.
|
||||
|
55
clippy_lints/src/utils/dump_hir.rs
Normal file
55
clippy_lints/src/utils/dump_hir.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use clippy_utils::get_attr;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// It formats the attached node with `{:#?}` and writes the result to the
|
||||
/// standard output. This is intended for debugging.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```rs
|
||||
/// #[clippy::dump]
|
||||
/// use std::mem;
|
||||
///
|
||||
/// #[clippy::dump]
|
||||
/// fn foo(input: u32) -> u64 {
|
||||
/// input as u64
|
||||
/// }
|
||||
/// ```
|
||||
pub DUMP_HIR,
|
||||
internal_warn,
|
||||
"helper to dump info about code"
|
||||
}
|
||||
|
||||
declare_lint_pass!(DumpHir => [DUMP_HIR]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DumpHir {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
if has_attr(cx, item.hir_id()) {
|
||||
println!("{item:#?}");
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if has_attr(cx, expr.hir_id) {
|
||||
println!("{expr:#?}");
|
||||
}
|
||||
}
|
||||
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
|
||||
match stmt.kind {
|
||||
hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return,
|
||||
_ => {},
|
||||
}
|
||||
if has_attr(cx, stmt.hir_id) {
|
||||
println!("{stmt:#?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
|
||||
let attrs = cx.tcx.hir().attrs(hir_id);
|
||||
get_attr(cx.sess(), attrs, "dump").count() > 0
|
||||
}
|
@ -1,577 +0,0 @@
|
||||
//! checks for attributes
|
||||
|
||||
use clippy_utils::get_attr;
|
||||
use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece};
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::Session;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Dumps every ast/hir node which has the `#[clippy::dump]`
|
||||
/// attribute
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// #[clippy::dump]
|
||||
/// extern crate foo;
|
||||
/// ```
|
||||
///
|
||||
/// prints
|
||||
///
|
||||
/// ```text
|
||||
/// item `foo`
|
||||
/// visibility inherited from outer item
|
||||
/// extern crate dylib source: "/path/to/foo.so"
|
||||
/// ```
|
||||
pub DEEP_CODE_INSPECTION,
|
||||
internal_warn,
|
||||
"helper to dump info about code"
|
||||
}
|
||||
|
||||
declare_lint_pass!(DeepCodeInspector => [DEEP_CODE_INSPECTION]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) {
|
||||
return;
|
||||
}
|
||||
print_item(cx, item);
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
|
||||
if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) {
|
||||
return;
|
||||
}
|
||||
println!("impl item `{}`", item.ident.name);
|
||||
match cx.tcx.visibility(item.def_id) {
|
||||
ty::Visibility::Public => println!("public"),
|
||||
ty::Visibility::Restricted(def_id) => {
|
||||
if def_id.is_top_level_module() {
|
||||
println!("visible crate wide")
|
||||
} else {
|
||||
println!("visible in module `{}`", cx.tcx.def_path_str(def_id))
|
||||
}
|
||||
},
|
||||
ty::Visibility::Invisible => println!("invisible"),
|
||||
}
|
||||
match item.kind {
|
||||
hir::ImplItemKind::Const(_, body_id) => {
|
||||
println!("associated constant");
|
||||
print_expr(cx, &cx.tcx.hir().body(body_id).value, 1);
|
||||
},
|
||||
hir::ImplItemKind::Fn(..) => println!("method"),
|
||||
hir::ImplItemKind::TyAlias(_) => println!("associated type"),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if !has_attr(cx.sess(), cx.tcx.hir().attrs(expr.hir_id)) {
|
||||
return;
|
||||
}
|
||||
print_expr(cx, expr, 0);
|
||||
}
|
||||
|
||||
fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) {
|
||||
if !has_attr(cx.sess(), cx.tcx.hir().attrs(arm.hir_id)) {
|
||||
return;
|
||||
}
|
||||
print_pat(cx, arm.pat, 1);
|
||||
if let Some(ref guard) = arm.guard {
|
||||
println!("guard:");
|
||||
print_guard(cx, guard, 1);
|
||||
}
|
||||
println!("body:");
|
||||
print_expr(cx, arm.body, 1);
|
||||
}
|
||||
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
|
||||
if !has_attr(cx.sess(), cx.tcx.hir().attrs(stmt.hir_id)) {
|
||||
return;
|
||||
}
|
||||
match stmt.kind {
|
||||
hir::StmtKind::Local(local) => {
|
||||
println!("local variable of type {}", cx.typeck_results().node_type(local.hir_id));
|
||||
println!("pattern:");
|
||||
print_pat(cx, local.pat, 0);
|
||||
if let Some(e) = local.init {
|
||||
println!("init expression:");
|
||||
print_expr(cx, e, 0);
|
||||
}
|
||||
},
|
||||
hir::StmtKind::Item(_) => println!("item decl"),
|
||||
hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) => print_expr(cx, e, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool {
|
||||
get_attr(sess, attrs, "dump").count() > 0
|
||||
}
|
||||
|
||||
#[allow(clippy::similar_names)]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) {
|
||||
let ind = " ".repeat(indent);
|
||||
println!("{}+", ind);
|
||||
println!("{}ty: {}", ind, cx.typeck_results().expr_ty(expr));
|
||||
println!(
|
||||
"{}adjustments: {:?}",
|
||||
ind,
|
||||
cx.typeck_results().adjustments().get(expr.hir_id)
|
||||
);
|
||||
match expr.kind {
|
||||
hir::ExprKind::Box(e) => {
|
||||
println!("{}Box", ind);
|
||||
print_expr(cx, e, indent + 1);
|
||||
},
|
||||
hir::ExprKind::Array(v) => {
|
||||
println!("{}Array", ind);
|
||||
for e in v {
|
||||
print_expr(cx, e, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Call(func, args) => {
|
||||
println!("{}Call", ind);
|
||||
println!("{}function:", ind);
|
||||
print_expr(cx, func, indent + 1);
|
||||
println!("{}arguments:", ind);
|
||||
for arg in args {
|
||||
print_expr(cx, arg, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Let(hir::Let { pat, init, ty, .. }) => {
|
||||
print_pat(cx, pat, indent + 1);
|
||||
if let Some(ty) = ty {
|
||||
println!("{} type annotation: {:?}", ind, ty);
|
||||
}
|
||||
print_expr(cx, init, indent + 1);
|
||||
},
|
||||
hir::ExprKind::MethodCall(path, args, _) => {
|
||||
println!("{}MethodCall", ind);
|
||||
println!("{}method name: {}", ind, path.ident.name);
|
||||
for arg in args {
|
||||
print_expr(cx, arg, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Tup(v) => {
|
||||
println!("{}Tup", ind);
|
||||
for e in v {
|
||||
print_expr(cx, e, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Binary(op, lhs, rhs) => {
|
||||
println!("{}Binary", ind);
|
||||
println!("{}op: {:?}", ind, op.node);
|
||||
println!("{}lhs:", ind);
|
||||
print_expr(cx, lhs, indent + 1);
|
||||
println!("{}rhs:", ind);
|
||||
print_expr(cx, rhs, indent + 1);
|
||||
},
|
||||
hir::ExprKind::Unary(op, inner) => {
|
||||
println!("{}Unary", ind);
|
||||
println!("{}op: {:?}", ind, op);
|
||||
print_expr(cx, inner, indent + 1);
|
||||
},
|
||||
hir::ExprKind::Lit(ref lit) => {
|
||||
println!("{}Lit", ind);
|
||||
println!("{}{:?}", ind, lit);
|
||||
},
|
||||
hir::ExprKind::Cast(e, target) => {
|
||||
println!("{}Cast", ind);
|
||||
print_expr(cx, e, indent + 1);
|
||||
println!("{}target type: {:?}", ind, target);
|
||||
},
|
||||
hir::ExprKind::Type(e, target) => {
|
||||
println!("{}Type", ind);
|
||||
print_expr(cx, e, indent + 1);
|
||||
println!("{}target type: {:?}", ind, target);
|
||||
},
|
||||
hir::ExprKind::Loop(..) => {
|
||||
println!("{}Loop", ind);
|
||||
},
|
||||
hir::ExprKind::If(cond, _, ref else_opt) => {
|
||||
println!("{}If", ind);
|
||||
println!("{}condition:", ind);
|
||||
print_expr(cx, cond, indent + 1);
|
||||
if let Some(els) = *else_opt {
|
||||
println!("{}else:", ind);
|
||||
print_expr(cx, els, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Match(cond, _, ref source) => {
|
||||
println!("{}Match", ind);
|
||||
println!("{}condition:", ind);
|
||||
print_expr(cx, cond, indent + 1);
|
||||
println!("{}source: {:?}", ind, source);
|
||||
},
|
||||
hir::ExprKind::Closure(ref clause, _, _, _, _) => {
|
||||
println!("{}Closure", ind);
|
||||
println!("{}clause: {:?}", ind, clause);
|
||||
},
|
||||
hir::ExprKind::Yield(sub, _) => {
|
||||
println!("{}Yield", ind);
|
||||
print_expr(cx, sub, indent + 1);
|
||||
},
|
||||
hir::ExprKind::Block(_, _) => {
|
||||
println!("{}Block", ind);
|
||||
},
|
||||
hir::ExprKind::Assign(lhs, rhs, _) => {
|
||||
println!("{}Assign", ind);
|
||||
println!("{}lhs:", ind);
|
||||
print_expr(cx, lhs, indent + 1);
|
||||
println!("{}rhs:", ind);
|
||||
print_expr(cx, rhs, indent + 1);
|
||||
},
|
||||
hir::ExprKind::AssignOp(ref binop, lhs, rhs) => {
|
||||
println!("{}AssignOp", ind);
|
||||
println!("{}op: {:?}", ind, binop.node);
|
||||
println!("{}lhs:", ind);
|
||||
print_expr(cx, lhs, indent + 1);
|
||||
println!("{}rhs:", ind);
|
||||
print_expr(cx, rhs, indent + 1);
|
||||
},
|
||||
hir::ExprKind::Field(e, ident) => {
|
||||
println!("{}Field", ind);
|
||||
println!("{}field name: {}", ind, ident.name);
|
||||
println!("{}struct expr:", ind);
|
||||
print_expr(cx, e, indent + 1);
|
||||
},
|
||||
hir::ExprKind::Index(arr, idx) => {
|
||||
println!("{}Index", ind);
|
||||
println!("{}array expr:", ind);
|
||||
print_expr(cx, arr, indent + 1);
|
||||
println!("{}index expr:", ind);
|
||||
print_expr(cx, idx, indent + 1);
|
||||
},
|
||||
hir::ExprKind::Path(hir::QPath::Resolved(ref ty, path)) => {
|
||||
println!("{}Resolved Path, {:?}", ind, ty);
|
||||
println!("{}path: {:?}", ind, path);
|
||||
},
|
||||
hir::ExprKind::Path(hir::QPath::TypeRelative(ty, seg)) => {
|
||||
println!("{}Relative Path, {:?}", ind, ty);
|
||||
println!("{}seg: {:?}", ind, seg);
|
||||
},
|
||||
hir::ExprKind::Path(hir::QPath::LangItem(lang_item, ..)) => {
|
||||
println!("{}Lang Item Path, {:?}", ind, lang_item.name());
|
||||
},
|
||||
hir::ExprKind::AddrOf(kind, ref muta, e) => {
|
||||
println!("{}AddrOf", ind);
|
||||
println!("kind: {:?}", kind);
|
||||
println!("mutability: {:?}", muta);
|
||||
print_expr(cx, e, indent + 1);
|
||||
},
|
||||
hir::ExprKind::Break(_, ref e) => {
|
||||
println!("{}Break", ind);
|
||||
if let Some(e) = *e {
|
||||
print_expr(cx, e, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Continue(_) => println!("{}Again", ind),
|
||||
hir::ExprKind::Ret(ref e) => {
|
||||
println!("{}Ret", ind);
|
||||
if let Some(e) = *e {
|
||||
print_expr(cx, e, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::ExprKind::InlineAsm(asm) => {
|
||||
println!("{}InlineAsm", ind);
|
||||
println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template));
|
||||
println!("{}options: {:?}", ind, asm.options);
|
||||
println!("{}operands:", ind);
|
||||
for (op, _op_sp) in asm.operands {
|
||||
match op {
|
||||
hir::InlineAsmOperand::In { expr, .. }
|
||||
| hir::InlineAsmOperand::InOut { expr, .. } => {
|
||||
print_expr(cx, expr, indent + 1);
|
||||
}
|
||||
hir::InlineAsmOperand::Out { expr, .. } => {
|
||||
if let Some(expr) = expr {
|
||||
print_expr(cx, expr, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||
print_expr(cx, in_expr, indent + 1);
|
||||
if let Some(out_expr) = out_expr {
|
||||
print_expr(cx, out_expr, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::InlineAsmOperand::Const { anon_const }
|
||||
| hir::InlineAsmOperand::SymFn { anon_const } => {
|
||||
println!("{}anon_const:", ind);
|
||||
print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
|
||||
},
|
||||
hir::InlineAsmOperand::SymStatic { path, .. } => {
|
||||
match path {
|
||||
hir::QPath::Resolved(ref ty, path) => {
|
||||
println!("{}Resolved Path, {:?}", ind, ty);
|
||||
println!("{}path: {:?}", ind, path);
|
||||
},
|
||||
hir::QPath::TypeRelative(ty, seg) => {
|
||||
println!("{}Relative Path, {:?}", ind, ty);
|
||||
println!("{}seg: {:?}", ind, seg);
|
||||
},
|
||||
hir::QPath::LangItem(lang_item, ..) => {
|
||||
println!("{}Lang Item Path, {:?}", ind, lang_item.name());
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Struct(path, fields, ref base) => {
|
||||
println!("{}Struct", ind);
|
||||
println!("{}path: {:?}", ind, path);
|
||||
for field in fields {
|
||||
println!("{}field \"{}\":", ind, field.ident.name);
|
||||
print_expr(cx, field.expr, indent + 1);
|
||||
}
|
||||
if let Some(base) = *base {
|
||||
println!("{}base:", ind);
|
||||
print_expr(cx, base, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::ExprKind::ConstBlock(ref anon_const) => {
|
||||
println!("{}ConstBlock", ind);
|
||||
println!("{}anon_const:", ind);
|
||||
print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
|
||||
},
|
||||
hir::ExprKind::Repeat(val, length) => {
|
||||
println!("{}Repeat", ind);
|
||||
println!("{}value:", ind);
|
||||
print_expr(cx, val, indent + 1);
|
||||
println!("{}repeat count:", ind);
|
||||
match length {
|
||||
hir::ArrayLen::Infer(_, _) => println!("{}repeat count: _", ind),
|
||||
hir::ArrayLen::Body(anon_const) => {
|
||||
print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
|
||||
},
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Err => {
|
||||
println!("{}Err", ind);
|
||||
},
|
||||
hir::ExprKind::DropTemps(e) => {
|
||||
println!("{}DropTemps", ind);
|
||||
print_expr(cx, e, indent + 1);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
||||
let did = item.def_id;
|
||||
println!("item `{}`", item.ident.name);
|
||||
match cx.tcx.visibility(item.def_id) {
|
||||
ty::Visibility::Public => println!("public"),
|
||||
ty::Visibility::Restricted(def_id) => {
|
||||
if def_id.is_top_level_module() {
|
||||
println!("visible crate wide")
|
||||
} else {
|
||||
println!("visible in module `{}`", cx.tcx.def_path_str(def_id))
|
||||
}
|
||||
},
|
||||
ty::Visibility::Invisible => println!("invisible"),
|
||||
}
|
||||
match item.kind {
|
||||
hir::ItemKind::ExternCrate(ref _renamed_from) => {
|
||||
if let Some(crate_id) = cx.tcx.extern_mod_stmt_cnum(did) {
|
||||
let source = cx.tcx.used_crate_source(crate_id);
|
||||
if let Some(ref src) = source.dylib {
|
||||
println!("extern crate dylib source: {:?}", src.0);
|
||||
}
|
||||
if let Some(ref src) = source.rlib {
|
||||
println!("extern crate rlib source: {:?}", src.0);
|
||||
}
|
||||
} else {
|
||||
println!("weird extern crate without a crate id");
|
||||
}
|
||||
},
|
||||
hir::ItemKind::Use(path, ref kind) => println!("{:?}, {:?}", path, kind),
|
||||
hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)),
|
||||
hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)),
|
||||
hir::ItemKind::Fn(..) => {
|
||||
let item_ty = cx.tcx.type_of(did);
|
||||
println!("function of type {:#?}", item_ty);
|
||||
},
|
||||
hir::ItemKind::Macro(ref macro_def, _) => {
|
||||
if macro_def.macro_rules {
|
||||
println!("macro introduced by `macro_rules!`");
|
||||
} else {
|
||||
println!("macro introduced by `macro`");
|
||||
}
|
||||
},
|
||||
hir::ItemKind::Mod(..) => println!("module"),
|
||||
hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi),
|
||||
hir::ItemKind::GlobalAsm(asm) => println!("global asm: {:?}", asm),
|
||||
hir::ItemKind::TyAlias(..) => {
|
||||
println!("type alias for {:?}", cx.tcx.type_of(did));
|
||||
},
|
||||
hir::ItemKind::OpaqueTy(..) => {
|
||||
println!("existential type with real type {:?}", cx.tcx.type_of(did));
|
||||
},
|
||||
hir::ItemKind::Enum(..) => {
|
||||
println!("enum definition of type {:?}", cx.tcx.type_of(did));
|
||||
},
|
||||
hir::ItemKind::Struct(..) => {
|
||||
println!("struct definition of type {:?}", cx.tcx.type_of(did));
|
||||
},
|
||||
hir::ItemKind::Union(..) => {
|
||||
println!("union definition of type {:?}", cx.tcx.type_of(did));
|
||||
},
|
||||
hir::ItemKind::Trait(..) => {
|
||||
println!("trait decl");
|
||||
if cx.tcx.trait_is_auto(did.to_def_id()) {
|
||||
println!("trait is auto");
|
||||
} else {
|
||||
println!("trait is not auto");
|
||||
}
|
||||
},
|
||||
hir::ItemKind::TraitAlias(..) => {
|
||||
println!("trait alias");
|
||||
},
|
||||
hir::ItemKind::Impl(hir::Impl {
|
||||
of_trait: Some(ref _trait_ref),
|
||||
..
|
||||
}) => {
|
||||
println!("trait impl");
|
||||
},
|
||||
hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) => {
|
||||
println!("impl");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::similar_names)]
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) {
|
||||
let ind = " ".repeat(indent);
|
||||
println!("{}+", ind);
|
||||
match pat.kind {
|
||||
hir::PatKind::Wild => println!("{}Wild", ind),
|
||||
hir::PatKind::Binding(ref mode, .., ident, ref inner) => {
|
||||
println!("{}Binding", ind);
|
||||
println!("{}mode: {:?}", ind, mode);
|
||||
println!("{}name: {}", ind, ident.name);
|
||||
if let Some(inner) = *inner {
|
||||
println!("{}inner:", ind);
|
||||
print_pat(cx, inner, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::PatKind::Or(fields) => {
|
||||
println!("{}Or", ind);
|
||||
for field in fields {
|
||||
print_pat(cx, field, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::PatKind::Struct(ref path, fields, ignore) => {
|
||||
println!("{}Struct", ind);
|
||||
println!(
|
||||
"{}name: {}",
|
||||
ind,
|
||||
rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
|
||||
);
|
||||
println!("{}ignore leftover fields: {}", ind, ignore);
|
||||
println!("{}fields:", ind);
|
||||
for field in fields {
|
||||
println!("{} field name: {}", ind, field.ident.name);
|
||||
if field.is_shorthand {
|
||||
println!("{} in shorthand notation", ind);
|
||||
}
|
||||
print_pat(cx, field.pat, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => {
|
||||
println!("{}TupleStruct", ind);
|
||||
println!(
|
||||
"{}path: {}",
|
||||
ind,
|
||||
rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
|
||||
);
|
||||
if let Some(dot_position) = opt_dots_position {
|
||||
println!("{}dot position: {}", ind, dot_position);
|
||||
}
|
||||
for field in fields {
|
||||
print_pat(cx, field, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::PatKind::Path(hir::QPath::Resolved(ref ty, path)) => {
|
||||
println!("{}Resolved Path, {:?}", ind, ty);
|
||||
println!("{}path: {:?}", ind, path);
|
||||
},
|
||||
hir::PatKind::Path(hir::QPath::TypeRelative(ty, seg)) => {
|
||||
println!("{}Relative Path, {:?}", ind, ty);
|
||||
println!("{}seg: {:?}", ind, seg);
|
||||
},
|
||||
hir::PatKind::Path(hir::QPath::LangItem(lang_item, ..)) => {
|
||||
println!("{}Lang Item Path, {:?}", ind, lang_item.name());
|
||||
},
|
||||
hir::PatKind::Tuple(pats, opt_dots_position) => {
|
||||
println!("{}Tuple", ind);
|
||||
if let Some(dot_position) = opt_dots_position {
|
||||
println!("{}dot position: {}", ind, dot_position);
|
||||
}
|
||||
for field in pats {
|
||||
print_pat(cx, field, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::PatKind::Box(inner) => {
|
||||
println!("{}Box", ind);
|
||||
print_pat(cx, inner, indent + 1);
|
||||
},
|
||||
hir::PatKind::Ref(inner, ref muta) => {
|
||||
println!("{}Ref", ind);
|
||||
println!("{}mutability: {:?}", ind, muta);
|
||||
print_pat(cx, inner, indent + 1);
|
||||
},
|
||||
hir::PatKind::Lit(e) => {
|
||||
println!("{}Lit", ind);
|
||||
print_expr(cx, e, indent + 1);
|
||||
},
|
||||
hir::PatKind::Range(ref l, ref r, ref range_end) => {
|
||||
println!("{}Range", ind);
|
||||
if let Some(expr) = l {
|
||||
print_expr(cx, expr, indent + 1);
|
||||
}
|
||||
if let Some(expr) = r {
|
||||
print_expr(cx, expr, indent + 1);
|
||||
}
|
||||
match *range_end {
|
||||
hir::RangeEnd::Included => println!("{} end included", ind),
|
||||
hir::RangeEnd::Excluded => println!("{} end excluded", ind),
|
||||
}
|
||||
},
|
||||
hir::PatKind::Slice(first_pats, ref range, last_pats) => {
|
||||
println!("{}Slice [a, b, ..i, y, z]", ind);
|
||||
println!("[a, b]:");
|
||||
for pat in first_pats {
|
||||
print_pat(cx, pat, indent + 1);
|
||||
}
|
||||
println!("i:");
|
||||
if let Some(pat) = *range {
|
||||
print_pat(cx, pat, indent + 1);
|
||||
}
|
||||
println!("[y, z]:");
|
||||
for pat in last_pats {
|
||||
print_pat(cx, pat, indent + 1);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn print_guard(cx: &LateContext<'_>, guard: &hir::Guard<'_>, indent: usize) {
|
||||
let ind = " ".repeat(indent);
|
||||
println!("{}+", ind);
|
||||
match guard {
|
||||
hir::Guard::If(expr) => {
|
||||
println!("{}If", ind);
|
||||
print_expr(cx, expr, indent + 1);
|
||||
},
|
||||
hir::Guard::IfLet(pat, expr) => {
|
||||
println!("{}IfLet", ind);
|
||||
print_pat(cx, pat, indent + 1);
|
||||
print_expr(cx, expr, indent + 1);
|
||||
},
|
||||
}
|
||||
}
|
@ -292,7 +292,7 @@ declare_clippy_lint! {
|
||||
/// Checks for unnecessary conversion from Symbol to a string.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's faster use symbols directly intead of strings.
|
||||
/// It's faster use symbols directly instead of strings.
|
||||
///
|
||||
/// ### Example
|
||||
/// Bad:
|
||||
@ -823,7 +823,7 @@ fn suggest_note(
|
||||
cx,
|
||||
COLLAPSIBLE_SPAN_LINT_CALLS,
|
||||
expr.span,
|
||||
"this call is collspible",
|
||||
"this call is collapsible",
|
||||
"collapse into",
|
||||
format!(
|
||||
"span_lint_and_note({}, {}, {}, {}, {}, {})",
|
||||
|
@ -33,7 +33,7 @@ use std::path::Path;
|
||||
/// This is the output file of the lint collector.
|
||||
const OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
|
||||
/// These lints are excluded from the export.
|
||||
const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "internal_metadata_collector"];
|
||||
const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "internal_metadata_collector"];
|
||||
/// These groups will be ignored by the lint group matcher. This is useful for collections like
|
||||
/// `clippy::all`
|
||||
const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"];
|
||||
@ -117,7 +117,7 @@ const APPLICABILITY_NAME_INDEX: usize = 2;
|
||||
/// This applicability will be set for unresolved applicability values.
|
||||
const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
|
||||
/// The version that will be displayed if none has been defined
|
||||
const VERION_DEFAULT_STR: &str = "Unknown";
|
||||
const VERSION_DEFAULT_STR: &str = "Unknown";
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -571,7 +571,7 @@ fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
|
||||
|
||||
fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
|
||||
extract_clippy_version_value(cx, item).map_or_else(
|
||||
|| VERION_DEFAULT_STR.to_string(),
|
||||
|| VERSION_DEFAULT_STR.to_string(),
|
||||
|version| version.as_str().to_string(),
|
||||
)
|
||||
}
|
||||
@ -872,7 +872,7 @@ impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> {
|
||||
self.suggestion_count += 2;
|
||||
}
|
||||
|
||||
/// Checks if the suggestions include multiple spanns
|
||||
/// Checks if the suggestions include multiple spans
|
||||
fn is_multi_part(&self) -> bool {
|
||||
self.suggestion_count > 1
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user