Merge commit '98e2b9f25b6db4b2680a3d388456d9f95cb28344' into clippyup
This commit is contained in:
parent
419bf6bbd8
commit
02bf692169
|
@ -3,7 +3,9 @@ Thank you for making Clippy better!
|
|||
We're collecting our changelog from pull request descriptions.
|
||||
If your PR only includes internal changes, you can just write
|
||||
`changelog: none`. Otherwise, please write a short comment
|
||||
explaining your change.
|
||||
explaining your change. Also, it's helpful for us that
|
||||
the lint name is put into brackets `[]` and backticks `` ` ` ``,
|
||||
e.g. ``[`lint_name`]``.
|
||||
|
||||
If your PR fixes an issue, you can add "fixes #issue_number" into this
|
||||
PR description. This way the issue will be automatically closed when
|
||||
|
@ -29,4 +31,5 @@ Delete this line and everything above before opening your PR.
|
|||
---
|
||||
|
||||
*Please write a short comment explaining your change (or "none" for internal only changes)*
|
||||
|
||||
changelog:
|
||||
|
|
|
@ -34,15 +34,16 @@ jobs:
|
|||
run: |
|
||||
MESSAGE=$(git log --format=%B -n 1)
|
||||
PR=$(echo "$MESSAGE" | grep -o "#[0-9]*" | head -1 | sed -e 's/^#//')
|
||||
output=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \
|
||||
python -c "import sys, json; print(json.load(sys.stdin)['body'])" | \
|
||||
grep "^changelog: " | \
|
||||
sed "s/changelog: //g")
|
||||
if [[ -z "$output" ]]; then
|
||||
body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \
|
||||
python -c "import sys, json; print(json.load(sys.stdin)['body'])")
|
||||
output=$(grep "^changelog:\s*\S" <<< "$body" | sed "s/changelog:\s*//g") || {
|
||||
echo "ERROR: PR body must contain 'changelog: ...'"
|
||||
exit 1
|
||||
elif [[ "$output" = "none" ]]; then
|
||||
}
|
||||
if [[ "$output" = "none" ]]; then
|
||||
echo "WARNING: changelog is 'none'"
|
||||
else
|
||||
echo "changelog: $output"
|
||||
fi
|
||||
env:
|
||||
PYTHONIOENCODING: 'utf-8'
|
||||
|
|
34
CHANGELOG.md
34
CHANGELOG.md
|
@ -179,7 +179,7 @@ Current stable, released 2021-03-25
|
|||
|
||||
* Replace [`find_map`] with [`manual_find_map`]
|
||||
[#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
|
||||
* [`unknown_clippy_lints`] Now integrated in the `unknown_lints` rustc lint
|
||||
* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint
|
||||
[#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
|
||||
|
||||
### Enhancements
|
||||
|
@ -280,7 +280,7 @@ Released 2021-02-11
|
|||
|
||||
* Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
|
||||
as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
|
||||
* Deprecate [`panic_params`] lint. This is now available in rustc as `panic_fmt`
|
||||
* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panic`
|
||||
[#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
|
||||
* Move [`map_err_ignore`] to `restriction`
|
||||
[#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
|
||||
|
@ -419,7 +419,7 @@ Released 2020-12-31
|
|||
[#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
|
||||
* Rename `zero_width_space` to [`invisible_characters`]
|
||||
[#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
|
||||
* Deprecate [`drop_bounds`] (uplifted)
|
||||
* Deprecate `drop_bounds` (uplifted)
|
||||
[#6111](https://github.com/rust-lang/rust-clippy/pull/6111)
|
||||
* Move [`string_lit_as_bytes`] to `nursery`
|
||||
[#6117](https://github.com/rust-lang/rust-clippy/pull/6117)
|
||||
|
@ -1018,7 +1018,7 @@ Released 2020-03-12
|
|||
[#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
|
||||
* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
|
||||
* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
|
||||
* Deprecate [`unused_label`] [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
|
||||
* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
|
||||
|
||||
### Enhancements
|
||||
|
||||
|
@ -1046,7 +1046,7 @@ Released 2020-03-12
|
|||
* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
|
||||
* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
|
||||
* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
|
||||
* [`unknown_clippy_lints`] [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
|
||||
* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
|
||||
* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
|
||||
* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
|
||||
* [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
|
||||
|
@ -1080,7 +1080,7 @@ Released 2020-01-30
|
|||
[Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
|
||||
details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
|
||||
* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
|
||||
* Deprecate [`into_iter_on_array`] [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
|
||||
* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
|
||||
* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
|
||||
[#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
|
||||
* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
|
||||
|
@ -1282,7 +1282,7 @@ Released 2019-05-20
|
|||
|
||||
[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
|
||||
|
||||
* New lint: [`drop_bounds`] to detect `T: Drop` bounds
|
||||
* New lint: `drop_bounds` to detect `T: Drop` bounds
|
||||
* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
|
||||
* Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
|
||||
* Move [`get_unwrap`] to the restriction category
|
||||
|
@ -1375,7 +1375,7 @@ Released 2019-01-17
|
|||
|
||||
* New lints: [`slow_vector_initialization`], [`mem_discriminant_non_enum`],
|
||||
[`redundant_clone`], [`wildcard_dependencies`],
|
||||
[`into_iter_on_ref`], [`into_iter_on_array`], [`deprecated_cfg_attr`],
|
||||
[`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`],
|
||||
[`mem_discriminant_non_enum`], [`cargo_common_metadata`]
|
||||
* Add support for `u128` and `i128` to integer related lints
|
||||
* Add float support to `mistyped_literal_suffixes`
|
||||
|
@ -1649,7 +1649,7 @@ Released 2018-09-13
|
|||
|
||||
## 0.0.166
|
||||
* Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
|
||||
* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], [`invalid_ref`], [`option_map_or_none`],
|
||||
* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`],
|
||||
[`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
|
||||
[`transmute_int_to_float`]
|
||||
|
||||
|
@ -2037,7 +2037,7 @@ Released 2018-09-13
|
|||
|
||||
## 0.0.64 — 2016-04-26
|
||||
* Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
|
||||
* New lints: [`temporary_cstring_as_ptr`], [`unsafe_removed_from_name`], and [`mem_forget`]
|
||||
* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`]
|
||||
|
||||
## 0.0.63 — 2016-04-08
|
||||
* Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
|
||||
|
@ -2091,7 +2091,7 @@ Released 2018-09-13
|
|||
|
||||
## 0.0.49 — 2016-03-09
|
||||
* Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
|
||||
* New lints: [`overflow_check_conditional`], [`unused_label`], [`new_without_default`]
|
||||
* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`]
|
||||
|
||||
## 0.0.48 — 2016-03-07
|
||||
* Fixed: ICE in [`needless_range_loop`] with globals
|
||||
|
@ -2124,6 +2124,7 @@ Released 2018-09-13
|
|||
[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
|
||||
[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
|
||||
[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
|
||||
[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
|
||||
[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
|
||||
[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
|
||||
[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
|
||||
|
@ -2148,6 +2149,7 @@ Released 2018-09-13
|
|||
[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
|
||||
[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
|
||||
[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
|
||||
[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied
|
||||
[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
|
||||
[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
|
||||
[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
|
||||
|
@ -2178,7 +2180,6 @@ Released 2018-09-13
|
|||
[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
|
||||
[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
|
||||
[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
|
||||
[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
|
||||
[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
|
||||
[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
|
||||
[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
|
||||
|
@ -2216,6 +2217,7 @@ Released 2018-09-13
|
|||
[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
|
||||
[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
|
||||
[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
|
||||
[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option
|
||||
[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
|
||||
[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
|
||||
[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
|
||||
|
@ -2264,10 +2266,9 @@ Released 2018-09-13
|
|||
[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
|
||||
[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
|
||||
[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
|
||||
[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
|
||||
[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
|
||||
[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
|
||||
[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
|
||||
[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
|
||||
[`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
|
||||
|
@ -2402,7 +2403,6 @@ Released 2018-09-13
|
|||
[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
|
||||
[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
|
||||
[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
|
||||
[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
|
||||
[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
|
||||
[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
|
||||
[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
|
||||
|
@ -2488,7 +2488,6 @@ Released 2018-09-13
|
|||
[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
|
||||
[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
|
||||
[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
|
||||
[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
|
||||
[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
|
||||
[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
|
||||
[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
|
||||
|
@ -2517,13 +2516,13 @@ Released 2018-09-13
|
|||
[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
|
||||
[`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
|
||||
[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
|
||||
[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
|
||||
[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
|
||||
[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
|
||||
[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
|
||||
[`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_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_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
|
||||
[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
|
||||
|
@ -2541,7 +2540,6 @@ Released 2018-09-13
|
|||
[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
|
||||
[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
|
||||
[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
|
||||
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
|
||||
[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
|
||||
[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
|
||||
[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
|
||||
|
|
|
@ -21,10 +21,10 @@ All contributors are expected to follow the [Rust Code of Conduct].
|
|||
- [IntelliJ Rust](#intellij-rust)
|
||||
- [Rust Analyzer](#rust-analyzer)
|
||||
- [How Clippy works](#how-clippy-works)
|
||||
- [Syncing changes between Clippy and [`rust-lang/rust`]](#syncing-changes-between-clippy-and-rust-langrust)
|
||||
- [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust)
|
||||
- [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos)
|
||||
- [Performing the sync from [`rust-lang/rust`] to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
|
||||
- [Performing the sync from Clippy to [`rust-lang/rust`]](#performing-the-sync-from-clippy-to-rust-langrust)
|
||||
- [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
|
||||
- [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust)
|
||||
- [Defining remotes](#defining-remotes)
|
||||
- [Issue and PR triage](#issue-and-pr-triage)
|
||||
- [Bors and Homu](#bors-and-homu)
|
||||
|
|
|
@ -127,10 +127,9 @@ fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)
|
|||
_ => &block.expr,
|
||||
};
|
||||
// function call
|
||||
if let Some(args) = match_panic_call(cx, begin_panic_call);
|
||||
if args.len() == 1;
|
||||
if let Some(arg) = match_panic_call(cx, begin_panic_call);
|
||||
// bind the second argument of the `assert!` macro if it exists
|
||||
if let panic_message = snippet_opt(cx, args[0].span);
|
||||
if let panic_message = snippet_opt(cx, arg.span);
|
||||
// second argument of begin_panic is irrelevant
|
||||
// as is the second match arm
|
||||
then {
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::{ast_utils, is_direct_expn_of};
|
||||
use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** This lint warns about boolean comparisons in assert-like macros.
|
||||
///
|
||||
/// **Why is this bad?** It is shorter to use the equivalent.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// assert_eq!("a".is_empty(), false);
|
||||
/// assert_ne!("a".is_empty(), true);
|
||||
///
|
||||
/// // Good
|
||||
/// assert!(!"a".is_empty());
|
||||
/// ```
|
||||
pub BOOL_ASSERT_COMPARISON,
|
||||
style,
|
||||
"Using a boolean as comparison value in an assert_* macro when there is no need"
|
||||
}
|
||||
|
||||
declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]);
|
||||
|
||||
fn is_bool_lit(e: &Expr) -> bool {
|
||||
matches!(
|
||||
e.kind,
|
||||
ExprKind::Lit(Lit {
|
||||
kind: LitKind::Bool(_),
|
||||
..
|
||||
})
|
||||
) && !e.span.from_expansion()
|
||||
}
|
||||
|
||||
impl EarlyLintPass for BoolAssertComparison {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
|
||||
let macros = ["assert_eq", "debug_assert_eq"];
|
||||
let inverted_macros = ["assert_ne", "debug_assert_ne"];
|
||||
|
||||
for mac in macros.iter().chain(inverted_macros.iter()) {
|
||||
if let Some(span) = is_direct_expn_of(e.span, mac) {
|
||||
if let Some([a, b]) = ast_utils::extract_assert_macro_args(e) {
|
||||
let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize;
|
||||
|
||||
if nb_bool_args != 1 {
|
||||
// If there are two boolean arguments, we definitely don't understand
|
||||
// what's going on, so better leave things as is...
|
||||
//
|
||||
// Or there is simply no boolean and then we can leave things as is!
|
||||
return;
|
||||
}
|
||||
|
||||
let non_eq_mac = &mac[..mac.len() - 3];
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BOOL_ASSERT_COMPARISON,
|
||||
span,
|
||||
&format!("used `{}!` with a literal bool", mac),
|
||||
"replace it with",
|
||||
format!("{}!(..)", non_eq_mac),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -261,7 +261,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
|
|||
}
|
||||
METHODS_WITH_NEGATION
|
||||
.iter()
|
||||
.cloned()
|
||||
.copied()
|
||||
.flat_map(|(a, b)| vec![(a, b), (b, a)])
|
||||
.find(|&(a, _)| {
|
||||
let path: &str = &path.ident.name.as_str();
|
||||
|
|
|
@ -323,7 +323,7 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function:
|
|||
if let [int] = &*tp.segments;
|
||||
then {
|
||||
let name = &int.ident.name.as_str();
|
||||
candidates.iter().find(|c| name == *c).cloned()
|
||||
candidates.iter().find(|c| name == *c).copied()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -337,7 +337,7 @@ fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
|
|||
if let [ty] = &*path.segments;
|
||||
then {
|
||||
let name = &ty.ident.name.as_str();
|
||||
INTS.iter().find(|c| name == *c).cloned()
|
||||
INTS.iter().find(|c| name == *c).copied()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::visitors::LocalUsedVisitor;
|
||||
use clippy_utils::{path_to_local, SpanlessEq};
|
||||
use clippy_utils::{is_lang_ctor, path_to_local, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp};
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{DefIdTree, TyCtxt, TypeckResults};
|
||||
use rustc_middle::ty::TypeckResults;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{MultiSpan, Span};
|
||||
|
||||
|
@ -52,7 +52,7 @@ declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]);
|
|||
impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if let ExprKind::Match(_expr, arms, _source) = expr.kind {
|
||||
if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(arm, cx.tcx)) {
|
||||
if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
|
||||
for arm in arms {
|
||||
check_arm(arm, wild_arm, cx);
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext
|
|||
// match <local> { .. }
|
||||
if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results()));
|
||||
// one of the branches must be "wild-like"
|
||||
if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx));
|
||||
if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner));
|
||||
let (wild_inner_arm, non_wild_inner_arm) =
|
||||
(&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]);
|
||||
if !pat_contains_or(non_wild_inner_arm.pat);
|
||||
|
@ -126,13 +126,13 @@ fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir>
|
|||
/// A "wild-like" pattern is wild ("_") or `None`.
|
||||
/// For this lint to apply, both the outer and inner match expressions
|
||||
/// must have "wild-like" branches that can be combined.
|
||||
fn arm_is_wild_like(arm: &Arm<'_>, tcx: TyCtxt<'_>) -> bool {
|
||||
fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
|
||||
if arm.guard.is_some() {
|
||||
return false;
|
||||
}
|
||||
match arm.pat.kind {
|
||||
PatKind::Binding(..) | PatKind::Wild => true,
|
||||
PatKind::Path(QPath::Resolved(None, path)) if is_none_ctor(path.res, tcx) => true,
|
||||
PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -164,17 +164,6 @@ fn pat_contains_or(pat: &Pat<'_>) -> bool {
|
|||
result
|
||||
}
|
||||
|
||||
fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool {
|
||||
if let Some(none_id) = tcx.lang_items().option_none_variant() {
|
||||
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = res {
|
||||
if let Some(variant_id) = tcx.parent(id) {
|
||||
return variant_id == none_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
|
||||
/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
|
||||
fn strip_ref_operators<'hir>(mut expr: &'hir Expr<'hir>, typeck_results: &TypeckResults<'_>) -> &'hir Expr<'hir> {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{get_trait_def_id, if_sequence, parent_node_is_if_expr, paths, SpanlessEq};
|
||||
use clippy_utils::{get_trait_def_id, if_sequence, is_else_clause, paths, SpanlessEq};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
|
|||
}
|
||||
|
||||
// We only care about the top-most `if` in the chain
|
||||
if parent_node_is_if_expr(expr, cx) {
|
||||
if is_else_clause(cx.tcx, expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
|
||||
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
|
||||
use clippy_utils::{
|
||||
both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, parent_node_is_if_expr,
|
||||
both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, is_else_clause,
|
||||
run_lints, search_same, ContainsName, SpanlessEq, SpanlessHash,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
|
@ -188,13 +188,18 @@ fn lint_same_then_else<'tcx>(
|
|||
expr: &'tcx Expr<'_>,
|
||||
) {
|
||||
// We only lint ifs with multiple blocks
|
||||
if blocks.len() < 2 || parent_node_is_if_expr(expr, cx) {
|
||||
if blocks.len() < 2 || is_else_clause(cx.tcx, expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if each block has shared code
|
||||
let has_expr = blocks[0].expr.is_some();
|
||||
let (start_eq, mut end_eq, expr_eq) = scan_block_for_eq(cx, blocks);
|
||||
|
||||
let (start_eq, mut end_eq, expr_eq) = if let Some(block_eq) = scan_block_for_eq(cx, blocks) {
|
||||
(block_eq.start_eq, block_eq.end_eq, block_eq.expr_eq)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// BRANCHES_SHARING_CODE prerequisites
|
||||
if has_conditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) {
|
||||
|
@ -290,7 +295,19 @@ fn lint_same_then_else<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, usize, bool) {
|
||||
struct BlockEqual {
|
||||
/// The amount statements that are equal from the start
|
||||
start_eq: usize,
|
||||
/// The amount statements that are equal from the end
|
||||
end_eq: usize,
|
||||
/// An indication if the block expressions are the same. This will also be true if both are
|
||||
/// `None`
|
||||
expr_eq: bool,
|
||||
}
|
||||
|
||||
/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to
|
||||
/// abort any further processing and avoid duplicate lint triggers.
|
||||
fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<BlockEqual> {
|
||||
let mut start_eq = usize::MAX;
|
||||
let mut end_eq = usize::MAX;
|
||||
let mut expr_eq = true;
|
||||
|
@ -332,7 +349,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize,
|
|||
"same as this",
|
||||
);
|
||||
|
||||
return (0, 0, false);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,7 +369,11 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize,
|
|||
end_eq = min_block_size - start_eq;
|
||||
}
|
||||
|
||||
(start_eq, end_eq, expr_eq)
|
||||
Some(BlockEqual {
|
||||
start_eq,
|
||||
end_eq,
|
||||
expr_eq,
|
||||
})
|
||||
}
|
||||
|
||||
fn check_for_warn_of_moved_symbol(
|
||||
|
|
|
@ -93,15 +93,6 @@ declare_deprecated_lint! {
|
|||
"the replacement suggested by this lint had substantially different behavior"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// **Deprecation reason:** This lint has been superseded by the warn-by-default
|
||||
/// `invalid_value` rustc lint.
|
||||
pub INVALID_REF,
|
||||
"superseded by rustc lint `invalid_value`"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
|
@ -110,24 +101,6 @@ declare_deprecated_lint! {
|
|||
"`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// **Deprecation reason:** This lint has been uplifted to rustc and is now called
|
||||
/// `array_into_iter`.
|
||||
pub INTO_ITER_ON_ARRAY,
|
||||
"this lint has been uplifted to rustc and is now called `array_into_iter`"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// **Deprecation reason:** This lint has been uplifted to rustc and is now called
|
||||
/// `unused_labels`.
|
||||
pub UNUSED_LABEL,
|
||||
"this lint has been uplifted to rustc and is now called `unused_labels`"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
|
@ -144,42 +117,6 @@ declare_deprecated_lint! {
|
|||
"the regex! macro has been removed from the regex crate in 2018"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// **Deprecation reason:** This lint has been uplifted to rustc and is now called
|
||||
/// `drop_bounds`.
|
||||
pub DROP_BOUNDS,
|
||||
"this lint has been uplifted to rustc and is now called `drop_bounds`"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// **Deprecation reason:** This lint has been uplifted to rustc and is now called
|
||||
/// `temporary_cstring_as_ptr`.
|
||||
pub TEMPORARY_CSTRING_AS_PTR,
|
||||
"this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// **Deprecation reason:** This lint has been uplifted to rustc and is now called
|
||||
/// `panic_fmt`.
|
||||
pub PANIC_PARAMS,
|
||||
"this lint has been uplifted to rustc and is now called `panic_fmt`"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// **Deprecation reason:** This lint has been integrated into the `unknown_lints`
|
||||
/// rustc lint.
|
||||
pub UNKNOWN_CLIPPY_LINTS,
|
||||
"this lint has been integrated into the `unknown_lints` rustc lint"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
|
@ -188,3 +125,12 @@ declare_deprecated_lint! {
|
|||
pub FIND_MAP,
|
||||
"this lint has been replaced by `manual_find_map`, a more specific lint"
|
||||
}
|
||||
|
||||
declare_deprecated_lint! {
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// **Deprecation reason:** This lint has been replaced by `manual_filter_map`, a
|
||||
/// more specific lint.
|
||||
pub FILTER_MAP,
|
||||
"this lint has been replaced by `manual_filter_map`, a more specific lint"
|
||||
}
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, match_type};
|
||||
use clippy_utils::SpanlessEq;
|
||||
use clippy_utils::{get_item_name, paths};
|
||||
use if_chain::if_chain;
|
||||
use clippy_utils::{
|
||||
can_move_expr_to_closure_no_visit,
|
||||
diagnostics::span_lint_and_sugg,
|
||||
is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, paths, peel_hir_expr_while,
|
||||
source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context},
|
||||
SpanlessEq,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, UnOp};
|
||||
use rustc_hir::{
|
||||
intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
|
||||
Block, Expr, ExprKind, Guard, HirId, Local, Stmt, StmtKind, UnOp,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::{Span, SyntaxContext, DUMMY_SP};
|
||||
use std::fmt::Write;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap`
|
||||
|
@ -19,15 +21,14 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Why is this bad?** Using `entry` is more efficient.
|
||||
///
|
||||
/// **Known problems:** Some false negatives, eg.:
|
||||
/// **Known problems:** The suggestion may have type inference errors in some cases. e.g.
|
||||
/// ```rust
|
||||
/// # use std::collections::HashMap;
|
||||
/// # let mut map = HashMap::new();
|
||||
/// # let v = 1;
|
||||
/// # let k = 1;
|
||||
/// if !map.contains_key(&k) {
|
||||
/// map.insert(k.clone(), v);
|
||||
/// }
|
||||
/// let mut map = std::collections::HashMap::new();
|
||||
/// let _ = if !map.contains_key(&0) {
|
||||
/// map.insert(0, 0)
|
||||
/// } else {
|
||||
/// None
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// **Example:**
|
||||
|
@ -56,132 +57,584 @@ declare_clippy_lint! {
|
|||
declare_lint_pass!(HashMapPass => [MAP_ENTRY]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for HashMapPass {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::If(check, then_block, ref else_block) = expr.kind {
|
||||
if let ExprKind::Unary(UnOp::Not, check) = check.kind {
|
||||
if let Some((ty, map, key)) = check_cond(cx, check) {
|
||||
// in case of `if !m.contains_key(&k) { m.insert(k, v); }`
|
||||
// we can give a better error message
|
||||
let sole_expr = {
|
||||
else_block.is_none()
|
||||
&& if let ExprKind::Block(then_block, _) = then_block.kind {
|
||||
(then_block.expr.is_some() as usize) + then_block.stmts.len() == 1
|
||||
} else {
|
||||
true
|
||||
}
|
||||
// XXXManishearth we can also check for if/else blocks containing `None`.
|
||||
};
|
||||
let (cond_expr, then_expr, else_expr) = match expr.kind {
|
||||
ExprKind::If(c, t, e) => (c, t, e),
|
||||
_ => return,
|
||||
};
|
||||
let (map_ty, contains_expr) = match try_parse_contains(cx, cond_expr) {
|
||||
Some(x) => x,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let mut visitor = InsertVisitor {
|
||||
cx,
|
||||
span: expr.span,
|
||||
ty,
|
||||
map,
|
||||
key,
|
||||
sole_expr,
|
||||
};
|
||||
let then_search = match find_insert_calls(cx, &contains_expr, then_expr) {
|
||||
Some(x) => x,
|
||||
None => return,
|
||||
};
|
||||
|
||||
walk_expr(&mut visitor, then_block);
|
||||
}
|
||||
} else if let Some(else_block) = *else_block {
|
||||
if let Some((ty, map, key)) = check_cond(cx, check) {
|
||||
let mut visitor = InsertVisitor {
|
||||
cx,
|
||||
span: expr.span,
|
||||
ty,
|
||||
map,
|
||||
key,
|
||||
sole_expr: false,
|
||||
};
|
||||
|
||||
walk_expr(&mut visitor, else_block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_cond<'a>(cx: &LateContext<'_>, check: &'a Expr<'a>) -> Option<(&'static str, &'a Expr<'a>, &'a Expr<'a>)> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, _, params, _) = check.kind;
|
||||
if params.len() >= 2;
|
||||
if path.ident.name == sym!(contains_key);
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, _, key) = params[1].kind;
|
||||
then {
|
||||
let map = ¶ms[0];
|
||||
let obj_ty = cx.typeck_results().expr_ty(map).peel_refs();
|
||||
|
||||
return if match_type(cx, obj_ty, &paths::BTREEMAP) {
|
||||
Some(("BTreeMap", map, key))
|
||||
}
|
||||
else if is_type_diagnostic_item(cx, obj_ty, sym::hashmap_type) {
|
||||
Some(("HashMap", map, key))
|
||||
}
|
||||
else {
|
||||
None
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0;
|
||||
let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0;
|
||||
let sugg = if let Some(else_expr) = else_expr {
|
||||
let else_search = match find_insert_calls(cx, &contains_expr, else_expr) {
|
||||
Some(search) => search,
|
||||
None => return,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
struct InsertVisitor<'a, 'tcx, 'b> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
span: Span,
|
||||
ty: &'static str,
|
||||
map: &'b Expr<'b>,
|
||||
key: &'b Expr<'b>,
|
||||
sole_expr: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'b> Visitor<'tcx> for InsertVisitor<'a, 'tcx, 'b> {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, _, params, _) = expr.kind;
|
||||
if params.len() == 3;
|
||||
if path.ident.name == sym!(insert);
|
||||
if get_item_name(self.cx, self.map) == get_item_name(self.cx, ¶ms[0]);
|
||||
if SpanlessEq::new(self.cx).eq_expr(self.key, ¶ms[1]);
|
||||
if snippet_opt(self.cx, self.map.span) == snippet_opt(self.cx, params[0].span);
|
||||
then {
|
||||
span_lint_and_then(self.cx, MAP_ENTRY, self.span,
|
||||
&format!("usage of `contains_key` followed by `insert` on a `{}`", self.ty), |diag| {
|
||||
if self.sole_expr {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let help = format!("{}.entry({}).or_insert({});",
|
||||
snippet_with_applicability(self.cx, self.map.span, "map", &mut app),
|
||||
snippet_with_applicability(self.cx, params[1].span, "..", &mut app),
|
||||
snippet_with_applicability(self.cx, params[2].span, "..", &mut app));
|
||||
|
||||
diag.span_suggestion(
|
||||
self.span,
|
||||
"consider using",
|
||||
help,
|
||||
Applicability::MachineApplicable, // snippet
|
||||
);
|
||||
}
|
||||
else {
|
||||
let help = format!("consider using `{}.entry({})`",
|
||||
snippet(self.cx, self.map.span, "map"),
|
||||
snippet(self.cx, params[1].span, ".."));
|
||||
|
||||
diag.span_label(
|
||||
self.span,
|
||||
&help,
|
||||
);
|
||||
}
|
||||
});
|
||||
if then_search.edits.is_empty() && else_search.edits.is_empty() {
|
||||
// No insertions
|
||||
return;
|
||||
} else if then_search.edits.is_empty() || else_search.edits.is_empty() {
|
||||
// if .. { insert } else { .. } or if .. { .. } else { insert }
|
||||
let ((then_str, entry_kind), else_str) = match (else_search.edits.is_empty(), contains_expr.negated) {
|
||||
(true, true) => (
|
||||
then_search.snippet_vacant(cx, then_expr.span, &mut app),
|
||||
snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app),
|
||||
),
|
||||
(true, false) => (
|
||||
then_search.snippet_occupied(cx, then_expr.span, &mut app),
|
||||
snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app),
|
||||
),
|
||||
(false, true) => (
|
||||
else_search.snippet_occupied(cx, else_expr.span, &mut app),
|
||||
snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
|
||||
),
|
||||
(false, false) => (
|
||||
else_search.snippet_vacant(cx, else_expr.span, &mut app),
|
||||
snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
|
||||
),
|
||||
};
|
||||
format!(
|
||||
"if let {}::{} = {}.entry({}) {} else {}",
|
||||
map_ty.entry_path(),
|
||||
entry_kind,
|
||||
map_str,
|
||||
key_str,
|
||||
then_str,
|
||||
else_str,
|
||||
)
|
||||
} else {
|
||||
// if .. { insert } else { insert }
|
||||
let ((then_str, then_entry), (else_str, else_entry)) = if contains_expr.negated {
|
||||
(
|
||||
then_search.snippet_vacant(cx, then_expr.span, &mut app),
|
||||
else_search.snippet_occupied(cx, else_expr.span, &mut app),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
then_search.snippet_occupied(cx, then_expr.span, &mut app),
|
||||
else_search.snippet_vacant(cx, else_expr.span, &mut app),
|
||||
)
|
||||
};
|
||||
let indent_str = snippet_indent(cx, expr.span);
|
||||
let indent_str = indent_str.as_deref().unwrap_or("");
|
||||
format!(
|
||||
"match {}.entry({}) {{\n{indent} {entry}::{} => {}\n\
|
||||
{indent} {entry}::{} => {}\n{indent}}}",
|
||||
map_str,
|
||||
key_str,
|
||||
then_entry,
|
||||
reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())),
|
||||
else_entry,
|
||||
reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())),
|
||||
entry = map_ty.entry_path(),
|
||||
indent = indent_str,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if then_search.edits.is_empty() {
|
||||
// no insertions
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if !self.sole_expr {
|
||||
walk_expr(self, expr);
|
||||
// if .. { insert }
|
||||
if !then_search.allow_insert_closure {
|
||||
let (body_str, entry_kind) = if contains_expr.negated {
|
||||
then_search.snippet_vacant(cx, then_expr.span, &mut app)
|
||||
} else {
|
||||
then_search.snippet_occupied(cx, then_expr.span, &mut app)
|
||||
};
|
||||
format!(
|
||||
"if let {}::{} = {}.entry({}) {}",
|
||||
map_ty.entry_path(),
|
||||
entry_kind,
|
||||
map_str,
|
||||
key_str,
|
||||
body_str,
|
||||
)
|
||||
} else if let Some(insertion) = then_search.as_single_insertion() {
|
||||
let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0;
|
||||
if contains_expr.negated {
|
||||
if insertion.value.can_have_side_effects() {
|
||||
format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, value_str)
|
||||
} else {
|
||||
format!("{}.entry({}).or_insert({});", map_str, key_str, value_str)
|
||||
}
|
||||
} else {
|
||||
// TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
|
||||
// This would need to be a different lint.
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app);
|
||||
if contains_expr.negated {
|
||||
format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, block_str)
|
||||
} else {
|
||||
// TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
|
||||
// This would need to be a different lint.
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_ENTRY,
|
||||
expr.span,
|
||||
&format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()),
|
||||
"try this",
|
||||
sugg,
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum MapType {
|
||||
Hash,
|
||||
BTree,
|
||||
}
|
||||
impl MapType {
|
||||
fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::Hash => "HashMap",
|
||||
Self::BTree => "BTreeMap",
|
||||
}
|
||||
}
|
||||
fn entry_path(self) -> &'static str {
|
||||
match self {
|
||||
Self::Hash => "std::collections::hash_map::Entry",
|
||||
Self::BTree => "std::collections::btree_map::Entry",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ContainsExpr<'tcx> {
|
||||
negated: bool,
|
||||
map: &'tcx Expr<'tcx>,
|
||||
key: &'tcx Expr<'tcx>,
|
||||
call_ctxt: SyntaxContext,
|
||||
}
|
||||
fn try_parse_contains(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> {
|
||||
let mut negated = false;
|
||||
let expr = peel_hir_expr_while(expr, |e| match e.kind {
|
||||
ExprKind::Unary(UnOp::Not, e) => {
|
||||
negated = !negated;
|
||||
Some(e)
|
||||
},
|
||||
_ => None,
|
||||
});
|
||||
match expr.kind {
|
||||
ExprKind::MethodCall(
|
||||
_,
|
||||
_,
|
||||
[map, Expr {
|
||||
kind: ExprKind::AddrOf(_, _, key),
|
||||
span: key_span,
|
||||
..
|
||||
}],
|
||||
_,
|
||||
) if key_span.ctxt() == expr.span.ctxt() => {
|
||||
let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
|
||||
let expr = ContainsExpr {
|
||||
negated,
|
||||
map,
|
||||
key,
|
||||
call_ctxt: expr.span.ctxt(),
|
||||
};
|
||||
if match_def_path(cx, id, &paths::BTREEMAP_CONTAINS_KEY) {
|
||||
Some((MapType::BTree, expr))
|
||||
} else if match_def_path(cx, id, &paths::HASHMAP_CONTAINS_KEY) {
|
||||
Some((MapType::Hash, expr))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
struct InsertExpr<'tcx> {
|
||||
map: &'tcx Expr<'tcx>,
|
||||
key: &'tcx Expr<'tcx>,
|
||||
value: &'tcx Expr<'tcx>,
|
||||
}
|
||||
fn try_parse_insert(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
|
||||
if let ExprKind::MethodCall(_, _, [map, key, value], _) = expr.kind {
|
||||
let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
|
||||
if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) {
|
||||
Some(InsertExpr { map, key, value })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// An edit that will need to be made to move the expression to use the entry api
|
||||
#[derive(Clone, Copy)]
|
||||
enum Edit<'tcx> {
|
||||
/// A semicolon that needs to be removed. Used to create a closure for `insert_with`.
|
||||
RemoveSemi(Span),
|
||||
/// An insertion into the map.
|
||||
Insertion(Insertion<'tcx>),
|
||||
}
|
||||
impl Edit<'tcx> {
|
||||
fn as_insertion(self) -> Option<Insertion<'tcx>> {
|
||||
if let Self::Insertion(i) = self { Some(i) } else { None }
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy)]
|
||||
struct Insertion<'tcx> {
|
||||
call: &'tcx Expr<'tcx>,
|
||||
value: &'tcx Expr<'tcx>,
|
||||
}
|
||||
|
||||
/// This visitor needs to do a multiple things:
|
||||
/// * Find all usages of the map. An insertion can only be made before any other usages of the map.
|
||||
/// * Determine if there's an insertion using the same key. There's no need for the entry api
|
||||
/// otherwise.
|
||||
/// * Determine if the final statement executed is an insertion. This is needed to use
|
||||
/// `or_insert_with`.
|
||||
/// * Determine if there's any sub-expression that can't be placed in a closure.
|
||||
/// * Determine if there's only a single insert statement. `or_insert` can be used in this case.
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
struct InsertSearcher<'cx, 'tcx> {
|
||||
cx: &'cx LateContext<'tcx>,
|
||||
/// The map expression used in the contains call.
|
||||
map: &'tcx Expr<'tcx>,
|
||||
/// The key expression used in the contains call.
|
||||
key: &'tcx Expr<'tcx>,
|
||||
/// The context of the top level block. All insert calls must be in the same context.
|
||||
ctxt: SyntaxContext,
|
||||
/// Whether this expression can be safely moved into a closure.
|
||||
allow_insert_closure: bool,
|
||||
/// Whether this expression can use the entry api.
|
||||
can_use_entry: bool,
|
||||
/// Whether this expression is the final expression in this code path. This may be a statement.
|
||||
in_tail_pos: bool,
|
||||
// Is this expression a single insert. A slightly better suggestion can be made in this case.
|
||||
is_single_insert: bool,
|
||||
/// If the visitor has seen the map being used.
|
||||
is_map_used: bool,
|
||||
/// The locations where changes need to be made for the suggestion.
|
||||
edits: Vec<Edit<'tcx>>,
|
||||
/// A stack of loops the visitor is currently in.
|
||||
loops: Vec<HirId>,
|
||||
}
|
||||
impl<'tcx> InsertSearcher<'_, 'tcx> {
|
||||
/// Visit the expression as a branch in control flow. Multiple insert calls can be used, but
|
||||
/// only if they are on separate code paths. This will return whether the map was used in the
|
||||
/// given expression.
|
||||
fn visit_cond_arm(&mut self, e: &'tcx Expr<'_>) -> bool {
|
||||
let is_map_used = self.is_map_used;
|
||||
let in_tail_pos = self.in_tail_pos;
|
||||
self.visit_expr(e);
|
||||
let res = self.is_map_used;
|
||||
self.is_map_used = is_map_used;
|
||||
self.in_tail_pos = in_tail_pos;
|
||||
res
|
||||
}
|
||||
|
||||
/// Visits an expression which is not itself in a tail position, but other sibling expressions
|
||||
/// may be. e.g. if conditions
|
||||
fn visit_non_tail_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||
let in_tail_pos = self.in_tail_pos;
|
||||
self.in_tail_pos = false;
|
||||
self.visit_expr(e);
|
||||
self.in_tail_pos = in_tail_pos;
|
||||
}
|
||||
}
|
||||
impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
|
||||
type Map = ErasedMap<'tcx>;
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
|
||||
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
|
||||
match stmt.kind {
|
||||
StmtKind::Semi(e) => {
|
||||
self.visit_expr(e);
|
||||
|
||||
if self.in_tail_pos && self.allow_insert_closure {
|
||||
// The spans are used to slice the top level expression into multiple parts. This requires that
|
||||
// they all come from the same part of the source code.
|
||||
if stmt.span.ctxt() == self.ctxt && e.span.ctxt() == self.ctxt {
|
||||
self.edits
|
||||
.push(Edit::RemoveSemi(stmt.span.trim_start(e.span).unwrap_or(DUMMY_SP)));
|
||||
} else {
|
||||
self.allow_insert_closure = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
StmtKind::Expr(e) => self.visit_expr(e),
|
||||
StmtKind::Local(Local { init: Some(e), .. }) => {
|
||||
self.allow_insert_closure &= !self.in_tail_pos;
|
||||
self.in_tail_pos = false;
|
||||
self.is_single_insert = false;
|
||||
self.visit_expr(e);
|
||||
},
|
||||
_ => {
|
||||
self.allow_insert_closure &= !self.in_tail_pos;
|
||||
self.is_single_insert = false;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, block: &'tcx Block<'_>) {
|
||||
// If the block is in a tail position, then the last expression (possibly a statement) is in the
|
||||
// tail position. The rest, however, are not.
|
||||
match (block.stmts, block.expr) {
|
||||
([], None) => {
|
||||
self.allow_insert_closure &= !self.in_tail_pos;
|
||||
},
|
||||
([], Some(expr)) => self.visit_expr(expr),
|
||||
(stmts, Some(expr)) => {
|
||||
let in_tail_pos = self.in_tail_pos;
|
||||
self.in_tail_pos = false;
|
||||
for stmt in stmts {
|
||||
self.visit_stmt(stmt);
|
||||
}
|
||||
self.in_tail_pos = in_tail_pos;
|
||||
self.visit_expr(expr);
|
||||
},
|
||||
([stmts @ .., stmt], None) => {
|
||||
let in_tail_pos = self.in_tail_pos;
|
||||
self.in_tail_pos = false;
|
||||
for stmt in stmts {
|
||||
self.visit_stmt(stmt);
|
||||
}
|
||||
self.in_tail_pos = in_tail_pos;
|
||||
self.visit_stmt(stmt);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if !self.can_use_entry {
|
||||
return;
|
||||
}
|
||||
|
||||
match try_parse_insert(self.cx, expr) {
|
||||
Some(insert_expr) if SpanlessEq::new(self.cx).eq_expr(self.map, insert_expr.map) => {
|
||||
// Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api.
|
||||
if self.is_map_used
|
||||
|| !SpanlessEq::new(self.cx).eq_expr(self.key, insert_expr.key)
|
||||
|| expr.span.ctxt() != self.ctxt
|
||||
{
|
||||
self.can_use_entry = false;
|
||||
return;
|
||||
}
|
||||
|
||||
self.edits.push(Edit::Insertion(Insertion {
|
||||
call: expr,
|
||||
value: insert_expr.value,
|
||||
}));
|
||||
self.is_map_used = true;
|
||||
self.allow_insert_closure &= self.in_tail_pos;
|
||||
|
||||
// The value doesn't affect whether there is only a single insert expression.
|
||||
let is_single_insert = self.is_single_insert;
|
||||
self.visit_non_tail_expr(insert_expr.value);
|
||||
self.is_single_insert = is_single_insert;
|
||||
},
|
||||
_ if SpanlessEq::new(self.cx).eq_expr(self.map, expr) => {
|
||||
self.is_map_used = true;
|
||||
},
|
||||
_ => match expr.kind {
|
||||
ExprKind::If(cond_expr, then_expr, Some(else_expr)) => {
|
||||
self.is_single_insert = false;
|
||||
self.visit_non_tail_expr(cond_expr);
|
||||
// Each branch may contain it's own insert expression.
|
||||
let mut is_map_used = self.visit_cond_arm(then_expr);
|
||||
is_map_used |= self.visit_cond_arm(else_expr);
|
||||
self.is_map_used = is_map_used;
|
||||
},
|
||||
ExprKind::Match(scrutinee_expr, arms, _) => {
|
||||
self.is_single_insert = false;
|
||||
self.visit_non_tail_expr(scrutinee_expr);
|
||||
// Each branch may contain it's own insert expression.
|
||||
let mut is_map_used = self.is_map_used;
|
||||
for arm in arms {
|
||||
if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard {
|
||||
self.visit_non_tail_expr(guard)
|
||||
}
|
||||
is_map_used |= self.visit_cond_arm(arm.body);
|
||||
}
|
||||
self.is_map_used = is_map_used;
|
||||
},
|
||||
ExprKind::Loop(block, ..) => {
|
||||
self.loops.push(expr.hir_id);
|
||||
self.is_single_insert = false;
|
||||
self.allow_insert_closure &= !self.in_tail_pos;
|
||||
// Don't allow insertions inside of a loop.
|
||||
let edit_len = self.edits.len();
|
||||
self.visit_block(block);
|
||||
if self.edits.len() != edit_len {
|
||||
self.can_use_entry = false;
|
||||
}
|
||||
self.loops.pop();
|
||||
},
|
||||
ExprKind::Block(block, _) => self.visit_block(block),
|
||||
ExprKind::InlineAsm(_) | ExprKind::LlvmInlineAsm(_) => {
|
||||
self.can_use_entry = false;
|
||||
},
|
||||
_ => {
|
||||
self.allow_insert_closure &= !self.in_tail_pos;
|
||||
self.allow_insert_closure &= can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops);
|
||||
// Sub expressions are no longer in the tail position.
|
||||
self.is_single_insert = false;
|
||||
self.in_tail_pos = false;
|
||||
walk_expr(self, expr);
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct InsertSearchResults<'tcx> {
|
||||
edits: Vec<Edit<'tcx>>,
|
||||
allow_insert_closure: bool,
|
||||
is_single_insert: bool,
|
||||
}
|
||||
impl InsertSearchResults<'tcx> {
|
||||
fn as_single_insertion(&self) -> Option<Insertion<'tcx>> {
|
||||
self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap())
|
||||
}
|
||||
|
||||
fn snippet(
|
||||
&self,
|
||||
cx: &LateContext<'_>,
|
||||
mut span: Span,
|
||||
app: &mut Applicability,
|
||||
write_wrapped: impl Fn(&mut String, Insertion<'_>, SyntaxContext, &mut Applicability),
|
||||
) -> String {
|
||||
let ctxt = span.ctxt();
|
||||
let mut res = String::new();
|
||||
for insertion in self.edits.iter().filter_map(|e| e.as_insertion()) {
|
||||
res.push_str(&snippet_with_applicability(
|
||||
cx,
|
||||
span.until(insertion.call.span),
|
||||
"..",
|
||||
app,
|
||||
));
|
||||
if is_expr_used_or_unified(cx.tcx, insertion.call) {
|
||||
write_wrapped(&mut res, insertion, ctxt, app);
|
||||
} else {
|
||||
let _ = write!(
|
||||
res,
|
||||
"e.insert({})",
|
||||
snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0
|
||||
);
|
||||
}
|
||||
span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP);
|
||||
}
|
||||
res.push_str(&snippet_with_applicability(cx, span, "..", app));
|
||||
res
|
||||
}
|
||||
|
||||
fn snippet_occupied(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) {
|
||||
(
|
||||
self.snippet(cx, span, app, |res, insertion, ctxt, app| {
|
||||
// Insertion into a map would return `Some(&mut value)`, but the entry returns `&mut value`
|
||||
let _ = write!(
|
||||
res,
|
||||
"Some(e.insert({}))",
|
||||
snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0
|
||||
);
|
||||
}),
|
||||
"Occupied(mut e)",
|
||||
)
|
||||
}
|
||||
|
||||
fn snippet_vacant(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) {
|
||||
(
|
||||
self.snippet(cx, span, app, |res, insertion, ctxt, app| {
|
||||
// Insertion into a map would return `None`, but the entry returns a mutable reference.
|
||||
let _ = if is_expr_final_block_expr(cx.tcx, insertion.call) {
|
||||
write!(
|
||||
res,
|
||||
"e.insert({});\n{}None",
|
||||
snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0,
|
||||
snippet_indent(cx, insertion.call.span).as_deref().unwrap_or(""),
|
||||
)
|
||||
} else {
|
||||
write!(
|
||||
res,
|
||||
"{{ e.insert({}); None }}",
|
||||
snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0,
|
||||
)
|
||||
};
|
||||
}),
|
||||
"Vacant(e)",
|
||||
)
|
||||
}
|
||||
|
||||
fn snippet_closure(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String {
|
||||
let ctxt = span.ctxt();
|
||||
let mut res = String::new();
|
||||
for edit in &self.edits {
|
||||
match *edit {
|
||||
Edit::Insertion(insertion) => {
|
||||
// Cut out the value from `map.insert(key, value)`
|
||||
res.push_str(&snippet_with_applicability(
|
||||
cx,
|
||||
span.until(insertion.call.span),
|
||||
"..",
|
||||
app,
|
||||
));
|
||||
res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0);
|
||||
span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP);
|
||||
},
|
||||
Edit::RemoveSemi(semi_span) => {
|
||||
// Cut out the semicolon. This allows the value to be returned from the closure.
|
||||
res.push_str(&snippet_with_applicability(cx, span.until(semi_span), "..", app));
|
||||
span = span.trim_start(semi_span).unwrap_or(DUMMY_SP);
|
||||
},
|
||||
}
|
||||
}
|
||||
res.push_str(&snippet_with_applicability(cx, span, "..", app));
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
fn find_insert_calls(
|
||||
cx: &LateContext<'tcx>,
|
||||
contains_expr: &ContainsExpr<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
) -> Option<InsertSearchResults<'tcx>> {
|
||||
let mut s = InsertSearcher {
|
||||
cx,
|
||||
map: contains_expr.map,
|
||||
key: contains_expr.key,
|
||||
ctxt: expr.span.ctxt(),
|
||||
edits: Vec::new(),
|
||||
is_map_used: false,
|
||||
allow_insert_closure: true,
|
||||
can_use_entry: true,
|
||||
in_tail_pos: true,
|
||||
is_single_insert: true,
|
||||
loops: Vec::new(),
|
||||
};
|
||||
s.visit_expr(expr);
|
||||
let allow_insert_closure = s.allow_insert_closure;
|
||||
let is_single_insert = s.is_single_insert;
|
||||
let edits = s.edits;
|
||||
s.can_use_entry.then(|| InsertSearchResults {
|
||||
edits,
|
||||
allow_insert_closure,
|
||||
is_single_insert,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::{in_macro, match_path_ast};
|
||||
use clippy_utils::in_macro;
|
||||
use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
@ -126,7 +126,9 @@ impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_B
|
|||
|
||||
fn is_bool_ty(ty: &Ty) -> bool {
|
||||
if let TyKind::Path(None, path) = &ty.kind {
|
||||
return match_path_ast(path, &["bool"]);
|
||||
if let [name] = path.segments.as_slice() {
|
||||
return name.ident.name == sym::bool;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::paths;
|
||||
use clippy_utils::source::{snippet, snippet_opt};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_expn_of, last_path_segment, match_def_path, match_function_call};
|
||||
use if_chain::if_chain;
|
||||
|
@ -100,15 +101,15 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &
|
|||
return Some(format!("{:?}.to_string()", s.as_str()));
|
||||
}
|
||||
} else {
|
||||
let snip = snippet(cx, format_args.span, "<arg>");
|
||||
let sugg = Sugg::hir(cx, format_args, "<arg>");
|
||||
if let ExprKind::MethodCall(path, _, _, _) = format_args.kind {
|
||||
if path.ident.name == sym!(to_string) {
|
||||
return Some(format!("{}", snip));
|
||||
return Some(format!("{}", sugg));
|
||||
}
|
||||
} else if let ExprKind::Binary(..) = format_args.kind {
|
||||
return Some(format!("{}", snip));
|
||||
return Some(format!("{}", sugg));
|
||||
}
|
||||
return Some(format!("{}.to_string()", snip));
|
||||
return Some(format!("{}.to_string()", sugg.maybe_par()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -136,7 +137,7 @@ fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Strin
|
|||
if let Some(s_src) = snippet_opt(cx, lit.span) {
|
||||
// Simulate macro expansion, converting {{ and }} to { and }.
|
||||
let s_expand = s_src.replace("{{", "{").replace("}}", "}");
|
||||
return Some(format!("{}.to_string()", s_expand))
|
||||
return Some(format!("{}.to_string()", s_expand));
|
||||
}
|
||||
} else if s.as_str().is_empty() {
|
||||
return on_argumentv1_new(cx, &tup[0], arms);
|
||||
|
|
|
@ -215,9 +215,22 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
|
|||
// the snippet should look like " else \n " with maybe comments anywhere
|
||||
// it’s bad when there is a ‘\n’ after the “else”
|
||||
if let Some(else_snippet) = snippet_opt(cx, else_span);
|
||||
if let Some(else_pos) = else_snippet.find("else");
|
||||
if else_snippet[else_pos..].contains('\n');
|
||||
if let Some((pre_else, post_else)) = else_snippet.split_once("else");
|
||||
if let Some((_, post_else_post_eol)) = post_else.split_once('\n');
|
||||
|
||||
then {
|
||||
// Allow allman style braces `} \n else \n {`
|
||||
if_chain! {
|
||||
if is_block(else_);
|
||||
if let Some((_, pre_else_post_eol)) = pre_else.split_once('\n');
|
||||
// Exactly one eol before and after the else
|
||||
if !pre_else_post_eol.contains('\n');
|
||||
if !post_else_post_eol.contains('\n');
|
||||
then {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let else_desc = if is_if(else_) { "if" } else { "{..}" };
|
||||
span_lint_and_note(
|
||||
cx,
|
||||
|
|
|
@ -73,7 +73,7 @@ impl LateLintPass<'_> for FromOverInto {
|
|||
cx.tcx.sess.source_map().guess_head_span(item.span),
|
||||
"an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true",
|
||||
None,
|
||||
"consider to implement `From` instead",
|
||||
&format!("consider to implement `From<{}>` instead", impl_trait_ref.self_ty()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::source::snippet_with_macro_callsite;
|
||||
use clippy_utils::{match_qpath, meets_msrv, parent_node_is_if_expr};
|
||||
use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
@ -67,7 +68,7 @@ impl LateLintPass<'_> for IfThenSomeElseNone {
|
|||
}
|
||||
|
||||
// We only care about the top-most `if` in the chain
|
||||
if parent_node_is_if_expr(expr, cx) {
|
||||
if is_else_clause(cx.tcx, expr) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -77,12 +78,12 @@ impl LateLintPass<'_> for IfThenSomeElseNone {
|
|||
if let Some(then_expr) = then_block.expr;
|
||||
if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind;
|
||||
if let ExprKind::Path(ref then_call_qpath) = then_call.kind;
|
||||
if match_qpath(then_call_qpath, &clippy_utils::paths::OPTION_SOME);
|
||||
if is_lang_ctor(cx, then_call_qpath, OptionSome);
|
||||
if let ExprKind::Block(els_block, _) = els.kind;
|
||||
if els_block.stmts.is_empty();
|
||||
if let Some(els_expr) = els_block.expr;
|
||||
if let ExprKind::Path(ref els_call_qpath) = els_expr.kind;
|
||||
if match_qpath(els_call_qpath, &clippy_utils::paths::OPTION_NONE);
|
||||
if let ExprKind::Path(ref qpath) = els_expr.kind;
|
||||
if is_lang_ctor(cx, qpath, OptionNone);
|
||||
then {
|
||||
let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
|
||||
let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
|
||||
|
|
|
@ -22,7 +22,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
|||
use clippy_utils::paths;
|
||||
use clippy_utils::source::{snippet, snippet_opt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{differing_macro_contexts, match_path};
|
||||
use clippy_utils::{differing_macro_contexts, match_def_path};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for public `impl` or `fn` missing generalization
|
||||
|
@ -333,12 +333,13 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't
|
|||
if let ExprKind::Call(fun, args) = e.kind;
|
||||
if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind;
|
||||
if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind;
|
||||
if let Some(ty_did) = ty_path.res.opt_def_id();
|
||||
then {
|
||||
if !TyS::same_type(self.target.ty(), self.maybe_typeck_results.unwrap().expr_ty(e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if match_path(ty_path, &paths::HASHMAP) {
|
||||
if match_def_path(self.cx, ty_did, &paths::HASHMAP) {
|
||||
if method.ident.name == sym::new {
|
||||
self.suggestions
|
||||
.insert(e.span, "HashMap::default()".to_string());
|
||||
|
@ -351,7 +352,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't
|
|||
),
|
||||
);
|
||||
}
|
||||
} else if match_path(ty_path, &paths::HASHSET) {
|
||||
} else if match_def_path(self.cx, ty_did, &paths::HASHSET) {
|
||||
if method.ident.name == sym::new {
|
||||
self.suggestions
|
||||
.insert(e.span, "HashSet::default()".to_string());
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::{in_macro, match_qpath, SpanlessEq};
|
||||
use clippy_utils::{in_macro, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, StmtKind};
|
||||
use rustc_hir::{lang_items::LangItem, BinOpKind, Expr, ExprKind, QPath, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
|
@ -87,7 +87,13 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
|
|||
|
||||
// Get the variable name
|
||||
let var_name = ares_path.segments[0].ident.name.as_str();
|
||||
const INT_TYPES: [&str; 5] = ["i8", "i16", "i32", "i64", "i128"];
|
||||
const INT_TYPES: [LangItem; 5] = [
|
||||
LangItem::I8,
|
||||
LangItem::I16,
|
||||
LangItem::I32,
|
||||
LangItem::I64,
|
||||
LangItem::Isize
|
||||
];
|
||||
|
||||
match cond_num_val.kind {
|
||||
ExprKind::Lit(ref cond_lit) => {
|
||||
|
@ -99,17 +105,30 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
|
|||
};
|
||||
}
|
||||
},
|
||||
ExprKind::Path(ref cond_num_path) => {
|
||||
if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "MIN"])) {
|
||||
print_lint_and_sugg(cx, &var_name, expr);
|
||||
};
|
||||
},
|
||||
ExprKind::Call(func, _) => {
|
||||
if let ExprKind::Path(ref cond_num_path) = func.kind {
|
||||
if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "min_value"])) {
|
||||
print_lint_and_sugg(cx, &var_name, expr);
|
||||
ExprKind::Path(QPath::TypeRelative(_, name)) => {
|
||||
if_chain! {
|
||||
if name.ident.as_str() == "MIN";
|
||||
if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(const_id);
|
||||
let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok());
|
||||
if int_ids.any(|int_id| int_id == impl_id);
|
||||
then {
|
||||
print_lint_and_sugg(cx, &var_name, expr)
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
ExprKind::Call(func, []) => {
|
||||
if_chain! {
|
||||
if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind;
|
||||
if name.ident.as_str() == "min_value";
|
||||
if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(func_id);
|
||||
let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok());
|
||||
if int_ids.any(|int_id| int_id == impl_id);
|
||||
then {
|
||||
print_lint_and_sugg(cx, &var_name, expr)
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::in_macro;
|
||||
use clippy_utils::source::snippet;
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
@ -66,6 +67,7 @@ declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRU
|
|||
impl LateLintPass<'_> for InconsistentStructConstructor {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if !in_macro(expr.span);
|
||||
if let ExprKind::Struct(qpath, fields, base) = expr.kind;
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if let Some(adt_def) = ty.ty_adt_def();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::ty::{implements_trait, match_type};
|
||||
use clippy_utils::{get_trait_def_id, higher, match_qpath, paths};
|
||||
use clippy_utils::{get_trait_def_id, higher, is_qpath_def_path, paths};
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
@ -163,7 +163,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
|
|||
ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
|
||||
ExprKind::Call(path, _) => {
|
||||
if let ExprKind::Path(ref qpath) = path.kind {
|
||||
match_qpath(qpath, &paths::REPEAT).into()
|
||||
is_qpath_def_path(cx, qpath, path.hir_id, &paths::ITER_REPEAT).into()
|
||||
} else {
|
||||
Finite
|
||||
}
|
||||
|
|
|
@ -179,6 +179,7 @@ mod await_holding_invalid;
|
|||
mod bit_mask;
|
||||
mod blacklisted_name;
|
||||
mod blocks_in_if_conditions;
|
||||
mod bool_assert_comparison;
|
||||
mod booleans;
|
||||
mod bytecount;
|
||||
mod cargo_common_metadata;
|
||||
|
@ -357,6 +358,7 @@ mod unicode;
|
|||
mod unit_return_expecting_ord;
|
||||
mod unit_types;
|
||||
mod unnamed_address;
|
||||
mod unnecessary_self_imports;
|
||||
mod unnecessary_sort_by;
|
||||
mod unnecessary_wraps;
|
||||
mod unnested_or_patterns;
|
||||
|
@ -391,6 +393,7 @@ pub use crate::utils::conf::Conf;
|
|||
///
|
||||
/// Used in `./src/driver.rs`.
|
||||
pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) {
|
||||
// NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
|
||||
store.register_pre_expansion_pass(|| box write::Write::default());
|
||||
store.register_pre_expansion_pass(|| box attrs::EarlyAttributes);
|
||||
store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro);
|
||||
|
@ -494,22 +497,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
"clippy::unsafe_vector_initialization",
|
||||
"the replacement suggested by this lint had substantially different behavior",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::invalid_ref",
|
||||
"superseded by rustc lint `invalid_value`",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::unused_collect",
|
||||
"`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::into_iter_on_array",
|
||||
"this lint has been uplifted to rustc and is now called `array_into_iter`",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::unused_label",
|
||||
"this lint has been uplifted to rustc and is now called `unused_labels`",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::replace_consts",
|
||||
"associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants",
|
||||
|
@ -518,26 +509,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
"clippy::regex_macro",
|
||||
"the regex! macro has been removed from the regex crate in 2018",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::drop_bounds",
|
||||
"this lint has been uplifted to rustc and is now called `drop_bounds`",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::temporary_cstring_as_ptr",
|
||||
"this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::panic_params",
|
||||
"this lint has been uplifted to rustc and is now called `panic_fmt`",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::unknown_clippy_lints",
|
||||
"this lint has been integrated into the `unknown_lints` rustc lint",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::find_map",
|
||||
"this lint has been replaced by `manual_find_map`, a more specific lint",
|
||||
);
|
||||
store.register_removed(
|
||||
"clippy::filter_map",
|
||||
"this lint has been replaced by `manual_filter_map`, a more specific lint",
|
||||
);
|
||||
// end deprecated lints, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
// begin register lints, do not remove this comment, it’s used in `update_lints`
|
||||
|
@ -592,6 +571,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
bit_mask::VERBOSE_BIT_MASK,
|
||||
blacklisted_name::BLACKLISTED_NAME,
|
||||
blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
|
||||
bool_assert_comparison::BOOL_ASSERT_COMPARISON,
|
||||
booleans::LOGIC_BUG,
|
||||
booleans::NONMINIMAL_BOOL,
|
||||
bytecount::NAIVE_BYTECOUNT,
|
||||
|
@ -783,17 +763,18 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
methods::BYTES_NTH,
|
||||
methods::CHARS_LAST_CMP,
|
||||
methods::CHARS_NEXT_CMP,
|
||||
methods::CLONED_INSTEAD_OF_COPIED,
|
||||
methods::CLONE_DOUBLE_REF,
|
||||
methods::CLONE_ON_COPY,
|
||||
methods::CLONE_ON_REF_PTR,
|
||||
methods::EXPECT_FUN_CALL,
|
||||
methods::EXPECT_USED,
|
||||
methods::FILETYPE_IS_FILE,
|
||||
methods::FILTER_MAP,
|
||||
methods::FILTER_MAP_IDENTITY,
|
||||
methods::FILTER_MAP_NEXT,
|
||||
methods::FILTER_NEXT,
|
||||
methods::FLAT_MAP_IDENTITY,
|
||||
methods::FLAT_MAP_OPTION,
|
||||
methods::FROM_ITER_INSTEAD_OF_COLLECT,
|
||||
methods::GET_UNWRAP,
|
||||
methods::IMPLICIT_CLONE,
|
||||
|
@ -904,6 +885,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
|
||||
precedence::PRECEDENCE,
|
||||
ptr::CMP_NULL,
|
||||
ptr::INVALID_NULL_PTR_USAGE,
|
||||
ptr::MUT_FROM_REF,
|
||||
ptr::PTR_ARG,
|
||||
ptr_eq::PTR_EQ,
|
||||
|
@ -988,6 +970,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
unit_types::UNIT_CMP,
|
||||
unnamed_address::FN_ADDRESS_COMPARISONS,
|
||||
unnamed_address::VTABLE_ADDRESS_COMPARISONS,
|
||||
unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
|
||||
unnecessary_sort_by::UNNECESSARY_SORT_BY,
|
||||
unnecessary_wraps::UNNECESSARY_WRAPS,
|
||||
unnested_or_patterns::UNNESTED_OR_PATTERNS,
|
||||
|
@ -1073,6 +1056,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback);
|
||||
store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor);
|
||||
store.register_late_pass(|| box non_octal_unix_permissions::NonOctalUnixPermissions);
|
||||
store.register_early_pass(|| box unnecessary_self_imports::UnnecessarySelfImports);
|
||||
|
||||
let msrv = conf.msrv.as_ref().and_then(|s| {
|
||||
parse_msrv(s, None, None).or_else(|| {
|
||||
|
@ -1295,6 +1279,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| box from_str_radix_10::FromStrRadix10);
|
||||
store.register_late_pass(|| box manual_map::ManualMap);
|
||||
store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv));
|
||||
store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison);
|
||||
|
||||
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
|
||||
LintId::of(arithmetic::FLOAT_ARITHMETIC),
|
||||
|
@ -1345,6 +1330,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(strings::STRING_TO_STRING),
|
||||
LintId::of(strings::STR_TO_STRING),
|
||||
LintId::of(types::RC_BUFFER),
|
||||
LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
|
||||
LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
|
||||
LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
|
||||
LintId::of(write::PRINT_STDERR),
|
||||
|
@ -1404,8 +1390,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
|
||||
LintId::of(matches::MATCH_WILD_ERR_ARM),
|
||||
LintId::of(matches::SINGLE_MATCH_ELSE),
|
||||
LintId::of(methods::FILTER_MAP),
|
||||
LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
|
||||
LintId::of(methods::FILTER_MAP_NEXT),
|
||||
LintId::of(methods::FLAT_MAP_OPTION),
|
||||
LintId::of(methods::IMPLICIT_CLONE),
|
||||
LintId::of(methods::INEFFICIENT_TO_STRING),
|
||||
LintId::of(methods::MAP_FLATTEN),
|
||||
|
@ -1428,6 +1415,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
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::NON_ASCII_LITERAL),
|
||||
|
@ -1474,6 +1462,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
|
||||
LintId::of(blacklisted_name::BLACKLISTED_NAME),
|
||||
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
|
||||
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
|
||||
LintId::of(booleans::LOGIC_BUG),
|
||||
LintId::of(booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(casts::CAST_REF_TO_MUT),
|
||||
|
@ -1673,6 +1662,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
|
||||
LintId::of(precedence::PRECEDENCE),
|
||||
LintId::of(ptr::CMP_NULL),
|
||||
LintId::of(ptr::INVALID_NULL_PTR_USAGE),
|
||||
LintId::of(ptr::MUT_FROM_REF),
|
||||
LintId::of(ptr::PTR_ARG),
|
||||
LintId::of(ptr_eq::PTR_EQ),
|
||||
|
@ -1715,7 +1705,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
|
||||
LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
|
||||
LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
|
||||
LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
|
||||
LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
|
||||
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
|
||||
LintId::of(transmute::WRONG_TRANSMUTE),
|
||||
|
@ -1759,6 +1748,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
|
||||
LintId::of(blacklisted_name::BLACKLISTED_NAME),
|
||||
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
|
||||
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
|
||||
LintId::of(casts::FN_TO_NUMERIC_CAST),
|
||||
LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
|
||||
LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
|
||||
|
@ -1949,7 +1939,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
|
||||
LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
|
||||
LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
|
||||
LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
|
||||
LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
|
||||
LintId::of(types::BORROWED_BOX),
|
||||
LintId::of(types::TYPE_COMPLEXITY),
|
||||
|
@ -2012,6 +2001,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
|
||||
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
|
||||
LintId::of(ptr::INVALID_NULL_PTR_USAGE),
|
||||
LintId::of(ptr::MUT_FROM_REF),
|
||||
LintId::of(ranges::REVERSED_EMPTY_RANGES),
|
||||
LintId::of(regex::INVALID_REGEX),
|
||||
|
@ -2153,6 +2143,15 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
|
|||
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");
|
||||
|
||||
// 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_panic");
|
||||
ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints");
|
||||
}
|
||||
|
||||
// only exists to let the dogfood integration test works.
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
use super::EXPLICIT_INTO_ITER_LOOP;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{match_trait_method, paths};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::TyS;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, args: &'hir [Expr<'hir>], arg: &Expr<'_>) {
|
||||
let receiver_ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]);
|
||||
if !TyS::same_type(receiver_ty, receiver_ty_adjusted) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, self_arg: &'hir Expr<'hir>, call_expr: &Expr<'_>) {
|
||||
let self_ty = cx.typeck_results().expr_ty(self_arg);
|
||||
let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
|
||||
if !(TyS::same_type(self_ty, self_ty_adjusted) && match_trait_method(cx, call_expr, &paths::INTO_ITERATOR)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
|
||||
let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
EXPLICIT_INTO_ITER_LOOP,
|
||||
arg.span,
|
||||
call_expr.span,
|
||||
"it is more concise to loop over containers instead of using explicit \
|
||||
iteration methods",
|
||||
"to write this more concisely, try",
|
||||
|
|
|
@ -9,12 +9,12 @@ use rustc_lint::LateContext;
|
|||
use rustc_middle::ty::{self, Ty, TyS};
|
||||
use rustc_span::sym;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, method_name: &str) {
|
||||
let should_lint = match method_name {
|
||||
"iter" | "iter_mut" => is_ref_iterable_type(cx, &args[0]),
|
||||
"iter" | "iter_mut" => is_ref_iterable_type(cx, self_arg),
|
||||
"into_iter" if match_trait_method(cx, arg, &paths::INTO_ITERATOR) => {
|
||||
let receiver_ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]);
|
||||
let receiver_ty = cx.typeck_results().expr_ty(self_arg);
|
||||
let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
|
||||
let ref_receiver_ty = cx.tcx.mk_ref(
|
||||
cx.tcx.lifetimes.re_erased,
|
||||
ty::TypeAndMut {
|
||||
|
@ -32,7 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, met
|
|||
}
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability);
|
||||
let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability);
|
||||
let muta = if method_name == "iter_mut" { "mut " } else { "" };
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use super::utils::make_iterator_snippet;
|
||||
use super::MANUAL_FLATTEN;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{is_ok_ctor, is_some_ctor, path_to_local_id};
|
||||
use clippy_utils::{is_lang_ctor, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind};
|
||||
use rustc_hir::LangItem::{OptionSome, ResultOk};
|
||||
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, StmtKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::source_map::Span;
|
||||
|
@ -42,9 +43,9 @@ pub(super) fn check<'tcx>(
|
|||
if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
|
||||
if path_to_local_id(match_expr, pat_hir_id);
|
||||
// Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
|
||||
if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind;
|
||||
let some_ctor = is_some_ctor(cx, path.res);
|
||||
let ok_ctor = is_ok_ctor(cx, path.res);
|
||||
if let PatKind::TupleStruct(ref qpath, _, _) = match_arms[0].pat.kind;
|
||||
let some_ctor = is_lang_ctor(cx, qpath, OptionSome);
|
||||
let ok_ctor = is_lang_ctor(cx, qpath, ResultOk);
|
||||
if some_ctor || ok_ctor;
|
||||
then {
|
||||
let if_let_type = if some_ctor { "Some" } else { "Ok" };
|
||||
|
|
|
@ -602,22 +602,19 @@ fn check_for_loop<'tcx>(
|
|||
fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) {
|
||||
let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
|
||||
|
||||
if let ExprKind::MethodCall(method, _, args, _) = arg.kind {
|
||||
// just the receiver, no arguments
|
||||
if args.len() == 1 {
|
||||
let method_name = &*method.ident.as_str();
|
||||
// check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
|
||||
match method_name {
|
||||
"iter" | "iter_mut" => explicit_iter_loop::check(cx, args, arg, method_name),
|
||||
"into_iter" => {
|
||||
explicit_iter_loop::check(cx, args, arg, method_name);
|
||||
explicit_into_iter_loop::check(cx, args, arg);
|
||||
},
|
||||
"next" => {
|
||||
next_loop_linted = iter_next_loop::check(cx, arg, expr);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
if let ExprKind::MethodCall(method, _, [self_arg], _) = arg.kind {
|
||||
let method_name = &*method.ident.as_str();
|
||||
// check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
|
||||
match method_name {
|
||||
"iter" | "iter_mut" => explicit_iter_loop::check(cx, self_arg, arg, method_name),
|
||||
"into_iter" => {
|
||||
explicit_iter_loop::check(cx, self_arg, arg, method_name);
|
||||
explicit_into_iter_loop::check(cx, self_arg, arg);
|
||||
},
|
||||
"next" => {
|
||||
next_loop_linted = iter_next_loop::check(cx, arg, expr);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
|
|||
ExprKind::Binary(_, e1, e2)
|
||||
| ExprKind::Assign(e1, e2, _)
|
||||
| ExprKind::AssignOp(_, e1, e2)
|
||||
| ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().cloned(), main_loop_id),
|
||||
| ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id),
|
||||
ExprKind::Loop(b, _, _, _) => {
|
||||
// Break can come from the inner loop so remove them.
|
||||
absorb_break(&never_loop_block(b, main_loop_id))
|
||||
|
|
|
@ -112,6 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
|
|||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||
if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use));
|
||||
if let Res::Def(DefKind::Mod, id) = path.res;
|
||||
if !id.is_local();
|
||||
then {
|
||||
for kid in cx.tcx.item_children(id).iter() {
|
||||
if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
|
||||
use clippy_utils::{in_constant, is_allowed, is_else_clause, match_def_path, match_var, paths, peel_hir_expr_refs};
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
|
||||
use clippy_utils::{
|
||||
can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs,
|
||||
};
|
||||
use rustc_ast::util::parser::PREC_POSTFIX;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{
|
||||
def::Res,
|
||||
intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
|
||||
Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath,
|
||||
};
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
@ -103,12 +102,18 @@ impl LateLintPass<'_> for ManualMap {
|
|||
None => return,
|
||||
};
|
||||
|
||||
// These two lints will go back and forth with each other.
|
||||
if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit
|
||||
&& !is_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// `map` won't perform any adjustments.
|
||||
if !cx.typeck_results().expr_adjustments(some_expr).is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if !can_move_expr_to_closure(cx, some_expr) {
|
||||
return;
|
||||
}
|
||||
|
@ -192,51 +197,6 @@ impl LateLintPass<'_> for ManualMap {
|
|||
}
|
||||
}
|
||||
|
||||
// Checks if the expression can be moved into a closure as is.
|
||||
fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||
struct V<'cx, 'tcx> {
|
||||
cx: &'cx LateContext<'tcx>,
|
||||
make_closure: bool,
|
||||
}
|
||||
impl Visitor<'tcx> for V<'_, 'tcx> {
|
||||
type Map = ErasedMap<'tcx>;
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||
match e.kind {
|
||||
ExprKind::Break(..)
|
||||
| ExprKind::Continue(_)
|
||||
| ExprKind::Ret(_)
|
||||
| ExprKind::Yield(..)
|
||||
| ExprKind::InlineAsm(_)
|
||||
| ExprKind::LlvmInlineAsm(_) => {
|
||||
self.make_closure = false;
|
||||
},
|
||||
// Accessing a field of a local value can only be done if the type isn't
|
||||
// partially moved.
|
||||
ExprKind::Field(base_expr, _)
|
||||
if matches!(
|
||||
base_expr.kind,
|
||||
ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. }))
|
||||
) && can_partially_move_ty(self.cx, self.cx.typeck_results().expr_ty(base_expr)) =>
|
||||
{
|
||||
// TODO: check if the local has been partially moved. Assume it has for now.
|
||||
self.make_closure = false;
|
||||
return;
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
walk_expr(self, e);
|
||||
}
|
||||
}
|
||||
|
||||
let mut v = V { cx, make_closure: true };
|
||||
v.visit_expr(expr);
|
||||
v.make_closure
|
||||
}
|
||||
|
||||
// Checks whether the expression could be passed as a function, or whether a closure is needed.
|
||||
// Returns the function to be passed to `map` if it exists.
|
||||
fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
|
@ -269,20 +229,9 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxCon
|
|||
match pat.kind {
|
||||
PatKind::Wild => Some(OptionPat::Wild),
|
||||
PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
|
||||
PatKind::Path(QPath::Resolved(None, path))
|
||||
if path
|
||||
.res
|
||||
.opt_def_id()
|
||||
.map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)) =>
|
||||
{
|
||||
Some(OptionPat::None)
|
||||
},
|
||||
PatKind::TupleStruct(QPath::Resolved(None, path), [pattern], _)
|
||||
if path
|
||||
.res
|
||||
.opt_def_id()
|
||||
.map_or(false, |id| match_def_path(cx, id, &paths::OPTION_SOME))
|
||||
&& pat.span.ctxt() == ctxt =>
|
||||
PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None),
|
||||
PatKind::TupleStruct(ref qpath, [pattern], _)
|
||||
if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt =>
|
||||
{
|
||||
Some(OptionPat::Some { pattern, ref_count })
|
||||
},
|
||||
|
@ -298,17 +247,11 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte
|
|||
match expr.kind {
|
||||
ExprKind::Call(
|
||||
Expr {
|
||||
kind: ExprKind::Path(QPath::Resolved(None, path)),
|
||||
kind: ExprKind::Path(ref qpath),
|
||||
..
|
||||
},
|
||||
[arg],
|
||||
) if ctxt == expr.span.ctxt() => {
|
||||
if match_def_path(cx, path.res.opt_def_id()?, &paths::OPTION_SOME) {
|
||||
Some(arg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(arg),
|
||||
ExprKind::Block(
|
||||
Block {
|
||||
stmts: [],
|
||||
|
@ -324,10 +267,7 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxConte
|
|||
// Checks for the `None` value.
|
||||
fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||
match expr.kind {
|
||||
ExprKind::Path(QPath::Resolved(None, path)) => path
|
||||
.res
|
||||
.opt_def_id()
|
||||
.map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)),
|
||||
ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
|
||||
ExprKind::Block(
|
||||
Block {
|
||||
stmts: [],
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{match_qpath, path_to_local_id, paths};
|
||||
use clippy_utils::{is_lang_ctor, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{ResultErr, ResultOk};
|
||||
use rustc_hir::{Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LintContext;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -54,7 +55,7 @@ impl LateLintPass<'_> for ManualOkOr {
|
|||
let or_expr = &args[1];
|
||||
if is_ok_wrapping(cx, &args[2]);
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
|
||||
if match_qpath(err_path, &paths::RESULT_ERR);
|
||||
if is_lang_ctor(cx, err_path, ResultErr);
|
||||
if let Some(method_receiver_snippet) = snippet_opt(cx, method_receiver.span);
|
||||
if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
|
||||
if let Some(indent) = indent_of(cx, scrutinee.span);
|
||||
|
@ -81,7 +82,7 @@ impl LateLintPass<'_> for ManualOkOr {
|
|||
|
||||
fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Path(ref qpath) = map_expr.kind {
|
||||
if match_qpath(qpath, &paths::RESULT_OK) {
|
||||
if is_lang_ctor(cx, qpath, ResultOk) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +91,7 @@ fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
|
|||
let body = cx.tcx.hir().body(body_id);
|
||||
if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
|
||||
if match_qpath(ok_path, &paths::RESULT_OK);
|
||||
if is_lang_ctor(cx, ok_path, ResultOk);
|
||||
then { path_to_local_id(ok_arg, param_id) } else { false }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
|||
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::usage::contains_return_break_continue_macro;
|
||||
use clippy_utils::{in_constant, match_qpath, path_to_local_id, paths, sugg};
|
||||
use clippy_utils::{in_constant, is_lang_ctor, path_to_local_id, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind};
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LintContext;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
@ -68,23 +69,21 @@ impl Case {
|
|||
}
|
||||
|
||||
fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
|
||||
fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
|
||||
if_chain! {
|
||||
if arms.len() == 2;
|
||||
if arms.iter().all(|arm| arm.guard.is_none());
|
||||
if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)|
|
||||
if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| {
|
||||
match arm.pat.kind {
|
||||
PatKind::Path(ref some_qpath) =>
|
||||
match_qpath(some_qpath, &paths::OPTION_NONE),
|
||||
PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) =>
|
||||
match_qpath(err_qpath, &paths::RESULT_ERR),
|
||||
PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
|
||||
PatKind::TupleStruct(ref qpath, &[pat], _) =>
|
||||
matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr),
|
||||
_ => false,
|
||||
}
|
||||
);
|
||||
});
|
||||
let unwrap_arm = &arms[1 - idx];
|
||||
if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
|
||||
if match_qpath(unwrap_qpath, &paths::OPTION_SOME)
|
||||
|| match_qpath(unwrap_qpath, &paths::RESULT_OK);
|
||||
if let PatKind::TupleStruct(ref qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
|
||||
if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
|
||||
if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
|
||||
if path_to_local_id(unwrap_arm.body, binding_hir_id);
|
||||
if !contains_return_break_continue_macro(or_arm.body);
|
||||
|
@ -106,7 +105,7 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(or_arm) = applicable_or_arm(match_arms);
|
||||
if let Some(or_arm) = applicable_or_arm(cx, match_arms);
|
||||
if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span);
|
||||
if let Some(indent) = indent_of(cx, expr.span);
|
||||
if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_adjusted, is_trait_method, match_path, match_var, paths, remove_blocks};
|
||||
use clippy_utils::{is_adjusted, is_qpath_def_path, is_trait_method, match_var, paths, remove_blocks};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
|
||||
|
@ -80,7 +80,7 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a
|
|||
fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
match expr.kind {
|
||||
ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
|
||||
ExprKind::Path(QPath::Resolved(_, path)) => match_path(path, &paths::STD_CONVERT_IDENTITY),
|
||||
ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use clippy_utils::sugg::Sugg;
|
|||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
|
||||
use clippy_utils::visitors::LocalUsedVisitor;
|
||||
use clippy_utils::{
|
||||
get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, path_to_local,
|
||||
get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, path_to_local,
|
||||
path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, strip_pat_refs,
|
||||
};
|
||||
use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
|
||||
|
@ -15,6 +15,7 @@ use if_chain::if_chain;
|
|||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{
|
||||
self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource,
|
||||
Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind,
|
||||
|
@ -422,7 +423,12 @@ declare_clippy_lint! {
|
|||
/// **Why is this bad?** It's more concise and clear to just use the proper
|
||||
/// utility function
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
/// **Known problems:** This will change the drop order for the matched type. Both `if let` and
|
||||
/// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
|
||||
/// value before entering the block. For most types this change will not matter, but for a few
|
||||
/// types this will not be an acceptable change (e.g. locks). See the
|
||||
/// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
|
||||
/// drop order.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
|
@ -737,8 +743,11 @@ fn report_single_match_single_pattern(
|
|||
let (msg, sugg) = if_chain! {
|
||||
if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
|
||||
let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
|
||||
if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait();
|
||||
if ty.is_integral() || ty.is_char() || ty.is_str() || implements_trait(cx, ty, trait_id, &[]);
|
||||
if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait();
|
||||
if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait();
|
||||
if ty.is_integral() || ty.is_char() || ty.is_str()
|
||||
|| (implements_trait(cx, ty, spe_trait_id, &[])
|
||||
&& implements_trait(cx, ty, pe_trait_id, &[ty.into()]));
|
||||
then {
|
||||
// scrutinee derives PartialEq and the pattern is a constant.
|
||||
let pat_ref_count = match pat.kind {
|
||||
|
@ -1120,7 +1129,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
|
|||
Applicability::MaybeIncorrect,
|
||||
),
|
||||
variants => {
|
||||
let mut suggestions: Vec<_> = variants.iter().cloned().map(format_suggestion).collect();
|
||||
let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
|
||||
let message = if adt_def.is_variant_list_non_exhaustive() {
|
||||
suggestions.push("_".into());
|
||||
"wildcard matches known variants and will also match future added variants"
|
||||
|
@ -1189,10 +1198,10 @@ fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], e
|
|||
|
||||
fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
|
||||
if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
|
||||
let arm_ref: Option<BindingAnnotation> = if is_none_arm(&arms[0]) {
|
||||
is_ref_some_arm(&arms[1])
|
||||
} else if is_none_arm(&arms[1]) {
|
||||
is_ref_some_arm(&arms[0])
|
||||
let arm_ref: Option<BindingAnnotation> = if is_none_arm(cx, &arms[0]) {
|
||||
is_ref_some_arm(cx, &arms[1])
|
||||
} else if is_none_arm(cx, &arms[1]) {
|
||||
is_ref_some_arm(cx, &arms[0])
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -1500,7 +1509,7 @@ fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'
|
|||
/// Gets all arms that are unbounded `PatRange`s.
|
||||
fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<Constant>> {
|
||||
arms.iter()
|
||||
.flat_map(|arm| {
|
||||
.filter_map(|arm| {
|
||||
if let Arm { pat, guard: None, .. } = *arm {
|
||||
if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
|
||||
let lhs = match lhs {
|
||||
|
@ -1575,20 +1584,20 @@ fn is_unit_expr(expr: &Expr<'_>) -> bool {
|
|||
}
|
||||
|
||||
// Checks if arm has the form `None => None`
|
||||
fn is_none_arm(arm: &Arm<'_>) -> bool {
|
||||
matches!(arm.pat.kind, PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE))
|
||||
fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
|
||||
matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
|
||||
}
|
||||
|
||||
// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
|
||||
fn is_ref_some_arm(arm: &Arm<'_>) -> Option<BindingAnnotation> {
|
||||
fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
|
||||
if_chain! {
|
||||
if let PatKind::TupleStruct(ref path, pats, _) = arm.pat.kind;
|
||||
if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME);
|
||||
if let PatKind::TupleStruct(ref qpath, pats, _) = arm.pat.kind;
|
||||
if is_lang_ctor(cx, qpath, OptionSome);
|
||||
if let PatKind::Binding(rb, .., ident, _) = pats[0].kind;
|
||||
if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
|
||||
if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind;
|
||||
if let ExprKind::Path(ref some_path) = e.kind;
|
||||
if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1;
|
||||
if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1;
|
||||
if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
|
||||
if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
|
||||
then {
|
||||
|
@ -1699,54 +1708,206 @@ where
|
|||
mod redundant_pattern_match {
|
||||
use super::REDUNDANT_PATTERN_MATCHING;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{is_trait_method, match_qpath, paths};
|
||||
use clippy_utils::source::{snippet, snippet_with_applicability};
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
|
||||
use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath};
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
|
||||
use rustc_hir::{
|
||||
intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
|
||||
Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, PatKind, QPath,
|
||||
};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
|
||||
use rustc_span::sym;
|
||||
|
||||
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
|
||||
match match_source {
|
||||
MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms),
|
||||
MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"),
|
||||
MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"),
|
||||
MatchSource::IfLetDesugar { contains_else_clause } => {
|
||||
find_sugg_for_if_let(cx, expr, op, &arms[0], "if", *contains_else_clause)
|
||||
},
|
||||
MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, &arms[0], "while", false),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
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(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop(cx, ty)),
|
||||
ty::Array(ty, _) => type_needs_ordered_drop(cx, ty),
|
||||
ty::Adt(adt, subs) => adt
|
||||
.all_fields()
|
||||
.map(|f| f.ty(cx.tcx, subs))
|
||||
.any(|ty| type_needs_ordered_drop(cx, ty)),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
// Check for std types which implement drop, but only for memory allocation.
|
||||
else if is_type_diagnostic_item(cx, ty, sym::vec_type)
|
||||
|| 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)
|
||||
|| match_type(cx, ty, &paths::BTREEMAP)
|
||||
|| match_type(cx, ty, &paths::LINKED_LIST)
|
||||
|| 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(cx, ty))
|
||||
} 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! {
|
||||
if let ty::Adt(_, subs) = ty.kind();
|
||||
if let Some(sub) = subs.get(index);
|
||||
if let GenericArgKind::Type(sub_ty) = sub.unpack();
|
||||
then {
|
||||
Some(sub_ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if there are any temporaries created in the given expression for which drop order
|
||||
// matters.
|
||||
fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
struct V<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
res: bool,
|
||||
}
|
||||
impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
|
||||
type Map = ErasedMap<'tcx>;
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||
match expr.kind {
|
||||
// Taking the reference of a value leaves a temporary
|
||||
// e.g. In `&String::new()` the string is a temporary value.
|
||||
// Remaining fields are temporary values
|
||||
// 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)) {
|
||||
self.res = true;
|
||||
} else {
|
||||
self.visit_expr(expr);
|
||||
}
|
||||
}
|
||||
},
|
||||
// the base type is alway taken by reference.
|
||||
// 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)) {
|
||||
self.res = true;
|
||||
} else {
|
||||
self.visit_expr(base);
|
||||
}
|
||||
}
|
||||
self.visit_expr(index);
|
||||
},
|
||||
// Method calls can take self by reference.
|
||||
// e.g. In `String::new().len()` the string is a temporary value.
|
||||
ExprKind::MethodCall(_, _, [self_arg, args @ ..], _) => {
|
||||
if !matches!(self_arg.kind, ExprKind::Path(_)) {
|
||||
let self_by_ref = self
|
||||
.cx
|
||||
.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))
|
||||
{
|
||||
self.res = true;
|
||||
} else {
|
||||
self.visit_expr(self_arg)
|
||||
}
|
||||
}
|
||||
args.iter().for_each(|arg| self.visit_expr(arg));
|
||||
},
|
||||
// Either explicitly drops values, or changes control flow.
|
||||
ExprKind::DropTemps(_)
|
||||
| ExprKind::Ret(_)
|
||||
| ExprKind::Break(..)
|
||||
| ExprKind::Yield(..)
|
||||
| ExprKind::Block(Block { expr: None, .. }, _)
|
||||
| ExprKind::Loop(..) => (),
|
||||
|
||||
// Only consider the final expression.
|
||||
ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr),
|
||||
|
||||
_ => walk_expr(self, expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut v = V { cx, res: false };
|
||||
v.visit_expr(expr);
|
||||
v.res
|
||||
}
|
||||
|
||||
fn find_sugg_for_if_let<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
op: &Expr<'_>,
|
||||
arms: &[Arm<'_>],
|
||||
op: &'tcx Expr<'tcx>,
|
||||
arm: &Arm<'_>,
|
||||
keyword: &'static str,
|
||||
has_else: bool,
|
||||
) {
|
||||
// also look inside refs
|
||||
let mut kind = &arms[0].pat.kind;
|
||||
let mut kind = &arm.pat.kind;
|
||||
// if we have &None for example, peel it so we can detect "if let None = x"
|
||||
if let PatKind::Ref(inner, _mutability) = kind {
|
||||
kind = &inner.kind;
|
||||
}
|
||||
let good_method = match kind {
|
||||
PatKind::TupleStruct(ref path, patterns, _) if patterns.len() == 1 => {
|
||||
if let PatKind::Wild = patterns[0].kind {
|
||||
if match_qpath(path, &paths::RESULT_OK) {
|
||||
"is_ok()"
|
||||
} else if match_qpath(path, &paths::RESULT_ERR) {
|
||||
"is_err()"
|
||||
} else if match_qpath(path, &paths::OPTION_SOME) {
|
||||
"is_some()"
|
||||
} else if match_qpath(path, &paths::POLL_READY) {
|
||||
"is_ready()"
|
||||
} else if match_qpath(path, &paths::IPADDR_V4) {
|
||||
"is_ipv4()"
|
||||
} else if match_qpath(path, &paths::IPADDR_V6) {
|
||||
"is_ipv6()"
|
||||
let op_ty = cx.typeck_results().expr_ty(op);
|
||||
// Determine which function should be used, and the type contained by the corresponding
|
||||
// variant.
|
||||
let (good_method, inner_ty) = match kind {
|
||||
PatKind::TupleStruct(ref path, [sub_pat], _) => {
|
||||
if let PatKind::Wild = sub_pat.kind {
|
||||
if is_lang_ctor(cx, path, ResultOk) {
|
||||
("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
|
||||
} else if is_lang_ctor(cx, path, ResultErr) {
|
||||
("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
|
||||
} else if is_lang_ctor(cx, path, OptionSome) {
|
||||
("is_some()", op_ty)
|
||||
} else if is_lang_ctor(cx, path, PollReady) {
|
||||
("is_ready()", op_ty)
|
||||
} else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) {
|
||||
("is_ipv4()", op_ty)
|
||||
} else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V6) {
|
||||
("is_ipv6()", op_ty)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
@ -1755,17 +1916,36 @@ mod redundant_pattern_match {
|
|||
}
|
||||
},
|
||||
PatKind::Path(ref path) => {
|
||||
if match_qpath(path, &paths::OPTION_NONE) {
|
||||
let method = if is_lang_ctor(cx, path, OptionNone) {
|
||||
"is_none()"
|
||||
} else if match_qpath(path, &paths::POLL_PENDING) {
|
||||
} else if is_lang_ctor(cx, path, PollPending) {
|
||||
"is_pending()"
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
// `None` and `Pending` don't have an inner type.
|
||||
(method, cx.tcx.types.unit)
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// If this is the last expression in a block or there is an else clause then the whole
|
||||
// type needs to be considered, not just the inner type of the branch being matched on.
|
||||
// Note the last expression in a block is dropped after all local bindings.
|
||||
let check_ty = if has_else
|
||||
|| (keyword == "if" && matches!(cx.tcx.hir().parent_iter(expr.hir_id).next(), Some((_, Node::Block(..)))))
|
||||
{
|
||||
op_ty
|
||||
} else {
|
||||
inner_ty
|
||||
};
|
||||
|
||||
// All temporaries created in the scrutinee expression are dropped at the same time as the
|
||||
// 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, op);
|
||||
|
||||
// check that `while_let_on_iterator` lint does not trigger
|
||||
if_chain! {
|
||||
if keyword == "while";
|
||||
|
@ -1784,7 +1964,7 @@ mod redundant_pattern_match {
|
|||
span_lint_and_then(
|
||||
cx,
|
||||
REDUNDANT_PATTERN_MATCHING,
|
||||
arms[0].pat.span,
|
||||
arm.pat.span,
|
||||
&format!("redundant pattern matching, consider using `{}`", good_method),
|
||||
|diag| {
|
||||
// while let ... = ... { ... }
|
||||
|
@ -1798,12 +1978,20 @@ mod redundant_pattern_match {
|
|||
// while let ... = ... { ... }
|
||||
// ^^^^^^^^^^^^^^^^^^^
|
||||
let span = expr_span.until(op_span.shrink_to_hi());
|
||||
diag.span_suggestion(
|
||||
span,
|
||||
"try this",
|
||||
format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method),
|
||||
Applicability::MachineApplicable, // snippet
|
||||
);
|
||||
|
||||
let mut app = if needs_drop {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
};
|
||||
let sugg = snippet_with_applicability(cx, op_span, "_", &mut app);
|
||||
|
||||
diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app);
|
||||
|
||||
if needs_drop {
|
||||
diag.note("this will change drop order of the result, as well as all temporaries");
|
||||
diag.note("add `#[allow(clippy::redundant_pattern_matching)]` if this is important");
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -1819,6 +2007,7 @@ mod redundant_pattern_match {
|
|||
) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
|
||||
if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
|
||||
find_good_method_for_match(
|
||||
cx,
|
||||
arms,
|
||||
path_left,
|
||||
path_right,
|
||||
|
@ -1829,6 +2018,7 @@ mod redundant_pattern_match {
|
|||
)
|
||||
.or_else(|| {
|
||||
find_good_method_for_match(
|
||||
cx,
|
||||
arms,
|
||||
path_left,
|
||||
path_right,
|
||||
|
@ -1848,6 +2038,7 @@ mod redundant_pattern_match {
|
|||
{
|
||||
if let PatKind::Wild = patterns[0].kind {
|
||||
find_good_method_for_match(
|
||||
cx,
|
||||
arms,
|
||||
path_left,
|
||||
path_right,
|
||||
|
@ -1858,6 +2049,7 @@ mod redundant_pattern_match {
|
|||
)
|
||||
.or_else(|| {
|
||||
find_good_method_for_match(
|
||||
cx,
|
||||
arms,
|
||||
path_left,
|
||||
path_right,
|
||||
|
@ -1898,7 +2090,9 @@ mod redundant_pattern_match {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn find_good_method_for_match<'a>(
|
||||
cx: &LateContext<'_>,
|
||||
arms: &[Arm<'_>],
|
||||
path_left: &QPath<'_>,
|
||||
path_right: &QPath<'_>,
|
||||
|
@ -1907,9 +2101,13 @@ mod redundant_pattern_match {
|
|||
should_be_left: &'a str,
|
||||
should_be_right: &'a str,
|
||||
) -> Option<&'a str> {
|
||||
let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
|
||||
let body_node_pair = if is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_left)
|
||||
&& is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_right)
|
||||
{
|
||||
(&(*arms[0].body).kind, &(*arms[1].body).kind)
|
||||
} else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) {
|
||||
} else if is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_left)
|
||||
&& is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_right)
|
||||
{
|
||||
(&(*arms[1].body).kind, &(*arms[0].body).kind)
|
||||
} else {
|
||||
return None;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::is_diagnostic_assoc_item;
|
||||
use clippy_utils::source::{snippet, snippet_with_applicability};
|
||||
use clippy_utils::{in_macro, match_def_path, match_qpath, meets_msrv, paths};
|
||||
use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
@ -102,7 +102,7 @@ impl_lint_pass!(MemReplace =>
|
|||
fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
|
||||
if let ExprKind::Path(ref replacement_qpath) = src.kind {
|
||||
// Check that second argument is `Option::None`
|
||||
if match_qpath(replacement_qpath, &paths::OPTION_NONE) {
|
||||
if is_lang_ctor(cx, replacement_qpath, OptionNone) {
|
||||
// Since this is a late pass (already type-checked),
|
||||
// and we already know that the second argument is an
|
||||
// `Option`, we do not need to check the first
|
||||
|
@ -210,17 +210,17 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<
|
|||
sym::BinaryHeap,
|
||||
];
|
||||
|
||||
if std_types_symbols
|
||||
.iter()
|
||||
.any(|symbol| is_diagnostic_assoc_item(cx, def_id, *symbol))
|
||||
{
|
||||
if let QPath::TypeRelative(_, method) = path {
|
||||
if method.ident.name == sym::new {
|
||||
return true;
|
||||
if let QPath::TypeRelative(_, method) = path {
|
||||
if method.ident.name == sym::new {
|
||||
if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
|
||||
if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
|
||||
return std_types_symbols
|
||||
.iter()
|
||||
.any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
|
@ -230,7 +230,7 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
|
|||
if !in_external_macro(cx.tcx.sess, expr_span);
|
||||
if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
|
||||
if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
|
||||
if is_diagnostic_assoc_item(cx, repl_def_id, sym::Default)
|
||||
if is_diag_trait_item(cx, repl_def_id, sym::Default)
|
||||
|| is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
|
||||
|
||||
then {
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::ty::{get_iterator_item_ty, is_copy};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::CLONED_INSTEAD_OF_COPIED;
|
||||
|
||||
pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) {
|
||||
let recv_ty = cx.typeck_results().expr_ty_adjusted(recv);
|
||||
let inner_ty = match recv_ty.kind() {
|
||||
// `Option<T>` -> `T`
|
||||
ty::Adt(adt, subst) if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) => subst.type_at(0),
|
||||
_ if is_trait_method(cx, expr, sym::Iterator) => match get_iterator_item_ty(cx, recv_ty) {
|
||||
// <T as Iterator>::Item
|
||||
Some(ty) => ty,
|
||||
_ => return,
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
match inner_ty.kind() {
|
||||
// &T where T: Copy
|
||||
ty::Ref(_, ty, _) if is_copy(cx, ty) => {},
|
||||
_ => return,
|
||||
};
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CLONED_INSTEAD_OF_COPIED,
|
||||
span,
|
||||
"used `cloned` where `copied` could be used instead",
|
||||
"try",
|
||||
"copied".into(),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::is_trait_method;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::FILTER_MAP;
|
||||
|
||||
/// lint use of `filter().flat_map()` for `Iterators`
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
// lint if caller of `.filter().flat_map()` is an Iterator
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
let msg = "called `filter(..).flat_map(..)` on an `Iterator`";
|
||||
let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
|
||||
and filtering by returning `iter::empty()`";
|
||||
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::is_trait_method;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::FILTER_MAP;
|
||||
|
||||
/// lint use of `filter_map().flat_map()` for `Iterators`
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
// lint if caller of `.filter_map().flat_map()` is an Iterator
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`";
|
||||
let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
|
||||
and filtering by returning `iter::empty()`";
|
||||
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths};
|
||||
use clippy_utils::{is_expr_path_def_path, is_trait_method, path_to_local_id, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
|
@ -33,14 +33,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg:
|
|||
}
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Path(ref qpath) = filter_map_arg.kind;
|
||||
|
||||
if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY);
|
||||
|
||||
then {
|
||||
apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`");
|
||||
}
|
||||
if is_expr_path_def_path(cx, filter_map_arg, &paths::CONVERT_IDENTITY) {
|
||||
apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::is_trait_method;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::FILTER_MAP;
|
||||
|
||||
/// lint use of `filter_map().map()` for `Iterators`
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
// lint if caller of `.filter_map().map()` is an Iterator
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
let msg = "called `filter_map(..).map(..)` on an `Iterator`";
|
||||
let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead";
|
||||
span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::{is_trait_method, match_qpath, paths};
|
||||
use clippy_utils::{is_expr_path_def_path, is_trait_method, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
|
@ -16,8 +16,6 @@ pub(super) fn check<'tcx>(
|
|||
flat_map_span: Span,
|
||||
) {
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
let arg_node = &flat_map_arg.kind;
|
||||
|
||||
let apply_lint = |message: &str| {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
@ -31,8 +29,8 @@ pub(super) fn check<'tcx>(
|
|||
};
|
||||
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node;
|
||||
let body = cx.tcx.hir().body(*body_id);
|
||||
if let hir::ExprKind::Closure(_, _, body_id, _, _) = flat_map_arg.kind;
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
|
||||
if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind;
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind;
|
||||
|
@ -45,14 +43,8 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Path(ref qpath) = arg_node;
|
||||
|
||||
if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY);
|
||||
|
||||
then {
|
||||
apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
|
||||
}
|
||||
if is_expr_path_def_path(cx, flat_map_arg, &paths::CONVERT_IDENTITY) {
|
||||
apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_trait_method;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::{source_map::Span, sym};
|
||||
|
||||
use super::FLAT_MAP_OPTION;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) {
|
||||
if !is_trait_method(cx, expr, sym::Iterator) {
|
||||
return;
|
||||
}
|
||||
let arg_ty = cx.typeck_results().expr_ty_adjusted(arg);
|
||||
let sig = match arg_ty.kind() {
|
||||
ty::Closure(_, substs) => substs.as_closure().sig(),
|
||||
_ if arg_ty.is_fn() => arg_ty.fn_sig(cx.tcx),
|
||||
_ => return,
|
||||
};
|
||||
if !is_type_diagnostic_item(cx, sig.output().skip_binder(), sym::option_type) {
|
||||
return;
|
||||
}
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
FLAT_MAP_OPTION,
|
||||
span,
|
||||
"used `flat_map` where `filter_map` could be used instead",
|
||||
"try",
|
||||
"filter_map".into(),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
}
|
|
@ -1,26 +1,23 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{get_trait_def_id, match_qpath, paths, sugg};
|
||||
use clippy_utils::{is_expr_path_def_path, paths, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::ExprKind;
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::FROM_ITER_INSTEAD_OF_COLLECT;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func_kind: &ExprKind<'_>) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func: &hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Path(path) = func_kind;
|
||||
if match_qpath(path, &["from_iter"]);
|
||||
if is_expr_path_def_path(cx, func, &paths::FROM_ITERATOR_METHOD);
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
let arg_ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR);
|
||||
if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
|
||||
|
||||
if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]);
|
||||
if implements_trait(cx, arg_ty, iter_id, &[]);
|
||||
then {
|
||||
// `expr` implements `FromIterator` trait
|
||||
let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par();
|
||||
|
|
|
@ -1,28 +1,36 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::{is_diag_item_method, is_diag_trait_item};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::ExprKind;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::TyS;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::IMPLICIT_CLONE;
|
||||
use clippy_utils::is_diagnostic_assoc_item;
|
||||
|
||||
pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, trait_diagnostic: Symbol) {
|
||||
pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, span: Span) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_path, _, [arg], _) = &expr.kind;
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if match method_name {
|
||||
"to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr),
|
||||
"to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned),
|
||||
"to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path),
|
||||
"to_vec" => cx.tcx.impl_of_method(method_def_id)
|
||||
.map(|impl_did| Some(impl_did) == cx.tcx.lang_items().slice_alloc_impl())
|
||||
== Some(true),
|
||||
_ => false,
|
||||
};
|
||||
let return_type = cx.typeck_results().expr_ty(expr);
|
||||
let input_type = cx.typeck_results().expr_ty(arg).peel_refs();
|
||||
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
let input_type = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did));
|
||||
if TyS::same_type(return_type, input_type);
|
||||
if is_diagnostic_assoc_item(cx, expr_def_id, trait_diagnostic);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,IMPLICIT_CLONE,method_path.ident.span,
|
||||
&format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_path.ident.name),
|
||||
cx,
|
||||
IMPLICIT_CLONE,
|
||||
span,
|
||||
&format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_name),
|
||||
"consider using",
|
||||
"clone".to_string(),
|
||||
Applicability::MachineApplicable
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::match_qpath;
|
||||
use clippy_utils::is_qpath_def_path;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
|
@ -94,11 +94,11 @@ fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<M
|
|||
|
||||
// `std::T::MAX` `std::T::MIN` constants
|
||||
if let hir::ExprKind::Path(path) = &expr.kind {
|
||||
if match_qpath(path, &["core", &ty_str, "MAX"][..]) {
|
||||
if is_qpath_def_path(cx, path, expr.hir_id, &["core", &ty_str, "MAX"][..]) {
|
||||
return Some(MinMax::Max);
|
||||
}
|
||||
|
||||
if match_qpath(path, &["core", &ty_str, "MIN"][..]) {
|
||||
if is_qpath_def_path(cx, path, expr.hir_id, &["core", &ty_str, "MIN"][..]) {
|
||||
return Some(MinMax::Min);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,17 +8,16 @@ mod chars_next_cmp;
|
|||
mod chars_next_cmp_with_unwrap;
|
||||
mod clone_on_copy;
|
||||
mod clone_on_ref_ptr;
|
||||
mod cloned_instead_of_copied;
|
||||
mod expect_fun_call;
|
||||
mod expect_used;
|
||||
mod filetype_is_file;
|
||||
mod filter_flat_map;
|
||||
mod filter_map;
|
||||
mod filter_map_flat_map;
|
||||
mod filter_map_identity;
|
||||
mod filter_map_map;
|
||||
mod filter_map_next;
|
||||
mod filter_next;
|
||||
mod flat_map_identity;
|
||||
mod flat_map_option;
|
||||
mod from_iter_instead_of_collect;
|
||||
mod get_unwrap;
|
||||
mod implicit_clone;
|
||||
|
@ -76,6 +75,52 @@ use rustc_span::symbol::SymbolStr;
|
|||
use rustc_span::{sym, Span};
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usages of `cloned()` on an `Iterator` or `Option` where
|
||||
/// `copied()` could be used instead.
|
||||
///
|
||||
/// **Why is this bad?** `copied()` is better because it guarantees that the type being cloned
|
||||
/// implements `Copy`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// [1, 2, 3].iter().cloned();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// [1, 2, 3].iter().copied();
|
||||
/// ```
|
||||
pub CLONED_INSTEAD_OF_COPIED,
|
||||
pedantic,
|
||||
"used `cloned` where `copied` could be used instead"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
|
||||
/// used instead.
|
||||
///
|
||||
/// **Why is this bad?** When applicable, `filter_map()` is more clear since it shows that
|
||||
/// `Option` is used to produce 0 or 1 items.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
|
||||
/// ```
|
||||
pub FLAT_MAP_OPTION,
|
||||
pedantic,
|
||||
"used `flat_map` where `filter_map` could be used instead"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.
|
||||
///
|
||||
|
@ -472,35 +517,6 @@ declare_clippy_lint! {
|
|||
"using combinations of `flatten` and `map` which can usually be written as a single method call"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `_.filter(_).map(_)`,
|
||||
/// `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar.
|
||||
///
|
||||
/// **Why is this bad?** Readability, this can be written more concisely as
|
||||
/// `_.filter_map(_)`.
|
||||
///
|
||||
/// **Known problems:** Often requires a condition + Option/Iterator creation
|
||||
/// inside the closure.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let vec = vec![1];
|
||||
///
|
||||
/// // Bad
|
||||
/// vec.iter().filter(|x| **x == 0).map(|x| *x * 2);
|
||||
///
|
||||
/// // Good
|
||||
/// vec.iter().filter_map(|x| if *x == 0 {
|
||||
/// Some(*x * 2)
|
||||
/// } else {
|
||||
/// None
|
||||
/// });
|
||||
/// ```
|
||||
pub FILTER_MAP,
|
||||
pedantic,
|
||||
"using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply
|
||||
/// as `filter_map(_)`.
|
||||
|
@ -1670,6 +1686,8 @@ impl_lint_pass!(Methods => [
|
|||
CLONE_ON_COPY,
|
||||
CLONE_ON_REF_PTR,
|
||||
CLONE_DOUBLE_REF,
|
||||
CLONED_INSTEAD_OF_COPIED,
|
||||
FLAT_MAP_OPTION,
|
||||
INEFFICIENT_TO_STRING,
|
||||
NEW_RET_NO_SELF,
|
||||
SINGLE_CHAR_PATTERN,
|
||||
|
@ -1677,7 +1695,6 @@ impl_lint_pass!(Methods => [
|
|||
SEARCH_IS_SOME,
|
||||
FILTER_NEXT,
|
||||
SKIP_WHILE_NEXT,
|
||||
FILTER_MAP,
|
||||
FILTER_MAP_IDENTITY,
|
||||
MANUAL_FILTER_MAP,
|
||||
MANUAL_FIND_MAP,
|
||||
|
@ -1741,7 +1758,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
|
||||
match expr.kind {
|
||||
hir::ExprKind::Call(func, args) => {
|
||||
from_iter_instead_of_collect::check(cx, expr, args, &func.kind);
|
||||
from_iter_instead_of_collect::check(cx, expr, args, func);
|
||||
},
|
||||
hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => {
|
||||
or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
|
||||
|
@ -1942,6 +1959,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
|
||||
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
|
||||
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
|
||||
("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span),
|
||||
("collect", []) => match method_call!(recv) {
|
||||
Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2),
|
||||
Some(("map", [m_recv, m_arg], _)) => {
|
||||
|
@ -1965,10 +1983,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
unnecessary_filter_map::check(cx, expr, arg);
|
||||
filter_map_identity::check(cx, expr, arg, span);
|
||||
},
|
||||
("flat_map", [flm_arg]) => match method_call!(recv) {
|
||||
Some(("filter", [_, _], _)) => filter_flat_map::check(cx, expr),
|
||||
Some(("filter_map", [_, _], _)) => filter_map_flat_map::check(cx, expr),
|
||||
_ => flat_map_identity::check(cx, expr, flm_arg, span),
|
||||
("flat_map", [arg]) => {
|
||||
flat_map_identity::check(cx, expr, arg, span);
|
||||
flat_map_option::check(cx, expr, arg, span);
|
||||
},
|
||||
("flatten", []) => {
|
||||
if let Some(("map", [recv, map_arg], _)) = method_call!(recv) {
|
||||
|
@ -1993,7 +2010,6 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
("filter", [f_arg]) => {
|
||||
filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false)
|
||||
},
|
||||
("filter_map", [_]) => filter_map_map::check(cx, expr),
|
||||
("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true),
|
||||
_ => {},
|
||||
}
|
||||
|
@ -2025,10 +2041,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
}
|
||||
},
|
||||
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
|
||||
("to_os_string", []) => implicit_clone::check(cx, expr, sym::OsStr),
|
||||
("to_owned", []) => implicit_clone::check(cx, expr, sym::ToOwned),
|
||||
("to_path_buf", []) => implicit_clone::check(cx, expr, sym::Path),
|
||||
("to_vec", []) => implicit_clone::check(cx, expr, sym::slice),
|
||||
("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
|
||||
implicit_clone::check(cx, name, expr, recv, span);
|
||||
},
|
||||
("unwrap", []) => match method_call!(recv) {
|
||||
Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false),
|
||||
Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true),
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_lang_ctor;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{match_qpath, paths};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
|
@ -32,7 +33,7 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
let (lint_name, msg, instead, hint) = {
|
||||
let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
|
||||
match_qpath(qpath, &paths::OPTION_NONE)
|
||||
is_lang_ctor(cx, qpath, OptionNone)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
@ -43,7 +44,7 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
|
||||
let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
|
||||
match_qpath(qpath, &paths::OPTION_SOME)
|
||||
is_lang_ctor(cx, qpath, OptionSome)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{match_def_path, match_qpath, paths};
|
||||
use clippy_utils::{is_expr_path_def_path, match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
|
@ -12,8 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
|
|||
if_chain! {
|
||||
if let hir::ExprKind::Call(callee, args) = recv.kind;
|
||||
if args.is_empty();
|
||||
if let hir::ExprKind::Path(ref path) = callee.kind;
|
||||
if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT);
|
||||
if is_expr_path_def_path(cx, callee, &paths::MEM_MAYBEUNINIT_UNINIT);
|
||||
if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(expr));
|
||||
then {
|
||||
span_lint(
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::usage::mutated_variables;
|
||||
use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths};
|
||||
use clippy_utils::{is_lang_ctor, is_trait_method, path_to_local_id};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_span::sym;
|
||||
|
@ -54,14 +55,12 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
|
|||
match &expr.kind {
|
||||
hir::ExprKind::Call(func, args) => {
|
||||
if let hir::ExprKind::Path(ref path) = func.kind {
|
||||
if match_qpath(path, &paths::OPTION_SOME) {
|
||||
if is_lang_ctor(cx, path, OptionSome) {
|
||||
if path_to_local_id(&args[0], arg_id) {
|
||||
return (false, false);
|
||||
}
|
||||
return (true, false);
|
||||
}
|
||||
// We don't know. It might do anything.
|
||||
return (true, true);
|
||||
}
|
||||
(true, true)
|
||||
},
|
||||
|
@ -85,7 +84,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
|
|||
let else_check = check_expression(cx, arg_id, else_arm);
|
||||
(if_check.0 | else_check.0, if_check.1 | else_check.1)
|
||||
},
|
||||
hir::ExprKind::Path(path) if match_qpath(path, &paths::OPTION_NONE) => (false, true),
|
||||
hir::ExprKind::Path(path) if is_lang_ctor(cx, path, OptionNone) => (false, true),
|
||||
_ => (true, true),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,6 +102,14 @@ pub(super) fn check<'tcx>(
|
|||
.iter()
|
||||
.all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item))
|
||||
}) {
|
||||
// don't lint if it implements a trait but not willing to check `Copy` types conventions (see #7032)
|
||||
if implements_trait
|
||||
&& !conventions
|
||||
.iter()
|
||||
.any(|conv| matches!(conv, Convention::IsSelfTypeCopy(_)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
|
||||
let suggestion = {
|
||||
if conventions.len() > 1 {
|
||||
|
|
|
@ -20,8 +20,8 @@ use rustc_span::symbol::sym;
|
|||
use crate::consts::{constant, Constant};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{
|
||||
get_item_name, get_parent_expr, higher, in_constant, is_diagnostic_assoc_item, is_integer_const, iter_input_pats,
|
||||
last_path_segment, match_qpath, unsext, SpanlessEq,
|
||||
expr_path_res, get_item_name, get_parent_expr, higher, in_constant, is_diag_trait_item, is_integer_const,
|
||||
iter_input_pats, last_path_segment, match_any_def_paths, paths, unsext, SpanlessEq,
|
||||
};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -555,8 +555,8 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left:
|
|||
ExprKind::MethodCall(.., args, _) if args.len() == 1 => {
|
||||
if_chain!(
|
||||
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString)
|
||||
|| is_diagnostic_assoc_item(cx, expr_def_id, sym::ToOwned);
|
||||
if is_diag_trait_item(cx, expr_def_id, sym::ToString)
|
||||
|| is_diag_trait_item(cx, expr_def_id, sym::ToOwned);
|
||||
then {
|
||||
(cx.typeck_results().expr_ty(&args[0]), snippet(cx, args[0].span, ".."))
|
||||
} else {
|
||||
|
@ -564,13 +564,13 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left:
|
|||
}
|
||||
)
|
||||
},
|
||||
ExprKind::Call(path, v) if v.len() == 1 => {
|
||||
if let ExprKind::Path(ref path) = path.kind {
|
||||
if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) {
|
||||
(cx.typeck_results().expr_ty(&v[0]), snippet(cx, v[0].span, ".."))
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
ExprKind::Call(path, [arg]) => {
|
||||
if expr_path_res(cx, path)
|
||||
.opt_def_id()
|
||||
.and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
|
||||
.is_some()
|
||||
{
|
||||
(cx.typeck_results().expr_ty(arg), snippet(cx, arg.span, ".."))
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
|||
|
||||
let mir = cx.tcx.optimized_mir(def_id);
|
||||
|
||||
if let Err((span, err)) = is_min_const_fn(cx.tcx, mir) {
|
||||
if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) {
|
||||
if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) {
|
||||
cx.tcx.sess.span_err(span, &err);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use clippy_utils::sext;
|
|||
use if_chain::if_chain;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use std::fmt::Display;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{is_expn_of, parent_node_is_if_expr};
|
||||
use clippy_utils::{is_else_clause, is_expn_of};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
|
||||
|
@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
|
|||
snip = snip.make_return();
|
||||
}
|
||||
|
||||
if parent_node_is_if_expr(e, cx) {
|
||||
if is_else_clause(cx.tcx, e) {
|
||||
snip = snip.blockify()
|
||||
}
|
||||
|
||||
|
|
|
@ -279,7 +279,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
|
|||
spans.extend(
|
||||
deref_span
|
||||
.iter()
|
||||
.cloned()
|
||||
.copied()
|
||||
.map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
|
||||
);
|
||||
spans.sort_by_key(|&(span, _)| span);
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_lang_ctor;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{differing_macro_contexts, is_ok_ctor, is_some_ctor, meets_msrv};
|
||||
use clippy_utils::{differing_macro_contexts, meets_msrv};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionSome, ResultOk};
|
||||
use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_semver::RustcVersion;
|
||||
|
@ -159,8 +161,8 @@ fn is_some_or_ok_call<'a>(
|
|||
if_chain! {
|
||||
// Check outer expression matches CALL_IDENT(ARGUMENT) format
|
||||
if let ExprKind::Call(path, args) = &expr.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind;
|
||||
if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res);
|
||||
if let ExprKind::Path(ref qpath) = &path.kind;
|
||||
if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
|
||||
|
||||
// Extract inner expression from ARGUMENT
|
||||
if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::paths;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::usage::contains_return_break_continue_macro;
|
||||
use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, match_qpath};
|
||||
use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, is_lang_ctor};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::OptionSome;
|
||||
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
@ -164,7 +164,7 @@ fn detect_option_if_let_else<'tcx>(
|
|||
if arms.len() == 2;
|
||||
if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already
|
||||
if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind;
|
||||
if match_qpath(struct_qpath, &paths::OPTION_SOME);
|
||||
if is_lang_ctor(cx, struct_qpath, OptionSome);
|
||||
if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind;
|
||||
if !contains_return_break_continue_macro(arms[0].body);
|
||||
if !contains_return_break_continue_macro(arms[1].body);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{find_macro_calls, return_ty};
|
||||
use clippy_utils::{find_macro_calls, is_expn_of, return_ty};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
|
|||
}
|
||||
|
||||
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
|
||||
let panics = find_macro_calls(
|
||||
let mut panics = find_macro_calls(
|
||||
&[
|
||||
"unimplemented",
|
||||
"unreachable",
|
||||
|
@ -61,12 +61,10 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir
|
|||
"assert",
|
||||
"assert_eq",
|
||||
"assert_ne",
|
||||
"debug_assert",
|
||||
"debug_assert_eq",
|
||||
"debug_assert_ne",
|
||||
],
|
||||
body,
|
||||
);
|
||||
panics.retain(|span| is_expn_of(*span, "debug_assert").is_none());
|
||||
if !panics.is_empty() {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
|
|
|
@ -74,7 +74,7 @@ declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANI
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if match_panic_call(cx, expr).is_some() {
|
||||
if match_panic_call(cx, expr).is_some() && is_expn_of(expr.span, "debug_assert").is_none() {
|
||||
let span = get_outer_span(expr);
|
||||
if is_expn_of(expr.span, "unimplemented").is_some() {
|
||||
span_lint(
|
||||
|
|
|
@ -42,6 +42,14 @@ declare_clippy_lint! {
|
|||
/// false positives in cases involving multiple lifetimes that are bounded by
|
||||
/// each other.
|
||||
///
|
||||
/// Also, it does not take account of other similar cases where getting memory addresses
|
||||
/// matters; namely, returning the pointer to the argument in question,
|
||||
/// and passing the argument, as both references and pointers,
|
||||
/// to a function that needs the memory address. For further details, refer to
|
||||
/// [this issue](https://github.com/rust-lang/rust-clippy/issues/5953)
|
||||
/// that explains a real case in which this false positive
|
||||
/// led to an **undefined behaviour** introduced with unsafe code.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
|
|
|
@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the
|
|||
use clippy_utils::ptr::get_spans;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty};
|
||||
use clippy_utils::{is_allowed, match_qpath, paths};
|
||||
use clippy_utils::{expr_path_res, is_allowed, match_any_def_paths, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{
|
||||
|
@ -15,6 +15,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
|||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::{sym, MultiSpan};
|
||||
use std::borrow::Cow;
|
||||
|
||||
|
@ -94,7 +95,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
pub CMP_NULL,
|
||||
style,
|
||||
"comparing a pointer to a null pointer, suggesting to use `.is_null()` instead."
|
||||
"comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -119,7 +120,28 @@ declare_clippy_lint! {
|
|||
"fns that create mutable refs from immutable ref args"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF]);
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** This lint checks for invalid usages of `ptr::null`.
|
||||
///
|
||||
/// **Why is this bad?** This causes undefined behavior.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// // Bad. Undefined behavior
|
||||
/// unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
|
||||
/// ```
|
||||
pub INVALID_NULL_PTR_USAGE,
|
||||
correctness,
|
||||
"invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Ptr {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
|
@ -153,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
|
|||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Binary(ref op, l, r) = expr.kind {
|
||||
if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(l) || is_null_path(r)) {
|
||||
if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) {
|
||||
span_lint(
|
||||
cx,
|
||||
CMP_NULL,
|
||||
|
@ -161,6 +183,55 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
|
|||
"comparing with null is better expressed by the `.is_null()` method",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
check_invalid_ptr_usage(cx, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
// (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B.
|
||||
const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 16] = [
|
||||
(&paths::SLICE_FROM_RAW_PARTS, &[0]),
|
||||
(&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]),
|
||||
(&paths::PTR_COPY, &[0, 1]),
|
||||
(&paths::PTR_COPY_NONOVERLAPPING, &[0, 1]),
|
||||
(&paths::PTR_READ, &[0]),
|
||||
(&paths::PTR_READ_UNALIGNED, &[0]),
|
||||
(&paths::PTR_READ_VOLATILE, &[0]),
|
||||
(&paths::PTR_REPLACE, &[0]),
|
||||
(&paths::PTR_SLICE_FROM_RAW_PARTS, &[0]),
|
||||
(&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]),
|
||||
(&paths::PTR_SWAP, &[0, 1]),
|
||||
(&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]),
|
||||
(&paths::PTR_WRITE, &[0]),
|
||||
(&paths::PTR_WRITE_UNALIGNED, &[0]),
|
||||
(&paths::PTR_WRITE_VOLATILE, &[0]),
|
||||
(&paths::PTR_WRITE_BYTES, &[0]),
|
||||
];
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Call(ref fun, ref args) = 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();
|
||||
let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::<Vec<_>>();
|
||||
if let Some(&(_, arg_indices)) = INVALID_NULL_PTR_USAGE_TABLE
|
||||
.iter()
|
||||
.find(|&&(fn_path, _)| fn_path == fun_def_path);
|
||||
then {
|
||||
for &arg_idx in arg_indices {
|
||||
if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
INVALID_NULL_PTR_USAGE,
|
||||
arg.span,
|
||||
"pointer must be non-null",
|
||||
"change this to",
|
||||
"core::ptr::NonNull::dangling().as_ptr()".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -345,13 +416,12 @@ fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability,
|
|||
}
|
||||
}
|
||||
|
||||
fn is_null_path(expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Call(pathexp, args) = expr.kind {
|
||||
if args.is_empty() {
|
||||
if let ExprKind::Path(ref path) = pathexp.kind {
|
||||
return match_qpath(path, &paths::PTR_NULL) || match_qpath(path, &paths::PTR_NULL_MUT);
|
||||
}
|
||||
}
|
||||
fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Call(pathexp, []) = expr.kind {
|
||||
expr_path_res(cx, pathexp).opt_def_id().map_or(false, |id| {
|
||||
match_any_def_paths(cx, id, &[&paths::PTR_NULL, &paths::PTR_NULL_MUT]).is_some()
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_lang_ctor;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{eq_expr_value, match_def_path, match_qpath, paths};
|
||||
use clippy_utils::{eq_expr_value, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{def, BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind};
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
@ -100,15 +101,14 @@ impl QuestionMark {
|
|||
if Self::is_option(cx, subject);
|
||||
|
||||
if let PatKind::TupleStruct(path1, fields, None) = &arms[0].pat.kind;
|
||||
if match_qpath(path1, &["Some"]);
|
||||
if let PatKind::Binding(annot, _, bind, _) = &fields[0].kind;
|
||||
if is_lang_ctor(cx, path1, OptionSome);
|
||||
if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind;
|
||||
let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
|
||||
|
||||
if let ExprKind::Block(block, None) = &arms[0].body.kind;
|
||||
if block.stmts.is_empty();
|
||||
if let Some(trailing_expr) = &block.expr;
|
||||
if let ExprKind::Path(path) = &trailing_expr.kind;
|
||||
if match_qpath(path, &[&bind.as_str()]);
|
||||
if path_to_local_id(trailing_expr, bind_id);
|
||||
|
||||
if let PatKind::Wild = arms[1].pat.kind;
|
||||
if Self::expression_returns_none(cx, arms[1].body);
|
||||
|
@ -156,15 +156,7 @@ impl QuestionMark {
|
|||
false
|
||||
},
|
||||
ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr),
|
||||
ExprKind::Path(ref qp) => {
|
||||
if let Res::Def(DefKind::Ctor(def::CtorOf::Variant, def::CtorKind::Const), def_id) =
|
||||
cx.qpath_res(qp, expression.hir_id)
|
||||
{
|
||||
return match_def_path(cx, def_id, &paths::OPTION_NONE);
|
||||
}
|
||||
|
||||
false
|
||||
},
|
||||
ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{fn_def_id, in_macro, match_qpath};
|
||||
use clippy_utils::{fn_def_id, in_macro, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::Attribute;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -84,9 +84,8 @@ impl<'tcx> LateLintPass<'tcx> for Return {
|
|||
if local.ty.is_none();
|
||||
if cx.tcx.hir().attrs(local.hir_id).is_empty();
|
||||
if let Some(initexpr) = &local.init;
|
||||
if let PatKind::Binding(.., ident, _) = local.pat.kind;
|
||||
if let ExprKind::Path(qpath) = &retexpr.kind;
|
||||
if match_qpath(qpath, &[&*ident.name.as_str()]);
|
||||
if let PatKind::Binding(_, local_id, _, _) = local.pat.kind;
|
||||
if path_to_local_id(retexpr, local_id);
|
||||
if !last_statement_borrows(cx, initexpr);
|
||||
if !in_external_macro(cx.sess(), initexpr.span);
|
||||
if !in_external_macro(cx.sess(), retexpr.span);
|
||||
|
@ -223,6 +222,7 @@ fn check_final_expr<'tcx>(
|
|||
},
|
||||
_ => (),
|
||||
},
|
||||
ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
|
||||
use clippy_utils::in_macro;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::{Item, ItemKind, UseTreeKind};
|
||||
use rustc_ast::{ptr::P, Crate, Item, ItemKind, ModKind, UseTreeKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::{edition::Edition, symbol::kw, Span, Symbol};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checking for imports with single component use path.
|
||||
|
@ -38,26 +37,120 @@ declare_clippy_lint! {
|
|||
declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]);
|
||||
|
||||
impl EarlyLintPass for SingleComponentPathImports {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if_chain! {
|
||||
if !in_macro(item.span);
|
||||
if cx.sess.opts.edition >= Edition::Edition2018;
|
||||
if !item.vis.kind.is_pub();
|
||||
if let ItemKind::Use(use_tree) = &item.kind;
|
||||
if let segments = &use_tree.prefix.segments;
|
||||
if segments.len() == 1;
|
||||
if let UseTreeKind::Simple(None, _, _) = use_tree.kind;
|
||||
then {
|
||||
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
|
||||
if cx.sess.opts.edition < Edition::Edition2018 {
|
||||
return;
|
||||
}
|
||||
check_mod(cx, &krate.items);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) {
|
||||
// keep track of imports reused with `self` keyword,
|
||||
// such as `self::crypto_hash` in the example below
|
||||
// ```rust,ignore
|
||||
// use self::crypto_hash::{Algorithm, Hasher};
|
||||
// ```
|
||||
let mut imports_reused_with_self = Vec::new();
|
||||
|
||||
// keep track of single use statements
|
||||
// such as `crypto_hash` in the example below
|
||||
// ```rust,ignore
|
||||
// use crypto_hash;
|
||||
// ```
|
||||
let mut single_use_usages = Vec::new();
|
||||
|
||||
for item in items {
|
||||
track_uses(cx, &item, &mut imports_reused_with_self, &mut single_use_usages);
|
||||
}
|
||||
|
||||
for single_use in &single_use_usages {
|
||||
if !imports_reused_with_self.contains(&single_use.0) {
|
||||
let can_suggest = single_use.2;
|
||||
if can_suggest {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SINGLE_COMPONENT_PATH_IMPORTS,
|
||||
item.span,
|
||||
single_use.1,
|
||||
"this import is redundant",
|
||||
"remove it entirely",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
SINGLE_COMPONENT_PATH_IMPORTS,
|
||||
single_use.1,
|
||||
"this import is redundant",
|
||||
None,
|
||||
"remove this import",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn track_uses(
|
||||
cx: &EarlyContext<'_>,
|
||||
item: &Item,
|
||||
imports_reused_with_self: &mut Vec<Symbol>,
|
||||
single_use_usages: &mut Vec<(Symbol, Span, bool)>,
|
||||
) {
|
||||
if in_macro(item.span) || item.vis.kind.is_pub() {
|
||||
return;
|
||||
}
|
||||
|
||||
match &item.kind {
|
||||
ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => {
|
||||
check_mod(cx, &items);
|
||||
},
|
||||
ItemKind::Use(use_tree) => {
|
||||
let segments = &use_tree.prefix.segments;
|
||||
|
||||
// keep track of `use some_module;` usages
|
||||
if segments.len() == 1 {
|
||||
if let UseTreeKind::Simple(None, _, _) = use_tree.kind {
|
||||
let ident = &segments[0].ident;
|
||||
single_use_usages.push((ident.name, item.span, true));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if segments.is_empty() {
|
||||
// keep track of `use {some_module, some_other_module};` usages
|
||||
if let UseTreeKind::Nested(trees) = &use_tree.kind {
|
||||
for tree in trees {
|
||||
let segments = &tree.0.prefix.segments;
|
||||
if segments.len() == 1 {
|
||||
if let UseTreeKind::Simple(None, _, _) = tree.0.kind {
|
||||
let ident = &segments[0].ident;
|
||||
single_use_usages.push((ident.name, tree.0.span, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// keep track of `use self::some_module` usages
|
||||
if segments[0].ident.name == kw::SelfLower {
|
||||
// simple case such as `use self::module::SomeStruct`
|
||||
if segments.len() > 1 {
|
||||
imports_reused_with_self.push(segments[1].ident.name);
|
||||
return;
|
||||
}
|
||||
|
||||
// nested case such as `use self::{module1::Struct1, module2::Struct2}`
|
||||
if let UseTreeKind::Nested(trees) = &use_tree.kind {
|
||||
for tree in trees {
|
||||
let segments = &tree.0.prefix.segments;
|
||||
if !segments.is_empty() {
|
||||
imports_reused_with_self.push(segments[0].ident.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,8 +65,8 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool)
|
|||
|
||||
fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> {
|
||||
const FUNCTIONS: [&[&str]; 8] = [
|
||||
&paths::COPY_NONOVERLAPPING,
|
||||
&paths::COPY,
|
||||
&paths::PTR_COPY_NONOVERLAPPING,
|
||||
&paths::PTR_COPY,
|
||||
&paths::WRITE_BYTES,
|
||||
&paths::PTR_SWAP_NONOVERLAPPING,
|
||||
&paths::PTR_SLICE_FROM_RAW_PARTS,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{get_enclosing_block, match_qpath, SpanlessEq};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{get_enclosing_block, is_expr_path_def_path, path_to_local, path_to_local_id, paths, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -9,7 +10,7 @@ use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath,
|
|||
use rustc_lint::{LateContext, LateLintPass, Lint};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks slow zero-filled vector initialization
|
||||
|
@ -46,8 +47,8 @@ declare_lint_pass!(SlowVectorInit => [SLOW_VECTOR_INITIALIZATION]);
|
|||
/// assigned to a variable. For example, `let mut vec = Vec::with_capacity(0)` or
|
||||
/// `vec = Vec::with_capacity(0)`
|
||||
struct VecAllocation<'tcx> {
|
||||
/// Symbol of the local variable name
|
||||
variable_name: Symbol,
|
||||
/// HirId of the variable
|
||||
local_id: HirId,
|
||||
|
||||
/// Reference to the expression which allocates the vector
|
||||
allocation_expr: &'tcx Expr<'tcx>,
|
||||
|
@ -72,16 +73,15 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
|
|||
if_chain! {
|
||||
if let ExprKind::Assign(left, right, _) = expr.kind;
|
||||
|
||||
// Extract variable name
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind;
|
||||
if let Some(variable_name) = path.segments.get(0);
|
||||
// Extract variable
|
||||
if let Some(local_id) = path_to_local(left);
|
||||
|
||||
// Extract len argument
|
||||
if let Some(len_arg) = Self::is_vec_with_capacity(right);
|
||||
if let Some(len_arg) = Self::is_vec_with_capacity(cx, right);
|
||||
|
||||
then {
|
||||
let vi = VecAllocation {
|
||||
variable_name: variable_name.ident.name,
|
||||
local_id,
|
||||
allocation_expr: right,
|
||||
len_expr: len_arg,
|
||||
};
|
||||
|
@ -95,13 +95,13 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
|
|||
// Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)`
|
||||
if_chain! {
|
||||
if let StmtKind::Local(local) = stmt.kind;
|
||||
if let PatKind::Binding(BindingAnnotation::Mutable, .., variable_name, None) = local.pat.kind;
|
||||
if let PatKind::Binding(BindingAnnotation::Mutable, local_id, _, None) = local.pat.kind;
|
||||
if let Some(init) = local.init;
|
||||
if let Some(len_arg) = Self::is_vec_with_capacity(init);
|
||||
if let Some(len_arg) = Self::is_vec_with_capacity(cx, init);
|
||||
|
||||
then {
|
||||
let vi = VecAllocation {
|
||||
variable_name: variable_name.name,
|
||||
local_id,
|
||||
allocation_expr: init,
|
||||
len_expr: len_arg,
|
||||
};
|
||||
|
@ -115,19 +115,18 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
|
|||
impl SlowVectorInit {
|
||||
/// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression
|
||||
/// of the first argument of `with_capacity` call if it matches or `None` if it does not.
|
||||
fn is_vec_with_capacity<'tcx>(expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
fn is_vec_with_capacity<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func, args) = expr.kind;
|
||||
if let ExprKind::Path(ref path) = func.kind;
|
||||
if match_qpath(path, &["Vec", "with_capacity"]);
|
||||
if args.len() == 1;
|
||||
|
||||
if let ExprKind::Call(func, [arg]) = expr.kind;
|
||||
if let ExprKind::Path(QPath::TypeRelative(ty, name)) = func.kind;
|
||||
if name.ident.as_str() == "with_capacity";
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::vec_type);
|
||||
then {
|
||||
return Some(&args[0]);
|
||||
Some(arg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Search initialization for the given vector
|
||||
|
@ -208,11 +207,9 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
|
|||
fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if self.initialization_found;
|
||||
if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
|
||||
if let ExprKind::Path(ref qpath_subj) = args[0].kind;
|
||||
if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]);
|
||||
if let ExprKind::MethodCall(path, _, [self_arg, extend_arg], _) = expr.kind;
|
||||
if path_to_local_id(self_arg, self.vec_alloc.local_id);
|
||||
if path.ident.name == sym!(extend);
|
||||
if let Some(extend_arg) = args.get(1);
|
||||
if self.is_repeat_take(extend_arg);
|
||||
|
||||
then {
|
||||
|
@ -225,11 +222,9 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
|
|||
fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if self.initialization_found;
|
||||
if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
|
||||
if let ExprKind::Path(ref qpath_subj) = args[0].kind;
|
||||
if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]);
|
||||
if let ExprKind::MethodCall(path, _, [self_arg, len_arg, fill_arg], _) = expr.kind;
|
||||
if path_to_local_id(self_arg, self.vec_alloc.local_id);
|
||||
if path.ident.name == sym!(resize);
|
||||
if let (Some(len_arg), Some(fill_arg)) = (args.get(1), args.get(2));
|
||||
|
||||
// Check that is filled with 0
|
||||
if let ExprKind::Lit(ref lit) = fill_arg.kind;
|
||||
|
@ -252,7 +247,7 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
|
|||
|
||||
// Check that take is applied to `repeat(0)`
|
||||
if let Some(repeat_expr) = take_args.get(0);
|
||||
if Self::is_repeat_zero(repeat_expr);
|
||||
if self.is_repeat_zero(repeat_expr);
|
||||
|
||||
// Check that len expression is equals to `with_capacity` expression
|
||||
if let Some(len_arg) = take_args.get(1);
|
||||
|
@ -267,21 +262,19 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
|
|||
}
|
||||
|
||||
/// Returns `true` if given expression is `repeat(0)`
|
||||
fn is_repeat_zero(expr: &Expr<'_>) -> bool {
|
||||
fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(fn_expr, repeat_args) = expr.kind;
|
||||
if let ExprKind::Path(ref qpath_repeat) = fn_expr.kind;
|
||||
if match_qpath(qpath_repeat, &["repeat"]);
|
||||
if let Some(repeat_arg) = repeat_args.get(0);
|
||||
if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind;
|
||||
if is_expr_path_def_path(self.cx, fn_expr, &paths::ITER_REPEAT);
|
||||
if let ExprKind::Lit(ref lit) = repeat_arg.kind;
|
||||
if let LitKind::Int(0, _) = lit.node;
|
||||
|
||||
then {
|
||||
return true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -195,7 +195,7 @@ fn attempt_to_emit_no_difference_lint(
|
|||
i: usize,
|
||||
expected_loc: IdentLocation,
|
||||
) {
|
||||
if let Some(binop) = binops.get(i).cloned() {
|
||||
if let Some(binop) = binops.get(i).copied() {
|
||||
// We need to try and figure out which identifier we should
|
||||
// suggest using instead. Since there could be multiple
|
||||
// replacement candidates in a given expression, and we're
|
||||
|
|
|
@ -104,30 +104,32 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> {
|
|||
// tracker to decide if the last group of tabs is not closed by a non-tab character
|
||||
let mut is_active = false;
|
||||
|
||||
let chars_array: Vec<_> = the_str.chars().collect();
|
||||
// Note that we specifically need the char _byte_ indices here, not the positional indexes
|
||||
// within the char array to deal with multi-byte characters properly. `char_indices` does
|
||||
// exactly that. It provides an iterator over tuples of the form `(byte position, char)`.
|
||||
let char_indices: Vec<_> = the_str.char_indices().collect();
|
||||
|
||||
if chars_array == vec!['\t'] {
|
||||
if let [(_, '\t')] = char_indices.as_slice() {
|
||||
return vec![(0, 1)];
|
||||
}
|
||||
|
||||
for (index, arr) in chars_array.windows(2).enumerate() {
|
||||
let index = u32::try_from(index).expect(line_length_way_to_long);
|
||||
match arr {
|
||||
['\t', '\t'] => {
|
||||
for entry in char_indices.windows(2) {
|
||||
match entry {
|
||||
[(_, '\t'), (_, '\t')] => {
|
||||
// either string starts with double tab, then we have to set it active,
|
||||
// otherwise is_active is true anyway
|
||||
is_active = true;
|
||||
},
|
||||
[_, '\t'] => {
|
||||
[(_, _), (index_b, '\t')] => {
|
||||
// as ['\t', '\t'] is excluded, this has to be a start of a tab group,
|
||||
// set indices accordingly
|
||||
is_active = true;
|
||||
current_start = index + 1;
|
||||
current_start = u32::try_from(*index_b).unwrap();
|
||||
},
|
||||
['\t', _] => {
|
||||
[(_, '\t'), (index_b, _)] => {
|
||||
// this now has to be an end of the group, hence we have to push a new tuple
|
||||
is_active = false;
|
||||
spans.push((current_start, index + 1));
|
||||
spans.push((current_start, u32::try_from(*index_b).unwrap()));
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
@ -137,7 +139,7 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> {
|
|||
if is_active {
|
||||
spans.push((
|
||||
current_start,
|
||||
u32::try_from(the_str.chars().count()).expect(line_length_way_to_long),
|
||||
u32::try_from(char_indices.last().unwrap().0 + 1).expect(line_length_way_to_long),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -148,6 +150,13 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> {
|
|||
mod tests_for_get_chunks_of_tabs {
|
||||
use super::get_chunks_of_tabs;
|
||||
|
||||
#[test]
|
||||
fn test_unicode_han_string() {
|
||||
let res = get_chunks_of_tabs(" \u{4f4d}\t");
|
||||
|
||||
assert_eq!(res, vec![(4, 5)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_string() {
|
||||
let res = get_chunks_of_tabs("");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{is_diagnostic_assoc_item, match_def_path, path_to_local_id, paths};
|
||||
use clippy_utils::{is_diag_trait_item, match_def_path, path_to_local_id, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -95,7 +95,7 @@ impl LateLintPass<'_> for ToStringInDisplay {
|
|||
if let ExprKind::MethodCall(path, _, args, _) = expr.kind;
|
||||
if path.ident.name == sym!(to_string);
|
||||
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString);
|
||||
if is_diag_trait_item(cx, expr_def_id, sym::ToString);
|
||||
if path_to_local_id(&args[0], self_hir_id);
|
||||
then {
|
||||
span_lint(
|
||||
|
|
|
@ -274,7 +274,7 @@ declare_clippy_lint! {
|
|||
/// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
|
||||
/// ```
|
||||
pub TRANSMUTE_PTR_TO_PTR,
|
||||
complexity,
|
||||
pedantic,
|
||||
"transmutes from a pointer to a pointer / a reference to a reference"
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::consts::{constant_context, Constant};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{match_qpath, paths};
|
||||
use clippy_utils::{is_expr_path_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
|
@ -37,18 +37,15 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull {
|
|||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func, args) = expr.kind;
|
||||
if let ExprKind::Path(ref path) = func.kind;
|
||||
if match_qpath(path, &paths::STD_MEM_TRANSMUTE);
|
||||
if args.len() == 1;
|
||||
if let ExprKind::Call(func, [arg]) = expr.kind;
|
||||
if is_expr_path_def_path(cx, func, &paths::TRANSMUTE);
|
||||
|
||||
then {
|
||||
|
||||
// Catching transmute over constants that resolve to `null`.
|
||||
let mut const_eval_context = constant_context(cx, cx.typeck_results());
|
||||
if_chain! {
|
||||
if let ExprKind::Path(ref _qpath) = args[0].kind;
|
||||
let x = const_eval_context.expr(&args[0]);
|
||||
if let ExprKind::Path(ref _qpath) = arg.kind;
|
||||
let x = const_eval_context.expr(arg);
|
||||
if let Some(Constant::RawPtr(0)) = x;
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
|
||||
|
@ -58,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull {
|
|||
// Catching:
|
||||
// `std::mem::transmute(0 as *const i32)`
|
||||
if_chain! {
|
||||
if let ExprKind::Cast(inner_expr, _cast_ty) = args[0].kind;
|
||||
if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
|
||||
if let ExprKind::Lit(ref lit) = inner_expr.kind;
|
||||
if let LitKind::Int(0, _) = lit.node;
|
||||
then {
|
||||
|
@ -69,10 +66,8 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull {
|
|||
// Catching:
|
||||
// `std::mem::transmute(std::ptr::null::<i32>())`
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func1, args1) = args[0].kind;
|
||||
if let ExprKind::Path(ref path1) = func1.kind;
|
||||
if match_qpath(path1, &paths::STD_PTR_NULL);
|
||||
if args1.is_empty();
|
||||
if let ExprKind::Call(func1, []) = arg.kind;
|
||||
if is_expr_path_def_path(cx, func1, &paths::PTR_NULL);
|
||||
then {
|
||||
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{snippet, snippet_with_macro_callsite};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{differing_macro_contexts, in_macro, match_def_path, match_qpath, paths};
|
||||
use clippy_utils::{differing_macro_contexts, get_parent_expr, in_macro, is_lang_ctor, match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::ResultErr;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for TryErr {
|
|||
if let ExprKind::Call(err_fun, err_args) = try_arg.kind;
|
||||
if let Some(err_arg) = err_args.get(0);
|
||||
if let ExprKind::Path(ref err_fun_path) = err_fun.kind;
|
||||
if match_qpath(err_fun_path, &paths::RESULT_ERR);
|
||||
if is_lang_ctor(cx, err_fun_path, ResultErr);
|
||||
if let Some(return_ty) = find_return_type(cx, &expr.kind);
|
||||
then {
|
||||
let prefix;
|
||||
|
@ -101,10 +102,15 @@ impl<'tcx> LateLintPass<'tcx> for TryErr {
|
|||
} else {
|
||||
snippet(cx, err_arg.span, "_")
|
||||
};
|
||||
let suggestion = if err_ty == expr_err_ty {
|
||||
format!("return {}{}{}", prefix, origin_snippet, suffix)
|
||||
let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) {
|
||||
"" // already returns
|
||||
} else {
|
||||
format!("return {}{}.into(){}", prefix, origin_snippet, suffix)
|
||||
"return "
|
||||
};
|
||||
let suggestion = if err_ty == expr_err_ty {
|
||||
format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix)
|
||||
} else {
|
||||
format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix)
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{match_path, paths};
|
||||
use clippy_utils::{match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{
|
||||
|
@ -28,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
|
|||
_ => None,
|
||||
});
|
||||
then {
|
||||
if is_any_trait(inner) {
|
||||
if is_any_trait(cx, inner) {
|
||||
// Ignore `Box<Any>` types; see issue #1884 for details.
|
||||
return false;
|
||||
}
|
||||
|
@ -84,13 +84,14 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
|
|||
}
|
||||
|
||||
// Returns true if given type is `Any` trait.
|
||||
fn is_any_trait(t: &hir::Ty<'_>) -> bool {
|
||||
fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let TyKind::TraitObject(traits, ..) = t.kind;
|
||||
if !traits.is_empty();
|
||||
if let Some(trait_did) = traits[0].trait_ref.trait_def_id();
|
||||
// Only Send/Sync can be used as additional traits, so it is enough to
|
||||
// check only the first trait.
|
||||
if match_path(traits[0].trait_ref.path, &paths::ANY_TRAIT);
|
||||
if match_def_path(cx, trait_did, &paths::ANY_TRAIT);
|
||||
then {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::{Item, ItemKind, UseTreeKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::kw;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for imports ending in `::{self}`.
|
||||
///
|
||||
/// **Why is this bad?** In most cases, this can be written much more cleanly by omitting `::{self}`.
|
||||
///
|
||||
/// **Known problems:** Removing `::{self}` will cause any non-module items at the same path to also be imported.
|
||||
/// This might cause a naming conflict (https://github.com/rust-lang/rustfmt/issues/3568). This lint makes no attempt
|
||||
/// to detect this scenario and that is why it is a restriction lint.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io::{self};
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// use std::io;
|
||||
/// ```
|
||||
pub UNNECESSARY_SELF_IMPORTS,
|
||||
restriction,
|
||||
"imports ending in `::{self}`, which can be omitted"
|
||||
}
|
||||
|
||||
declare_lint_pass!(UnnecessarySelfImports => [UNNECESSARY_SELF_IMPORTS]);
|
||||
|
||||
impl EarlyLintPass for UnnecessarySelfImports {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if_chain! {
|
||||
if let ItemKind::Use(use_tree) = &item.kind;
|
||||
if let UseTreeKind::Nested(nodes) = &use_tree.kind;
|
||||
if let [(self_tree, _)] = &**nodes;
|
||||
if let [self_seg] = &*self_tree.prefix.segments;
|
||||
if self_seg.ident.name == kw::SelfLower;
|
||||
if let Some(last_segment) = use_tree.prefix.segments.last();
|
||||
|
||||
then {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
UNNECESSARY_SELF_IMPORTS,
|
||||
item.span,
|
||||
"import ending with `::{self}`",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
last_segment.span().with_hi(item.span.hi()),
|
||||
"consider omitting `::{self}`",
|
||||
format!(
|
||||
"{}{};",
|
||||
last_segment.ident,
|
||||
if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {}", alias) } else { String::new() },
|
||||
),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.note("this will slightly change semantics; any non-module items at the same path will also be imported");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{contains_return, in_macro, match_qpath, paths, return_ty, visitors::find_all_ret_expressions};
|
||||
use clippy_utils::{contains_return, in_macro, is_lang_ctor, return_ty, visitors::find_all_ret_expressions};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::LangItem::{OptionSome, ResultOk};
|
||||
use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
|
@ -85,11 +86,11 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
|
|||
}
|
||||
|
||||
// Get the wrapper and inner types, if can't, abort.
|
||||
let (return_type_label, path, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() {
|
||||
let (return_type_label, lang_item, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() {
|
||||
if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did) {
|
||||
("Option", &paths::OPTION_SOME, subst.type_at(0))
|
||||
("Option", OptionSome, subst.type_at(0))
|
||||
} else if cx.tcx.is_diagnostic_item(sym::result_type, adt_def.did) {
|
||||
("Result", &paths::RESULT_OK, subst.type_at(0))
|
||||
("Result", ResultOk, subst.type_at(0))
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
@ -103,14 +104,12 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
|
|||
if_chain! {
|
||||
if !in_macro(ret_expr.span);
|
||||
// Check if a function call.
|
||||
if let ExprKind::Call(func, args) = ret_expr.kind;
|
||||
// Get the Path of the function call.
|
||||
if let ExprKind::Path(ref qpath) = func.kind;
|
||||
if let ExprKind::Call(func, [arg]) = ret_expr.kind;
|
||||
// Check if OPTION_SOME or RESULT_OK, depending on return type.
|
||||
if match_qpath(qpath, path);
|
||||
if args.len() == 1;
|
||||
if let ExprKind::Path(qpath) = &func.kind;
|
||||
if is_lang_ctor(cx, qpath, lang_item);
|
||||
// Make sure the function argument does not contain a return expression.
|
||||
if !contains_return(&args[0]);
|
||||
if !contains_return(arg);
|
||||
then {
|
||||
suggs.push(
|
||||
(
|
||||
|
@ -118,7 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
|
|||
if inner_type.is_unit() {
|
||||
"".to_string()
|
||||
} else {
|
||||
snippet(cx, args[0].span.source_callsite(), "..").to_string()
|
||||
snippet(cx, arg.span.source_callsite(), "..").to_string()
|
||||
}
|
||||
)
|
||||
);
|
||||
|
|
|
@ -41,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
|
|||
};
|
||||
|
||||
match expr.kind {
|
||||
hir::ExprKind::Match(res, _, _) if is_try(expr).is_some() => {
|
||||
hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => {
|
||||
if let hir::ExprKind::Call(func, args) = res.kind {
|
||||
if matches!(
|
||||
func.kind,
|
||||
|
|
|
@ -3,7 +3,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sug
|
|||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::match_type;
|
||||
use clippy_utils::{
|
||||
is_else_clause, is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq,
|
||||
is_else_clause, is_expn_of, is_expr_path_def_path, match_def_path, method_calls, path_to_res, paths, run_lints,
|
||||
SpanlessEq,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId};
|
||||
|
@ -578,8 +579,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
|
|||
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func, and_then_args) = expr.kind;
|
||||
if let ExprKind::Path(ref path) = func.kind;
|
||||
if match_qpath(path, &["span_lint_and_then"]);
|
||||
if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
|
||||
if and_then_args.len() == 5;
|
||||
if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
|
||||
let body = cx.tcx.hir().body(*body_id);
|
||||
|
@ -761,8 +761,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
|
|||
if_chain! {
|
||||
// Check if this is a call to utils::match_type()
|
||||
if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
|
||||
if let ExprKind::Path(fn_qpath) = &fn_path.kind;
|
||||
if match_qpath(fn_qpath, &["utils", "match_type"]);
|
||||
if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]);
|
||||
// Extract the path to the matched type
|
||||
if let Some(segments) = path_to_matched_type(cx, ty_path);
|
||||
let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
|
||||
|
@ -771,6 +770,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
|
|||
let diag_items = cx.tcx.diagnostic_items(ty_did.krate);
|
||||
if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None });
|
||||
then {
|
||||
// TODO: check paths constants from external crates.
|
||||
let cx_snippet = snippet(cx, context.span, "_");
|
||||
let ty_snippet = snippet(cx, ty.span, "_");
|
||||
|
||||
|
@ -778,9 +778,9 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
|
|||
cx,
|
||||
MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
|
||||
expr.span,
|
||||
"usage of `utils::match_type()` on a type diagnostic item",
|
||||
"usage of `clippy_utils::ty::match_type()` on a type diagnostic item",
|
||||
"try",
|
||||
format!("utils::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
|
||||
format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -573,7 +573,7 @@ impl Write {
|
|||
diag.multipart_suggestion(
|
||||
"try this",
|
||||
iter::once((comma_span.to(token_expr.span), String::new()))
|
||||
.chain(fmt_spans.iter().cloned().zip(iter::repeat(replacement)))
|
||||
.chain(fmt_spans.iter().copied().zip(iter::repeat(replacement)))
|
||||
.collect(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
|
||||
|
||||
use crate::{both, over};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::{self as ast, *};
|
||||
use rustc_span::symbol::Ident;
|
||||
|
@ -571,3 +572,34 @@ pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool {
|
|||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract args from an assert-like macro.
|
||||
///
|
||||
/// Currently working with:
|
||||
/// - `assert_eq!` and `assert_ne!`
|
||||
/// - `debug_assert_eq!` and `debug_assert_ne!`
|
||||
///
|
||||
/// For example:
|
||||
///
|
||||
/// `debug_assert_eq!(a, b)` will return Some([a, b])
|
||||
pub fn extract_assert_macro_args(mut expr: &Expr) -> Option<[&Expr; 2]> {
|
||||
if_chain! {
|
||||
if let ExprKind::If(_, ref block, _) = expr.kind;
|
||||
if let StmtKind::Semi(ref e) = block.stmts.get(0)?.kind;
|
||||
then {
|
||||
expr = e;
|
||||
}
|
||||
}
|
||||
if_chain! {
|
||||
if let ExprKind::Block(ref block, _) = expr.kind;
|
||||
if let StmtKind::Expr(ref expr) = block.stmts.get(0)?.kind;
|
||||
if let ExprKind::Match(ref match_expr, _) = expr.kind;
|
||||
if let ExprKind::Tup(ref tup) = match_expr.kind;
|
||||
if let [a, b, ..] = tup.as_slice();
|
||||
if let (&ExprKind::AddrOf(_, _, ref a), &ExprKind::AddrOf(_, _, ref b)) = (&a.kind, &b.kind);
|
||||
then {
|
||||
return Some([&*a, &*b]);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
|
@ -151,10 +151,9 @@ pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
|
|||
|
||||
/// Return true if the attributes contain `#[doc(hidden)]`
|
||||
pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool {
|
||||
#[allow(clippy::filter_map)]
|
||||
attrs
|
||||
.iter()
|
||||
.filter(|attr| attr.has_name(sym::doc))
|
||||
.flat_map(ast::Attribute::meta_item_list)
|
||||
.filter_map(ast::Attribute::meta_item_list)
|
||||
.any(|l| attr::list_contains_name(&l, sym::hidden))
|
||||
}
|
||||
|
|
|
@ -96,6 +96,16 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
|
||||
match (&left.kind, &right.kind) {
|
||||
(&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => {
|
||||
// This additional check ensures that the type of the locals are equivalent even if the init
|
||||
// expression or type have some inferred parts.
|
||||
if let Some(typeck) = self.inner.maybe_typeck_results {
|
||||
let l_ty = typeck.pat_ty(&l.pat);
|
||||
let r_ty = typeck.pat_ty(&r.pat);
|
||||
if !rustc_middle::ty::TyS::same_type(l_ty, r_ty) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that
|
||||
// these only get added if the init and type is equal.
|
||||
both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
|
||||
|
@ -424,7 +434,7 @@ fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'
|
|||
TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
|
||||
)
|
||||
})
|
||||
.ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().cloned()) =>
|
||||
.ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) =>
|
||||
{
|
||||
kind
|
||||
},
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
|
||||
extern crate rustc_ast;
|
||||
extern crate rustc_ast_pretty;
|
||||
extern crate rustc_attr;
|
||||
extern crate rustc_data_structures;
|
||||
extern crate rustc_errors;
|
||||
extern crate rustc_hir;
|
||||
|
@ -57,13 +58,14 @@ use if_chain::if_chain;
|
|||
use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::LangItem::{ResultErr, ResultOk};
|
||||
use rustc_hir::{
|
||||
def, Arm, BindingAnnotation, Block, Body, Constness, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem,
|
||||
ImplItemKind, Item, ItemKind, LangItem, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath,
|
||||
TraitItem, TraitItemKind, TraitRef, TyKind,
|
||||
def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
|
||||
ImplItem, ImplItemKind, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment,
|
||||
QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
||||
use rustc_middle::hir::exports::Export;
|
||||
|
@ -80,7 +82,7 @@ use rustc_span::{Span, DUMMY_SP};
|
|||
use rustc_target::abi::Integer;
|
||||
|
||||
use crate::consts::{constant, Constant};
|
||||
use crate::ty::is_recursively_primitive_type;
|
||||
use crate::ty::{can_partially_move_ty, is_recursively_primitive_type};
|
||||
|
||||
pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
|
||||
if let Ok(version) = RustcVersion::parse(msrv) {
|
||||
|
@ -222,6 +224,19 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks if a `QPath` resolves to a constructor of a `LangItem`.
|
||||
/// For example, use this to check whether a function call or a pattern is `Some(..)`.
|
||||
pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool {
|
||||
if let QPath::Resolved(_, path) = qpath {
|
||||
if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
|
||||
if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) {
|
||||
return cx.tcx.parent(ctor_id) == Some(item_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns `true` if this `span` was expanded by any macro.
|
||||
#[must_use]
|
||||
pub fn in_macro(span: Span) -> bool {
|
||||
|
@ -279,27 +294,29 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str])
|
|||
trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
|
||||
}
|
||||
|
||||
/// Checks if the method call given in `def_id` belongs to a trait or other container with a given
|
||||
/// diagnostic item
|
||||
pub fn is_diagnostic_assoc_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
|
||||
cx.tcx
|
||||
.opt_associated_item(def_id)
|
||||
.and_then(|associated_item| match associated_item.container {
|
||||
rustc_ty::TraitContainer(assoc_def_id) => Some(assoc_def_id),
|
||||
rustc_ty::ImplContainer(assoc_def_id) => match cx.tcx.type_of(assoc_def_id).kind() {
|
||||
rustc_ty::Adt(adt, _) => Some(adt.did),
|
||||
rustc_ty::Slice(_) => cx.tcx.get_diagnostic_item(sym::slice), // this isn't perfect but it works
|
||||
_ => None,
|
||||
},
|
||||
})
|
||||
.map_or(false, |assoc_def_id| cx.tcx.is_diagnostic_item(diag_item, assoc_def_id))
|
||||
/// Checks if a method is defined in an impl of a diagnostic item
|
||||
pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
|
||||
if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
|
||||
if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
|
||||
return cx.tcx.is_diagnostic_item(diag_item, adt.did);
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Checks if a method is in a diagnostic item trait
|
||||
pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
|
||||
if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
|
||||
return cx.tcx.is_diagnostic_item(diag_item, trait_did);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Checks if the method call given in `expr` belongs to the given trait.
|
||||
pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
|
||||
cx.typeck_results()
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
.map_or(false, |did| is_diagnostic_assoc_item(cx, did, diag_item))
|
||||
.map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
|
||||
}
|
||||
|
||||
/// Checks if an expression references a variable of the given name.
|
||||
|
@ -380,6 +397,29 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
/// If the expression is a path, resolve it. Otherwise, return `Res::Err`.
|
||||
pub fn expr_path_res(cx: &LateContext<'_>, expr: &Expr<'_>) -> Res {
|
||||
if let ExprKind::Path(p) = &expr.kind {
|
||||
cx.qpath_res(p, expr.hir_id)
|
||||
} else {
|
||||
Res::Err
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves the path to a `DefId` and checks if it matches the given path.
|
||||
pub fn is_qpath_def_path(cx: &LateContext<'_>, path: &QPath<'_>, hir_id: HirId, segments: &[&str]) -> bool {
|
||||
cx.qpath_res(path, hir_id)
|
||||
.opt_def_id()
|
||||
.map_or(false, |id| match_def_path(cx, id, segments))
|
||||
}
|
||||
|
||||
/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
|
||||
pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
|
||||
expr_path_res(cx, expr)
|
||||
.opt_def_id()
|
||||
.map_or(false, |id| match_def_path(cx, id, segments))
|
||||
}
|
||||
|
||||
/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
|
||||
/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
|
||||
/// `QPath::Resolved.1.res.opt_def_id()`.
|
||||
|
@ -408,20 +448,6 @@ pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
|
|||
.all(|(a, b)| a.ident.name.as_str() == *b)
|
||||
}
|
||||
|
||||
/// Matches a `Path` against a slice of segment string literals, e.g.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust,ignore
|
||||
/// match_path_ast(path, &["std", "rt", "begin_unwind"])
|
||||
/// ```
|
||||
pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool {
|
||||
path.segments
|
||||
.iter()
|
||||
.rev()
|
||||
.zip(segments.iter().rev())
|
||||
.all(|(a, b)| a.ident.name.as_str() == *b)
|
||||
}
|
||||
|
||||
/// If the expression is a path to a local, returns the canonical `HirId` of the local.
|
||||
pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
|
||||
if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
|
||||
|
@ -522,6 +548,73 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
|
|||
None
|
||||
}
|
||||
|
||||
/// Checks if the top level expression can be moved into a closure as is.
|
||||
pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, jump_targets: &[HirId]) -> bool {
|
||||
match expr.kind {
|
||||
ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
|
||||
| ExprKind::Continue(Destination { target_id: Ok(id), .. })
|
||||
if jump_targets.contains(&id) =>
|
||||
{
|
||||
true
|
||||
},
|
||||
ExprKind::Break(..)
|
||||
| ExprKind::Continue(_)
|
||||
| ExprKind::Ret(_)
|
||||
| ExprKind::Yield(..)
|
||||
| ExprKind::InlineAsm(_)
|
||||
| ExprKind::LlvmInlineAsm(_) => false,
|
||||
// Accessing a field of a local value can only be done if the type isn't
|
||||
// partially moved.
|
||||
ExprKind::Field(base_expr, _)
|
||||
if matches!(
|
||||
base_expr.kind,
|
||||
ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. }))
|
||||
) && can_partially_move_ty(cx, cx.typeck_results().expr_ty(base_expr)) =>
|
||||
{
|
||||
// TODO: check if the local has been partially moved. Assume it has for now.
|
||||
false
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the expression can be moved into a closure as is.
|
||||
pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||
struct V<'cx, 'tcx> {
|
||||
cx: &'cx LateContext<'tcx>,
|
||||
loops: Vec<HirId>,
|
||||
allow_closure: bool,
|
||||
}
|
||||
impl Visitor<'tcx> for V<'_, 'tcx> {
|
||||
type Map = ErasedMap<'tcx>;
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||
if !self.allow_closure {
|
||||
return;
|
||||
}
|
||||
if let ExprKind::Loop(b, ..) = e.kind {
|
||||
self.loops.push(e.hir_id);
|
||||
self.visit_block(b);
|
||||
self.loops.pop();
|
||||
} else {
|
||||
self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops);
|
||||
walk_expr(self, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut v = V {
|
||||
cx,
|
||||
allow_closure: true,
|
||||
loops: Vec::new(),
|
||||
};
|
||||
v.visit_expr(expr);
|
||||
v.allow_closure
|
||||
}
|
||||
|
||||
/// Returns the method names and argument list of nested method call expressions that make up
|
||||
/// `expr`. method/span lists are sorted with the most recent call first.
|
||||
pub fn method_calls<'tcx>(
|
||||
|
@ -960,7 +1053,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
|
|||
/// the function once on the given pattern.
|
||||
pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
|
||||
if let PatKind::Or(pats) = pat.kind {
|
||||
pats.iter().cloned().for_each(f)
|
||||
pats.iter().copied().for_each(f)
|
||||
} else {
|
||||
f(pat)
|
||||
}
|
||||
|
@ -1011,11 +1104,11 @@ pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl It
|
|||
|
||||
/// Checks if a given expression is a match expression expanded from the `?`
|
||||
/// operator or the `try` macro.
|
||||
pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
fn is_ok(arm: &Arm<'_>) -> bool {
|
||||
pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let PatKind::TupleStruct(ref path, ref pat, None) = arm.pat.kind;
|
||||
if match_qpath(path, &paths::RESULT_OK[1..]);
|
||||
if is_lang_ctor(cx, path, ResultOk);
|
||||
if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
|
||||
if path_to_local_id(arm.body, hir_id);
|
||||
then {
|
||||
|
@ -1025,9 +1118,9 @@ pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
|||
false
|
||||
}
|
||||
|
||||
fn is_err(arm: &Arm<'_>) -> bool {
|
||||
fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
|
||||
if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
|
||||
match_qpath(path, &paths::RESULT_ERR[1..])
|
||||
is_lang_ctor(cx, path, ResultErr)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -1043,8 +1136,8 @@ pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
|||
if arms.len() == 2;
|
||||
if arms[0].guard.is_none();
|
||||
if arms[1].guard.is_none();
|
||||
if (is_ok(&arms[0]) && is_err(&arms[1])) ||
|
||||
(is_ok(&arms[1]) && is_err(&arms[0]));
|
||||
if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) ||
|
||||
(is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
|
||||
then {
|
||||
return Some(expr);
|
||||
}
|
||||
|
@ -1131,29 +1224,47 @@ pub fn match_function_call<'tcx>(
|
|||
None
|
||||
}
|
||||
|
||||
pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
|
||||
// We have to convert `syms` to `&[Symbol]` here because rustc's `match_def_path`
|
||||
// accepts only that. We should probably move to Symbols in Clippy as well.
|
||||
let syms = syms.iter().map(|p| Symbol::intern(p)).collect::<Vec<Symbol>>();
|
||||
cx.match_def_path(did, &syms)
|
||||
/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
|
||||
/// any.
|
||||
pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
|
||||
let search_path = cx.get_def_path(did);
|
||||
paths
|
||||
.iter()
|
||||
.position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
|
||||
}
|
||||
|
||||
pub fn match_panic_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx [Expr<'tcx>]> {
|
||||
match_function_call(cx, expr, &paths::BEGIN_PANIC)
|
||||
.or_else(|| match_function_call(cx, expr, &paths::BEGIN_PANIC_FMT))
|
||||
.or_else(|| match_function_call(cx, expr, &paths::PANIC_ANY))
|
||||
.or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC))
|
||||
.or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_FMT))
|
||||
.or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_STR))
|
||||
/// Checks if the given `DefId` matches the path.
|
||||
pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
|
||||
// We should probably move to Symbols in Clippy as well rather than interning every time.
|
||||
let path = cx.get_def_path(did);
|
||||
syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
|
||||
}
|
||||
|
||||
pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if let ExprKind::Call(func, [arg]) = expr.kind {
|
||||
expr_path_res(cx, func)
|
||||
.opt_def_id()
|
||||
.map_or(false, |id| match_panic_def_id(cx, id))
|
||||
.then(|| arg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool {
|
||||
match_def_path(cx, did, &paths::BEGIN_PANIC)
|
||||
|| match_def_path(cx, did, &paths::BEGIN_PANIC_FMT)
|
||||
|| match_def_path(cx, did, &paths::PANIC_ANY)
|
||||
|| match_def_path(cx, did, &paths::PANICKING_PANIC)
|
||||
|| match_def_path(cx, did, &paths::PANICKING_PANIC_FMT)
|
||||
|| match_def_path(cx, did, &paths::PANICKING_PANIC_STR)
|
||||
match_any_def_paths(
|
||||
cx,
|
||||
did,
|
||||
&[
|
||||
&paths::BEGIN_PANIC,
|
||||
&paths::BEGIN_PANIC_FMT,
|
||||
&paths::PANIC_ANY,
|
||||
&paths::PANICKING_PANIC,
|
||||
&paths::PANICKING_PANIC_FMT,
|
||||
&paths::PANICKING_PANIC_STR,
|
||||
],
|
||||
)
|
||||
.is_some()
|
||||
}
|
||||
|
||||
/// Returns the list of condition expressions and the list of blocks in a
|
||||
|
@ -1189,21 +1300,6 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>,
|
|||
(conds, blocks)
|
||||
}
|
||||
|
||||
/// This function returns true if the given expression is the `else` or `if else` part of an if
|
||||
/// statement
|
||||
pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool {
|
||||
let map = cx.tcx.hir();
|
||||
let parent_id = map.get_parent_node(expr.hir_id);
|
||||
let parent_node = map.get(parent_id);
|
||||
matches!(
|
||||
parent_node,
|
||||
Node::Expr(Expr {
|
||||
kind: ExprKind::If(_, _, _),
|
||||
..
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// Finds the `#[must_use]` attribute, if any
|
||||
pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
|
||||
attrs.iter().find(|a| a.has_name(sym::must_use))
|
||||
|
@ -1228,6 +1324,51 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some())
|
||||
}
|
||||
|
||||
/// Gets the node where an expression is either used, or it's type is unified with another branch.
|
||||
pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
|
||||
let map = tcx.hir();
|
||||
let mut child_id = expr.hir_id;
|
||||
let mut iter = map.parent_iter(child_id);
|
||||
loop {
|
||||
match iter.next() {
|
||||
None => break None,
|
||||
Some((id, Node::Block(_))) => child_id = id,
|
||||
Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
|
||||
Some((_, Node::Expr(expr))) => match expr.kind {
|
||||
ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
|
||||
ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
|
||||
ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
|
||||
_ => break Some(Node::Expr(expr)),
|
||||
},
|
||||
Some((_, node)) => break Some(node),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the result of an expression is used, or it's type is unified with another branch.
|
||||
pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
|
||||
!matches!(
|
||||
get_expr_use_or_unification_node(tcx, expr),
|
||||
None | Some(Node::Stmt(Stmt {
|
||||
kind: StmtKind::Expr(_)
|
||||
| StmtKind::Semi(_)
|
||||
| StmtKind::Local(Local {
|
||||
pat: Pat {
|
||||
kind: PatKind::Wild,
|
||||
..
|
||||
},
|
||||
..
|
||||
}),
|
||||
..
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks if the expression is the final expression returned from a block.
|
||||
pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
|
||||
matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
|
||||
}
|
||||
|
||||
pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
|
||||
cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
|
||||
if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
|
||||
|
@ -1397,28 +1538,43 @@ pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
|
|||
peel(pat, 0)
|
||||
}
|
||||
|
||||
/// Peels of expressions while the given closure returns `Some`.
|
||||
pub fn peel_hir_expr_while<'tcx>(
|
||||
mut expr: &'tcx Expr<'tcx>,
|
||||
mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
|
||||
) -> &'tcx Expr<'tcx> {
|
||||
while let Some(e) = f(expr) {
|
||||
expr = e;
|
||||
}
|
||||
expr
|
||||
}
|
||||
|
||||
/// Peels off up to the given number of references on the expression. Returns the underlying
|
||||
/// expression and the number of references removed.
|
||||
pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
|
||||
fn f(expr: &'a Expr<'a>, count: usize, target: usize) -> (&'a Expr<'a>, usize) {
|
||||
match expr.kind {
|
||||
ExprKind::AddrOf(_, _, expr) if count != target => f(expr, count + 1, target),
|
||||
_ => (expr, count),
|
||||
}
|
||||
}
|
||||
f(expr, 0, count)
|
||||
let mut remaining = count;
|
||||
let e = peel_hir_expr_while(expr, |e| match e.kind {
|
||||
ExprKind::AddrOf(BorrowKind::Ref, _, e) if remaining != 0 => {
|
||||
remaining -= 1;
|
||||
Some(e)
|
||||
},
|
||||
_ => None,
|
||||
});
|
||||
(e, count - remaining)
|
||||
}
|
||||
|
||||
/// Peels off all references on the expression. Returns the underlying expression and the number of
|
||||
/// references removed.
|
||||
pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
|
||||
fn f(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
|
||||
match expr.kind {
|
||||
ExprKind::AddrOf(BorrowKind::Ref, _, expr) => f(expr, count + 1),
|
||||
_ => (expr, count),
|
||||
}
|
||||
}
|
||||
f(expr, 0)
|
||||
let mut count = 0;
|
||||
let e = peel_hir_expr_while(expr, |e| match e.kind {
|
||||
ExprKind::AddrOf(BorrowKind::Ref, _, e) => {
|
||||
count += 1;
|
||||
Some(e)
|
||||
},
|
||||
_ => None,
|
||||
});
|
||||
(e, count)
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
@ -1450,27 +1606,3 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the resolution of a given path is an `Ok` variant of `Result`.
|
||||
pub fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool {
|
||||
if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() {
|
||||
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
|
||||
if let Some(variant_id) = cx.tcx.parent(id) {
|
||||
return variant_id == ok_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Check if the resolution of a given path is a `Some` variant of `Option`.
|
||||
pub fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool {
|
||||
if let Some(some_id) = cx.tcx.lang_items().option_some_variant() {
|
||||
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
|
||||
if let Some(variant_id) = cx.tcx.parent(id) {
|
||||
return variant_id == some_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//! Whenever possible, please consider diagnostic items over hardcoded paths.
|
||||
//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
|
||||
|
||||
pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
|
||||
pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"];
|
||||
pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
|
||||
pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
|
||||
pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
|
||||
|
@ -13,13 +13,13 @@ pub(super) const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_
|
|||
pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"];
|
||||
pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
|
||||
pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"];
|
||||
pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
|
||||
pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
|
||||
pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
|
||||
pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"];
|
||||
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
|
||||
pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
|
||||
pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
|
||||
pub const COPY: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"];
|
||||
pub const COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy"];
|
||||
pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
|
||||
pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"];
|
||||
pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
|
||||
|
@ -44,10 +44,14 @@ pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments
|
|||
pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
|
||||
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
|
||||
pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
|
||||
pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
|
||||
pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"];
|
||||
pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
|
||||
pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
|
||||
pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
|
||||
pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
|
||||
pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
|
||||
pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
|
||||
pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
|
||||
|
@ -60,8 +64,9 @@ pub const INTO: [&str; 3] = ["core", "convert", "Into"];
|
|||
pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
|
||||
pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
|
||||
pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
|
||||
pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"];
|
||||
pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"];
|
||||
pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
|
||||
pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
|
||||
pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
|
@ -100,12 +105,23 @@ pub const PERMISSIONS_FROM_MODE: [&str; 7] = ["std", "sys", "unix", "ext", "fs",
|
|||
pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
|
||||
pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"];
|
||||
pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"];
|
||||
pub const PTR_COPY: [&str; 4] = ["core", "intrinsics", "", "copy"];
|
||||
pub const PTR_COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"];
|
||||
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
|
||||
pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"];
|
||||
pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"];
|
||||
pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"];
|
||||
pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"];
|
||||
pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"];
|
||||
pub const PTR_READ: [&str; 3] = ["core", "ptr", "read"];
|
||||
pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"];
|
||||
pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"];
|
||||
pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"];
|
||||
pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"];
|
||||
pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"];
|
||||
pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
|
||||
pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"];
|
||||
pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"];
|
||||
pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
|
||||
pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
|
||||
pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
|
||||
|
@ -117,7 +133,6 @@ pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
|
|||
pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
|
||||
pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
|
||||
pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
|
||||
pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"];
|
||||
pub const RESULT: [&str; 3] = ["core", "result", "Result"];
|
||||
pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
|
||||
pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
|
||||
|
@ -131,10 +146,8 @@ pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec
|
|||
pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
|
||||
pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
|
||||
pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
|
||||
pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"];
|
||||
pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
|
||||
pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
|
||||
pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"];
|
||||
pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"];
|
||||
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
|
||||
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
|
||||
pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
// This code used to be a part of `rustc` but moved to Clippy as a result of
|
||||
// https://github.com/rust-lang/rust/issues/76618. Because of that, it contains unused code and some
|
||||
// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
|
||||
// differ from the time of `rustc` even if the name stays the same.
|
||||
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::mir::{
|
||||
|
@ -6,6 +11,7 @@ use rustc_middle::mir::{
|
|||
};
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Span;
|
||||
use rustc_target::spec::abi::Abi::RustIntrinsic;
|
||||
|
@ -13,7 +19,7 @@ use std::borrow::Cow;
|
|||
|
||||
type McfResult = Result<(), (Span, Cow<'static, str>)>;
|
||||
|
||||
pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
|
||||
pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult {
|
||||
let def_id = body.source.def_id();
|
||||
let mut current = def_id;
|
||||
loop {
|
||||
|
@ -70,7 +76,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult {
|
|||
)?;
|
||||
|
||||
for bb in body.basic_blocks() {
|
||||
check_terminator(tcx, body, bb.terminator())?;
|
||||
check_terminator(tcx, body, bb.terminator(), msrv)?;
|
||||
for stmt in &bb.statements {
|
||||
check_statement(tcx, body, def_id, stmt)?;
|
||||
}
|
||||
|
@ -268,7 +274,12 @@ fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'t
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
|
||||
fn check_terminator(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &'a Body<'tcx>,
|
||||
terminator: &Terminator<'tcx>,
|
||||
msrv: Option<&RustcVersion>,
|
||||
) -> McfResult {
|
||||
let span = terminator.source_info.span;
|
||||
match &terminator.kind {
|
||||
TerminatorKind::FalseEdge { .. }
|
||||
|
@ -305,7 +316,7 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin
|
|||
} => {
|
||||
let fn_ty = func.ty(body, tcx);
|
||||
if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
|
||||
if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
|
||||
if !is_const_fn(tcx, fn_def_id, msrv) {
|
||||
return Err((
|
||||
span,
|
||||
format!(
|
||||
|
@ -350,3 +361,24 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin
|
|||
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool {
|
||||
rustc_mir::const_eval::is_const_fn(tcx, def_id)
|
||||
&& if let Some(const_stab) = tcx.lookup_const_stability(def_id) {
|
||||
if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level {
|
||||
// Checking MSRV is manually necessary because `rustc` has no such concept. This entire
|
||||
// function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`.
|
||||
// as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
|
||||
crate::meets_msrv(
|
||||
msrv,
|
||||
&RustcVersion::parse(&since.as_str())
|
||||
.expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"),
|
||||
)
|
||||
} else {
|
||||
// `rustc_mir::const_eval::is_const_fn` should return false for unstably const functions.
|
||||
unreachable!();
|
||||
}
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,15 @@ pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
|
|||
snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
|
||||
}
|
||||
|
||||
/// Gets a snippet of the indentation of the line of a span
|
||||
pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> {
|
||||
snippet_opt(cx, line_span(cx, span)).map(|mut s| {
|
||||
let len = s.len() - s.trim_start().len();
|
||||
s.truncate(len);
|
||||
s
|
||||
})
|
||||
}
|
||||
|
||||
// If the snippet is empty, it's an attribute that was inserted during macro
|
||||
// expansion and we want to ignore those, because they could come from external
|
||||
// sources that the user has no control over.
|
||||
|
|
|
@ -13,7 +13,7 @@ use rustc_lint::LateContext;
|
|||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
|
||||
use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TypeFoldable, UintTy};
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::symbol::{Ident, Symbol};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use rustc_trait_selection::traits::query::normalize::AtExt;
|
||||
|
||||
|
@ -52,6 +52,25 @@ pub fn contains_adt_constructor(ty: Ty<'_>, adt: &AdtDef) -> bool {
|
|||
})
|
||||
}
|
||||
|
||||
/// Resolves `<T as Iterator>::Item` for `T`
|
||||
/// Do not invoke without first verifying that the type implements `Iterator`
|
||||
pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
cx.tcx
|
||||
.get_diagnostic_item(sym::Iterator)
|
||||
.and_then(|iter_did| {
|
||||
cx.tcx.associated_items(iter_did).find_by_name_and_kind(
|
||||
cx.tcx,
|
||||
Ident::from_str("Item"),
|
||||
ty::AssocKind::Type,
|
||||
iter_did,
|
||||
)
|
||||
})
|
||||
.map(|assoc| {
|
||||
let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[]));
|
||||
cx.tcx.normalize_erasing_regions(cx.param_env, proj)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns true if ty has `iter` or `iter_mut` methods
|
||||
pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
|
||||
// FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
|
||||
|
|
|
@ -625,7 +625,7 @@ in the following steps:
|
|||
Here are some pointers to things you are likely going to need for every lint:
|
||||
|
||||
* [Clippy utils][utils] - Various helper functions. Maybe the function you need
|
||||
is already in here (`implements_trait`, `match_path`, `snippet`, etc)
|
||||
is already in here (`implements_trait`, `match_def_path`, `snippet`, etc)
|
||||
* [Clippy diagnostics][diagnostics]
|
||||
* [The `if_chain` macro][if_chain]
|
||||
* [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// When a new lint is introduced, we can search the results for new warnings and check for false
|
||||
// positives.
|
||||
|
||||
#![allow(clippy::filter_map, clippy::collapsible_else_if)]
|
||||
#![allow(clippy::collapsible_else_if)]
|
||||
|
||||
use std::ffi::OsStr;
|
||||
use std::process::Command;
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "nightly-2021-04-08"
|
||||
channel = "nightly-2021-04-22"
|
||||
components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
|
||||
|
|
|
@ -2,58 +2,18 @@
|
|||
#![deny(clippy::internal)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate clippy_utils;
|
||||
extern crate rustc_ast;
|
||||
extern crate rustc_errors;
|
||||
extern crate rustc_lint;
|
||||
extern crate rustc_session;
|
||||
extern crate rustc_span;
|
||||
|
||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
|
||||
use rustc_ast::ast::Expr;
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
|
||||
where
|
||||
F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
|
||||
{
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn span_lint_and_help<'a, T: LintContext>(
|
||||
cx: &'a T,
|
||||
lint: &'static Lint,
|
||||
span: Span,
|
||||
msg: &str,
|
||||
option_span: Option<Span>,
|
||||
help: &str,
|
||||
) {
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn span_lint_and_note<'a, T: LintContext>(
|
||||
cx: &'a T,
|
||||
lint: &'static Lint,
|
||||
span: Span,
|
||||
msg: &str,
|
||||
note_span: Option<Span>,
|
||||
note: &str,
|
||||
) {
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn span_lint_and_sugg<'a, T: LintContext>(
|
||||
cx: &'a T,
|
||||
lint: &'static Lint,
|
||||
sp: Span,
|
||||
msg: &str,
|
||||
help: &str,
|
||||
sugg: String,
|
||||
applicability: Applicability,
|
||||
) {
|
||||
}
|
||||
|
||||
declare_tool_lint! {
|
||||
pub clippy::TEST_LINT,
|
||||
|
|
|
@ -2,58 +2,18 @@
|
|||
#![deny(clippy::internal)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate clippy_utils;
|
||||
extern crate rustc_ast;
|
||||
extern crate rustc_errors;
|
||||
extern crate rustc_lint;
|
||||
extern crate rustc_session;
|
||||
extern crate rustc_span;
|
||||
|
||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
|
||||
use rustc_ast::ast::Expr;
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
|
||||
where
|
||||
F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
|
||||
{
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn span_lint_and_help<'a, T: LintContext>(
|
||||
cx: &'a T,
|
||||
lint: &'static Lint,
|
||||
span: Span,
|
||||
msg: &str,
|
||||
option_span: Option<Span>,
|
||||
help: &str,
|
||||
) {
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn span_lint_and_note<'a, T: LintContext>(
|
||||
cx: &'a T,
|
||||
lint: &'static Lint,
|
||||
span: Span,
|
||||
msg: &str,
|
||||
note_span: Option<Span>,
|
||||
note: &str,
|
||||
) {
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn span_lint_and_sugg<'a, T: LintContext>(
|
||||
cx: &'a T,
|
||||
lint: &'static Lint,
|
||||
sp: Span,
|
||||
msg: &str,
|
||||
help: &str,
|
||||
sugg: String,
|
||||
applicability: Applicability,
|
||||
) {
|
||||
}
|
||||
|
||||
declare_tool_lint! {
|
||||
pub clippy::TEST_LINT,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: this call is collapsible
|
||||
--> $DIR/collapsible_span_lint_calls.rs:75:9
|
||||
--> $DIR/collapsible_span_lint_calls.rs:35:9
|
||||
|
|
||||
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable);
|
||||
|
@ -14,7 +14,7 @@ LL | #![deny(clippy::internal)]
|
|||
= note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]`
|
||||
|
||||
error: this call is collapsible
|
||||
--> $DIR/collapsible_span_lint_calls.rs:78:9
|
||||
--> $DIR/collapsible_span_lint_calls.rs:38:9
|
||||
|
|
||||
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
LL | | db.span_help(expr.span, help_msg);
|
||||
|
@ -22,7 +22,7 @@ LL | | });
|
|||
| |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)`
|
||||
|
||||
error: this call is collapsible
|
||||
--> $DIR/collapsible_span_lint_calls.rs:81:9
|
||||
--> $DIR/collapsible_span_lint_calls.rs:41:9
|
||||
|
|
||||
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
LL | | db.help(help_msg);
|
||||
|
@ -30,7 +30,7 @@ LL | | });
|
|||
| |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)`
|
||||
|
||||
error: this call is collspible
|
||||
--> $DIR/collapsible_span_lint_calls.rs:84:9
|
||||
--> $DIR/collapsible_span_lint_calls.rs:44:9
|
||||
|
|
||||
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
LL | | db.span_note(expr.span, note_msg);
|
||||
|
@ -38,7 +38,7 @@ LL | | });
|
|||
| |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)`
|
||||
|
||||
error: this call is collspible
|
||||
--> $DIR/collapsible_span_lint_calls.rs:87:9
|
||||
--> $DIR/collapsible_span_lint_calls.rs:47:9
|
||||
|
|
||||
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
|
||||
LL | | db.note(note_msg);
|
||||
|
|
|
@ -1,29 +1,18 @@
|
|||
#![deny(clippy::internal)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate clippy_utils;
|
||||
extern crate rustc_hir;
|
||||
extern crate rustc_lint;
|
||||
extern crate rustc_middle;
|
||||
|
||||
#[macro_use]
|
||||
extern crate rustc_session;
|
||||
use clippy_utils::{paths, ty::match_type};
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::Ty;
|
||||
|
||||
mod paths {
|
||||
pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
|
||||
}
|
||||
|
||||
mod utils {
|
||||
use super::*;
|
||||
|
||||
pub fn match_type(_cx: &LateContext<'_>, _ty: Ty<'_>, _path: &[&str]) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
use utils::match_type;
|
||||
|
||||
declare_lint! {
|
||||
pub TEST_LINT,
|
||||
Warn,
|
||||
|
@ -38,12 +27,12 @@ impl<'tcx> LateLintPass<'tcx> for Pass {
|
|||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
|
||||
let _ = match_type(cx, ty, &paths::VEC);
|
||||
let _ = match_type(cx, ty, &paths::VEC); // FIXME: Doesn't lint external paths
|
||||
let _ = match_type(cx, ty, &OPTION);
|
||||
let _ = match_type(cx, ty, &["core", "result", "Result"]);
|
||||
|
||||
let rc_path = &["alloc", "rc", "Rc"];
|
||||
let _ = utils::match_type(cx, ty, rc_path);
|
||||
let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: usage of `utils::match_type()` on a type diagnostic item
|
||||
--> $DIR/match_type_on_diag_item.rs:41:17
|
||||
error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
|
||||
--> $DIR/match_type_on_diag_item.rs:31:17
|
||||
|
|
||||
LL | let _ = match_type(cx, ty, &paths::VEC);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::vec_type)`
|
||||
LL | let _ = match_type(cx, ty, &OPTION);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::option_type)`
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/match_type_on_diag_item.rs:1:9
|
||||
|
@ -11,23 +11,17 @@ LL | #![deny(clippy::internal)]
|
|||
| ^^^^^^^^^^^^^^^^
|
||||
= note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]`
|
||||
|
||||
error: usage of `utils::match_type()` on a type diagnostic item
|
||||
--> $DIR/match_type_on_diag_item.rs:42:17
|
||||
|
|
||||
LL | let _ = match_type(cx, ty, &OPTION);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::option_type)`
|
||||
|
||||
error: usage of `utils::match_type()` on a type diagnostic item
|
||||
--> $DIR/match_type_on_diag_item.rs:43:17
|
||||
error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
|
||||
--> $DIR/match_type_on_diag_item.rs:32:17
|
||||
|
|
||||
LL | let _ = match_type(cx, ty, &["core", "result", "Result"]);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::result_type)`
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::result_type)`
|
||||
|
||||
error: usage of `utils::match_type()` on a type diagnostic item
|
||||
--> $DIR/match_type_on_diag_item.rs:46:17
|
||||
error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
|
||||
--> $DIR/match_type_on_diag_item.rs:35:17
|
||||
|
|
||||
LL | let _ = utils::match_type(cx, ty, rc_path);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::Rc)`
|
||||
LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Rc)`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#![feature(asm)]
|
||||
// only-x86_64
|
||||
// ignore-aarch64
|
||||
|
||||
#![feature(asm)]
|
||||
|
||||
#[warn(clippy::inline_asm_x86_intel_syntax)]
|
||||
mod warn_intel {
|
||||
|
@ -23,6 +25,7 @@ mod warn_att {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn main() {
|
||||
unsafe {
|
||||
warn_att::use_asm();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: Intel x86 assembly syntax used
|
||||
--> $DIR/asm_syntax.rs:7:9
|
||||
--> $DIR/asm_syntax.rs:9:9
|
||||
|
|
||||
LL | asm!("");
|
||||
| ^^^^^^^^^
|
||||
|
@ -8,7 +8,7 @@ LL | asm!("");
|
|||
= help: use AT&T x86 assembly syntax
|
||||
|
||||
error: Intel x86 assembly syntax used
|
||||
--> $DIR/asm_syntax.rs:8:9
|
||||
--> $DIR/asm_syntax.rs:10:9
|
||||
|
|
||||
LL | asm!("", options());
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -16,7 +16,7 @@ LL | asm!("", options());
|
|||
= help: use AT&T x86 assembly syntax
|
||||
|
||||
error: Intel x86 assembly syntax used
|
||||
--> $DIR/asm_syntax.rs:9:9
|
||||
--> $DIR/asm_syntax.rs:11:9
|
||||
|
|
||||
LL | asm!("", options(nostack));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -24,7 +24,7 @@ LL | asm!("", options(nostack));
|
|||
= help: use AT&T x86 assembly syntax
|
||||
|
||||
error: AT&T x86 assembly syntax used
|
||||
--> $DIR/asm_syntax.rs:21:9
|
||||
--> $DIR/asm_syntax.rs:23:9
|
||||
|
|
||||
LL | asm!("", options(att_syntax));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -33,7 +33,7 @@ LL | asm!("", options(att_syntax));
|
|||
= help: use Intel x86 assembly syntax
|
||||
|
||||
error: AT&T x86 assembly syntax used
|
||||
--> $DIR/asm_syntax.rs:22:9
|
||||
--> $DIR/asm_syntax.rs:24:9
|
||||
|
|
||||
LL | asm!("", options(nostack, att_syntax));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
#![warn(clippy::bool_assert_comparison)]
|
||||
|
||||
macro_rules! a {
|
||||
() => {
|
||||
true
|
||||
};
|
||||
}
|
||||
macro_rules! b {
|
||||
() => {
|
||||
true
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!("a".len(), 1);
|
||||
assert_eq!("a".is_empty(), false);
|
||||
assert_eq!("".is_empty(), true);
|
||||
assert_eq!(true, "".is_empty());
|
||||
assert_eq!(a!(), b!());
|
||||
assert_eq!(a!(), "".is_empty());
|
||||
assert_eq!("".is_empty(), b!());
|
||||
|
||||
assert_ne!("a".len(), 1);
|
||||
assert_ne!("a".is_empty(), false);
|
||||
assert_ne!("".is_empty(), true);
|
||||
assert_ne!(true, "".is_empty());
|
||||
assert_ne!(a!(), b!());
|
||||
assert_ne!(a!(), "".is_empty());
|
||||
assert_ne!("".is_empty(), b!());
|
||||
|
||||
debug_assert_eq!("a".len(), 1);
|
||||
debug_assert_eq!("a".is_empty(), false);
|
||||
debug_assert_eq!("".is_empty(), true);
|
||||
debug_assert_eq!(true, "".is_empty());
|
||||
debug_assert_eq!(a!(), b!());
|
||||
debug_assert_eq!(a!(), "".is_empty());
|
||||
debug_assert_eq!("".is_empty(), b!());
|
||||
|
||||
debug_assert_ne!("a".len(), 1);
|
||||
debug_assert_ne!("a".is_empty(), false);
|
||||
debug_assert_ne!("".is_empty(), true);
|
||||
debug_assert_ne!(true, "".is_empty());
|
||||
debug_assert_ne!(a!(), b!());
|
||||
debug_assert_ne!(a!(), "".is_empty());
|
||||
debug_assert_ne!("".is_empty(), b!());
|
||||
|
||||
// assert with error messages
|
||||
assert_eq!("a".len(), 1, "tadam {}", 1);
|
||||
assert_eq!("a".len(), 1, "tadam {}", true);
|
||||
assert_eq!("a".is_empty(), false, "tadam {}", 1);
|
||||
assert_eq!("a".is_empty(), false, "tadam {}", true);
|
||||
assert_eq!(false, "a".is_empty(), "tadam {}", true);
|
||||
|
||||
debug_assert_eq!("a".len(), 1, "tadam {}", 1);
|
||||
debug_assert_eq!("a".len(), 1, "tadam {}", true);
|
||||
debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
|
||||
debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
|
||||
debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
error: used `assert_eq!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:16:5
|
||||
|
|
||||
LL | assert_eq!("a".is_empty(), false);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|
||||
|
|
||||
= note: `-D clippy::bool-assert-comparison` implied by `-D warnings`
|
||||
|
||||
error: used `assert_eq!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:17:5
|
||||
|
|
||||
LL | assert_eq!("".is_empty(), true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|
||||
|
||||
error: used `assert_eq!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:18:5
|
||||
|
|
||||
LL | assert_eq!(true, "".is_empty());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|
||||
|
||||
error: used `assert_ne!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:24:5
|
||||
|
|
||||
LL | assert_ne!("a".is_empty(), false);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|
||||
|
||||
error: used `assert_ne!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:25:5
|
||||
|
|
||||
LL | assert_ne!("".is_empty(), true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|
||||
|
||||
error: used `assert_ne!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:26:5
|
||||
|
|
||||
LL | assert_ne!(true, "".is_empty());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|
||||
|
||||
error: used `debug_assert_eq!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:32:5
|
||||
|
|
||||
LL | debug_assert_eq!("a".is_empty(), false);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
|
||||
|
||||
error: used `debug_assert_eq!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:33:5
|
||||
|
|
||||
LL | debug_assert_eq!("".is_empty(), true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
|
||||
|
||||
error: used `debug_assert_eq!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:34:5
|
||||
|
|
||||
LL | debug_assert_eq!(true, "".is_empty());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
|
||||
|
||||
error: used `debug_assert_ne!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:40:5
|
||||
|
|
||||
LL | debug_assert_ne!("a".is_empty(), false);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
|
||||
|
||||
error: used `debug_assert_ne!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:41:5
|
||||
|
|
||||
LL | debug_assert_ne!("".is_empty(), true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
|
||||
|
||||
error: used `debug_assert_ne!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:42:5
|
||||
|
|
||||
LL | debug_assert_ne!(true, "".is_empty());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
|
||||
|
||||
error: used `assert_eq!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:50:5
|
||||
|
|
||||
LL | assert_eq!("a".is_empty(), false, "tadam {}", 1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|
||||
|
||||
error: used `assert_eq!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:51:5
|
||||
|
|
||||
LL | assert_eq!("a".is_empty(), false, "tadam {}", true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|
||||
|
||||
error: used `assert_eq!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:52:5
|
||||
|
|
||||
LL | assert_eq!(false, "a".is_empty(), "tadam {}", true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
|
||||
|
||||
error: used `debug_assert_eq!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:56:5
|
||||
|
|
||||
LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
|
||||
|
||||
error: used `debug_assert_eq!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:57:5
|
||||
|
|
||||
LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
|
||||
|
||||
error: used `debug_assert_eq!` with a literal bool
|
||||
--> $DIR/bool_assert_comparison.rs:58:5
|
||||
|
|
||||
LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
|
||||
|
||||
error: aborting due to 18 previous errors
|
||||
|
|
@ -206,4 +206,18 @@ fn fp_test() {
|
|||
}
|
||||
}
|
||||
|
||||
fn fp_if_let_issue7054() {
|
||||
// This shouldn't trigger the lint
|
||||
let string;
|
||||
let _x = if let true = true {
|
||||
""
|
||||
} else if true {
|
||||
string = "x".to_owned();
|
||||
&string
|
||||
} else {
|
||||
string = "y".to_owned();
|
||||
&string
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -100,4 +100,15 @@ fn check_if_same_than_else_mask() {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::vec_init_then_push)]
|
||||
fn pf_local_with_inferred_type_issue7053() {
|
||||
if true {
|
||||
let mut v = Vec::new();
|
||||
v.push(0);
|
||||
} else {
|
||||
let mut v = Vec::new();
|
||||
v.push("");
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue