]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #8030 - WaffleLapkin:ignore_trait_assoc_types_type_complexity, r=llogiq
authorbors <bors@rust-lang.org>
Wed, 8 Dec 2021 11:54:03 +0000 (11:54 +0000)
committerbors <bors@rust-lang.org>
Wed, 8 Dec 2021 11:54:03 +0000 (11:54 +0000)
Ignore associated types in traits when considering type complexity

changelog: Ignore associated types in traits when checking ``[`type_complexity`]`` lint.

fixes #1013

128 files changed:
.github/ISSUE_TEMPLATE/blank_issue.md [deleted file]
.github/ISSUE_TEMPLATE/blank_issue.yml [new file with mode: 0644]
.github/ISSUE_TEMPLATE/bug_report.md [deleted file]
.github/ISSUE_TEMPLATE/bug_report.yml [new file with mode: 0644]
.github/ISSUE_TEMPLATE/false_negative.md [deleted file]
.github/ISSUE_TEMPLATE/false_negative.yml [new file with mode: 0644]
.github/ISSUE_TEMPLATE/false_positive.md [deleted file]
.github/ISSUE_TEMPLATE/false_positive.yml [new file with mode: 0644]
.github/ISSUE_TEMPLATE/ice.md [deleted file]
.github/ISSUE_TEMPLATE/ice.yml [new file with mode: 0644]
.github/ISSUE_TEMPLATE/new_lint.md [deleted file]
.github/ISSUE_TEMPLATE/new_lint.yml [new file with mode: 0644]
CHANGELOG.md
Cargo.toml
clippy_lints/Cargo.toml
clippy_lints/src/assertions_on_constants.rs
clippy_lints/src/bytecount.rs
clippy_lints/src/collapsible_match.rs
clippy_lints/src/dbg_macro.rs
clippy_lints/src/derivable_impls.rs
clippy_lints/src/derive.rs
clippy_lints/src/floating_point_arithmetic.rs
clippy_lints/src/if_then_some_else_none.rs
clippy_lints/src/implicit_saturating_sub.rs
clippy_lints/src/lib.register_all.rs
clippy_lints/src/lib.register_complexity.rs
clippy_lints/src/lib.register_lints.rs
clippy_lints/src/lib.register_nursery.rs
clippy_lints/src/lib.register_pedantic.rs
clippy_lints/src/lib.register_style.rs
clippy_lints/src/lib.register_suspicious.rs
clippy_lints/src/lib.rs
clippy_lints/src/lifetimes.rs
clippy_lints/src/loops/manual_flatten.rs
clippy_lints/src/loops/never_loop.rs
clippy_lints/src/manual_map.rs
clippy_lints/src/map_clone.rs
clippy_lints/src/map_unit_fn.rs
clippy_lints/src/matches.rs
clippy_lints/src/methods/bind_instead_of_map.rs
clippy_lints/src/methods/filter_map.rs
clippy_lints/src/methods/iter_cloned_collect.rs
clippy_lints/src/methods/mod.rs
clippy_lints/src/methods/option_as_ref_deref.rs
clippy_lints/src/methods/search_is_some.rs
clippy_lints/src/methods/single_char_pattern.rs
clippy_lints/src/methods/unnecessary_fold.rs
clippy_lints/src/methods/utils.rs
clippy_lints/src/needless_bool.rs
clippy_lints/src/needless_late_init.rs [new file with mode: 0644]
clippy_lints/src/no_effect.rs
clippy_lints/src/non_copy_const.rs
clippy_lints/src/non_send_fields_in_send_ty.rs
clippy_lints/src/nonstandard_macro_braces.rs
clippy_lints/src/option_if_let_else.rs
clippy_lints/src/question_mark.rs
clippy_lints/src/same_name_method.rs
clippy_lints/src/self_named_constructors.rs
clippy_lints/src/strings.rs
clippy_lints/src/strlen_on_c_strings.rs
clippy_lints/src/unicode.rs
clippy_lints/src/unused_self.rs
clippy_lints/src/utils/internal_lints.rs
clippy_lints/src/utils/internal_lints/metadata_collector.rs
clippy_lints/src/write.rs
clippy_utils/Cargo.toml
clippy_utils/src/lib.rs
clippy_utils/src/paths.rs
clippy_utils/src/sugg.rs
clippy_utils/src/visitors.rs
rust-toolchain
tests/ui-toml/strict_non_send_fields_in_send_ty/test.stderr
tests/ui/crashes/ice-6250.stderr
tests/ui/crate_level_checks/no_std_swap.rs
tests/ui/crate_level_checks/no_std_swap.stderr
tests/ui/iter_cloned_collect.fixed
tests/ui/iter_cloned_collect.rs
tests/ui/iter_cloned_collect.stderr
tests/ui/let_if_seq.rs
tests/ui/let_if_seq.stderr
tests/ui/min_max.rs
tests/ui/min_max.stderr
tests/ui/needless_bool/fixable.fixed
tests/ui/needless_bool/fixable.rs
tests/ui/needless_bool/fixable.stderr
tests/ui/needless_late_init.rs [new file with mode: 0644]
tests/ui/needless_late_init.stderr [new file with mode: 0644]
tests/ui/needless_late_init_fixable.fixed [new file with mode: 0644]
tests/ui/needless_late_init_fixable.rs [new file with mode: 0644]
tests/ui/needless_late_init_fixable.stderr [new file with mode: 0644]
tests/ui/non_send_fields_in_send_ty.rs
tests/ui/non_send_fields_in_send_ty.stderr
tests/ui/option_env_unwrap.rs
tests/ui/option_env_unwrap.stderr
tests/ui/option_filter_map.fixed
tests/ui/option_filter_map.rs
tests/ui/option_filter_map.stderr
tests/ui/option_if_let_else.fixed
tests/ui/option_if_let_else.rs
tests/ui/option_if_let_else.stderr
tests/ui/pattern_type_mismatch/mutability.rs
tests/ui/question_mark.fixed
tests/ui/question_mark.rs
tests/ui/redundant_closure_call_late.rs
tests/ui/redundant_closure_call_late.stderr
tests/ui/redundant_else.rs
tests/ui/same_name_method.stderr
tests/ui/search_is_some.rs
tests/ui/search_is_some.stderr
tests/ui/search_is_some_fixable.fixed [deleted file]
tests/ui/search_is_some_fixable.rs [deleted file]
tests/ui/search_is_some_fixable.stderr [deleted file]
tests/ui/search_is_some_fixable_none.fixed [new file with mode: 0644]
tests/ui/search_is_some_fixable_none.rs [new file with mode: 0644]
tests/ui/search_is_some_fixable_none.stderr [new file with mode: 0644]
tests/ui/search_is_some_fixable_some.fixed [new file with mode: 0644]
tests/ui/search_is_some_fixable_some.rs [new file with mode: 0644]
tests/ui/search_is_some_fixable_some.stderr [new file with mode: 0644]
tests/ui/single_char_pattern.fixed
tests/ui/single_char_pattern.rs
tests/ui/single_char_pattern.stderr
tests/ui/strlen_on_c_strings.fixed [new file with mode: 0644]
tests/ui/strlen_on_c_strings.rs
tests/ui/strlen_on_c_strings.stderr
tests/ui/unicode.rs
tests/ui/unicode.stderr
triagebot.toml
util/gh-pages/index.html

diff --git a/.github/ISSUE_TEMPLATE/blank_issue.md b/.github/ISSUE_TEMPLATE/blank_issue.md
deleted file mode 100644 (file)
index 866303a..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
----
-name: Blank Issue
-about: Create a blank issue.
----
-
-
-<!--
-Additional labels can be added to this issue by including the following command
-(without the space after the @ symbol):
-
-@ rustbot label +<label>
-
-Common labels for this issue type are:
-* C-an-interesting-project
-* C-enhancement
-* C-question
-* C-tracking-issue
--->
diff --git a/.github/ISSUE_TEMPLATE/blank_issue.yml b/.github/ISSUE_TEMPLATE/blank_issue.yml
new file mode 100644 (file)
index 0000000..d610e8c
--- /dev/null
@@ -0,0 +1,44 @@
+name: Blank Issue
+description: Create a blank issue.
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for filing an issue!
+  - type: textarea
+    id: problem
+    attributes:
+      label: Description
+      description: >
+        Please provide a discription of the issue, along with any information
+        you feel relevant to replicate it.
+    validations:
+      required: true
+  - type: textarea
+    id: version
+    attributes:
+      label: Version
+      description: "Rust version (`rustc -Vv`)"
+      placeholder: |
+        rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+        binary: rustc
+        commit-hash: f455e46eae1a227d735091091144601b467e1565
+        commit-date: 2020-06-20
+        host: x86_64-unknown-linux-gnu
+        release: 1.46.0-nightly
+        LLVM version: 10.0
+      render: text
+  - type: textarea
+    id: labels
+    attributes:
+      label: Additional Labels
+      description: >
+        Additional labels can be added to this issue by including the following
+        command
+      placeholder: |
+        @rustbot label +<label>
+
+        Common labels for this issue type are:
+        * C-an-interesting-project
+        * C-enhancement
+        * C-question
+        * C-tracking-issue
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644 (file)
index 119a498..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
----
-name: Bug Report
-about: Create a bug report for Clippy
-labels: C-bug
----
-<!--
-Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
-along with any information you feel relevant to replicating the bug.
--->
-
-I tried this code:
-
-```rust
-<code>
-```
-
-I expected to see this happen: *explanation*
-
-Instead, this happened: *explanation*
-
-### Meta
-
-**Rust version (`rustc -Vv`):**
-
-```
-rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-binary: rustc
-commit-hash: f455e46eae1a227d735091091144601b467e1565
-commit-date: 2020-06-20
-host: x86_64-unknown-linux-gnu
-release: 1.46.0-nightly
-LLVM version: 10.0
-```
-
-<!--
-Additional labels can be added to this issue by including the following command
-(without the space after the @ symbol):
-
-@ rustbot label +<label>
-
-Common labels for this issue type are:
-* `I-suggestion-causes-error`
--->
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644 (file)
index 0000000..68877ef
--- /dev/null
@@ -0,0 +1,57 @@
+name: Bug Report
+description: Create a bug report for Clippy
+labels: ["C-bug"]
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for filing a bug report! 🐛
+  - type: textarea
+    id: problem
+    attributes:
+      label: Summary
+      description: >
+        Please provide a short summary of the bug, along with any information
+        you feel relevant to replicate the bug.
+    validations:
+      required: true
+  - type: textarea
+    id: reproducer
+    attributes:
+      label: Reproducer
+      description: Please provide the code and steps to repoduce the bug
+      value: |
+        I tried this code:
+
+        ```rust
+        <code>
+        ```
+
+        I expected to see this happen:
+
+        Instead, this happened:
+  - type: textarea
+    id: version
+    attributes:
+      label: Version
+      description: "Rust version (`rustc -Vv`)"
+      placeholder: |
+        rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+        binary: rustc
+        commit-hash: f455e46eae1a227d735091091144601b467e1565
+        commit-date: 2020-06-20
+        host: x86_64-unknown-linux-gnu
+        release: 1.46.0-nightly
+        LLVM version: 10.0
+      render: text
+  - type: textarea
+    id: labels
+    attributes:
+      label: Additional Labels
+      description: >
+        Additional labels can be added to this issue by including the following
+        command
+      placeholder: |
+        @rustbot label +<label>
+
+        Common labels for this issue type are:
+        * `I-suggestion-causes-error`
diff --git a/.github/ISSUE_TEMPLATE/false_negative.md b/.github/ISSUE_TEMPLATE/false_negative.md
deleted file mode 100644 (file)
index d9ea2db..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
----
-name: Bug Report (False Negative)
-about: Create a bug report about missing warnings from a lint
-labels: C-bug, I-false-negative
----
-<!--
-Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
-along with any information you feel relevant to replicating the bug.
--->
-Lint name:
-
-
-I tried this code:
-
-```rust
-<code>
-```
-
-I expected to see this happen: *explanation*
-
-Instead, this happened: *explanation*
-
-### Meta
-
-**Rust version (`rustc -Vv`):**
-
-```
-rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-binary: rustc
-commit-hash: f455e46eae1a227d735091091144601b467e1565
-commit-date: 2020-06-20
-host: x86_64-unknown-linux-gnu
-release: 1.46.0-nightly
-LLVM version: 10.0
-```
diff --git a/.github/ISSUE_TEMPLATE/false_negative.yml b/.github/ISSUE_TEMPLATE/false_negative.yml
new file mode 100644 (file)
index 0000000..9357ccc
--- /dev/null
@@ -0,0 +1,50 @@
+name: Bug Report (False Negative)
+description: Create a bug report about missing warnings from a lint
+labels: ["C-bug", "I-false-negative"]
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for filing a bug report! 🐛
+  - type: textarea
+    id: problem
+    attributes:
+      label: Summary
+      description: >
+        Please provide a short summary of the bug, along with any information
+        you feel relevant to replicate the bug.
+    validations:
+      required: true
+  - type: input
+    id: lint-name
+    attributes:
+      label: Lint Name
+      description: Please provide the lint name.
+  - type: textarea
+    id: reproducer
+    attributes:
+      label: Reproducer
+      description: Please provide the code and steps to repoduce the bug
+      value: |
+        I tried this code:
+
+        ```rust
+        <code>
+        ```
+
+        I expected to see this happen:
+
+        Instead, this happened:
+  - type: textarea
+    id: version
+    attributes:
+      label: Version
+      description: "Rust version (`rustc -Vv`)"
+      placeholder: |
+        rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+        binary: rustc
+        commit-hash: f455e46eae1a227d735091091144601b467e1565
+        commit-date: 2020-06-20
+        host: x86_64-unknown-linux-gnu
+        release: 1.46.0-nightly
+        LLVM version: 10.0
+      render: text
diff --git a/.github/ISSUE_TEMPLATE/false_positive.md b/.github/ISSUE_TEMPLATE/false_positive.md
deleted file mode 100644 (file)
index 82158e0..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
----
-name: Bug Report (False Positive)
-about: Create a bug report about a wrongly emitted lint warning
-labels: C-bug, I-false-positive
----
-<!--
-Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
-along with any information you feel relevant to replicating the bug.
--->
-Lint name:
-
-
-I tried this code:
-
-```rust
-<code>
-```
-
-I expected to see this happen: *explanation*
-
-Instead, this happened: *explanation*
-
-### Meta
-
-**Rust version (`rustc -Vv`):**
-```
-rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-binary: rustc
-commit-hash: f455e46eae1a227d735091091144601b467e1565
-commit-date: 2020-06-20
-host: x86_64-unknown-linux-gnu
-release: 1.46.0-nightly
-LLVM version: 10.0
-```
-
-<!--
-Additional labels can be added to this issue by including the following command
-(without the space after the @ symbol):
-
-@ rustbot label +<label>
-
-Common labels for this issue type are:
-* I-suggestion-causes-error
--->
diff --git a/.github/ISSUE_TEMPLATE/false_positive.yml b/.github/ISSUE_TEMPLATE/false_positive.yml
new file mode 100644 (file)
index 0000000..b7dd400
--- /dev/null
@@ -0,0 +1,68 @@
+name: Bug Report (False Positive)
+description: Create a bug report about a wrongly emitted lint warning
+labels: ["C-bug", "I-false-positive"]
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for filing a bug report! 🐛
+  - type: textarea
+    id: problem
+    attributes:
+      label: Summary
+      description: >
+        Please provide a short summary of the bug, along with any information
+        you feel relevant to replicate the bug.
+    validations:
+      required: true
+  - type: input
+    id: lint-name
+    attributes:
+      label: Lint Name
+      description: Please provide the lint name.
+  - type: textarea
+    id: reproducer
+    attributes:
+      label: Reproducer
+      description: >
+        Please provide the code and steps to repoduce the bug together with the
+        output from Clippy.
+      value: |
+        I tried this code:
+
+        ```rust
+        <code>
+        ```
+
+        I saw this happen:
+
+        ```
+        <output>
+        ```
+
+        I expected to see this happen:
+  - type: textarea
+    id: version
+    attributes:
+      label: Version
+      description: "Rust version (`rustc -Vv`)"
+      placeholder: |
+        rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+        binary: rustc
+        commit-hash: f455e46eae1a227d735091091144601b467e1565
+        commit-date: 2020-06-20
+        host: x86_64-unknown-linux-gnu
+        release: 1.46.0-nightly
+        LLVM version: 10.0
+      render: text
+  - type: textarea
+    id: labels
+    attributes:
+      label: Additional Labels
+      description: >
+        Additional labels can be added to this issue by including the following
+        command
+      placeholder: |
+        @rustbot label +<label>
+
+        Common labels for this issue type are:
+        * `I-suggestion-causes-error`
diff --git a/.github/ISSUE_TEMPLATE/ice.md b/.github/ISSUE_TEMPLATE/ice.md
deleted file mode 100644 (file)
index 6c1bed6..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
----
-name: Internal Compiler Error
-about: Create a report for an internal compiler error in Clippy.
-labels: C-bug, I-ICE
----
-<!--
-Thank you for finding an Internal Compiler Error! 🧊  If possible, try to provide
-a minimal verifiable example. You can read "Rust Bug Minimization Patterns" for
-how to create smaller examples.
-
-http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/
-
--->
-
-### Code
-
-```rust
-<code>
-```
-
-### Meta
-
-**Rust version (`rustc -Vv`):**
-```
-rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-binary: rustc
-commit-hash: f455e46eae1a227d735091091144601b467e1565
-commit-date: 2020-06-20
-host: x86_64-unknown-linux-gnu
-release: 1.46.0-nightly
-LLVM version: 10.0
-```
-
-### Error output
-
-```
-<output>
-```
-
-<!--
-Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in your
-environment. E.g. `RUST_BACKTRACE=1 cargo clippy`.
--->
-<details><summary>Backtrace</summary>
-  <p>
-  
-  ```
-  <backtrace>
-  ```
-  
-  </p>
-</details>
diff --git a/.github/ISSUE_TEMPLATE/ice.yml b/.github/ISSUE_TEMPLATE/ice.yml
new file mode 100644 (file)
index 0000000..2a5b8b3
--- /dev/null
@@ -0,0 +1,48 @@
+name: Internal Compiler Error
+description: Create a report for an internal compiler error (ICE) in Clippy.
+labels: ["C-bug", "I-ICE"]
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for finding an Internal Compiler Error! 🧊
+  - type: textarea
+    id: problem
+    attributes:
+      label: Summary
+      description: |
+        If possible, try to provide a minimal verifiable example. You can read ["Rust Bug Minimization Patterns"][mve] for how to create smaller examples. Otherwise, provide the crate where the ICE occured.
+
+        [mve]: http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/
+    validations:
+      required: true
+  - type: textarea
+    id: version
+    attributes:
+      label: Version
+      description: "Rust version (`rustc -Vv`)"
+      placeholder: |
+        rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+        binary: rustc
+        commit-hash: f455e46eae1a227d735091091144601b467e1565
+        commit-date: 2020-06-20
+        host: x86_64-unknown-linux-gnu
+        release: 1.46.0-nightly
+        LLVM version: 10.0
+      render: text
+  - type: textarea
+    id: error
+    attributes:
+      label: Error output
+      description: >
+        Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in
+        your environment. E.g. `RUST_BACKTRACE=1 cargo clippy`.
+      value: |
+        <details><summary>Backtrace</summary>
+          <p>
+
+          ```
+          <backtrace>
+          ```
+
+          </p>
+        </details>
diff --git a/.github/ISSUE_TEMPLATE/new_lint.md b/.github/ISSUE_TEMPLATE/new_lint.md
deleted file mode 100644 (file)
index 2216bb9..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
----
-name: New lint suggestion
-about: Suggest a new Clippy lint.
-labels: A-lint
----
-
-### What it does
-
-*What does this lint do?*
-
-### Categories (optional)
-
-- Kind: *See <https://github.com/rust-lang/rust-clippy/blob/master/README.md#clippy> for list of lint kinds*
-
-*What is the advantage of the recommended code over the original code*
-
-For example:
-- Remove bounds check inserted by ...
-- Remove the need to duplicate/store ...
-- Remove typo ...
-
-### Drawbacks
-
-None.
-
-### Example
-
-```rust
-<code>
-```
-
-Could be written as:
-
-```rust
-<code>
-```
diff --git a/.github/ISSUE_TEMPLATE/new_lint.yml b/.github/ISSUE_TEMPLATE/new_lint.yml
new file mode 100644 (file)
index 0000000..0b43d8d
--- /dev/null
@@ -0,0 +1,71 @@
+name: New lint suggestion
+description: Suggest a new Clippy lint.
+labels: ["A-lint"]
+body:
+  - type: markdown
+    attributes:
+      value: Thank you for your lint idea!
+  - type: textarea
+    id: what
+    attributes:
+      label: What it does
+      description: What does this lint do?
+    validations:
+      required: true
+  - type: input
+    id: lint-name
+    attributes:
+      label: Lint Name
+      description: Please provide the lint name.
+  - type: dropdown
+    id: category
+    attributes:
+      label: Category
+      description: >
+        What category should this lint go into? If you're unsure you can select
+        multiple categories. You can find a category description in the
+        `README`.
+      multiple: true
+      options:
+        - correctness
+        - suspicious
+        - style
+        - complexity
+        - perf
+        - pedantic
+        - restriction
+        - cargo
+  - type: textarea
+    id: advantage
+    attributes:
+      label: Advantage
+      description: >
+        What is the advantage of the recommended code over the original code?
+      placeholder: |
+        - Remove bounds check inserted by ...
+        - Remove the need to duplicate/store ...
+        - Remove typo ...
+  - type: textarea
+    id: drawbacks
+    attributes:
+      label: Drawbacks
+      description: What might be possible drawbacks of such a lint?
+  - type: textarea
+    id: example
+    attributes:
+      label: Example
+      description: >
+        Include a short example showing when the lint should trigger together
+        with the improved code.
+      value: |
+        ```rust
+        <code>
+        ```
+
+        Could be written as:
+
+        ```rust
+        <code>
+        ```
+    validations:
+      required: true
index 401557b3eacd73fe36e46c5346bfb8f9dd066ba5..157ea0c963afc5d1d8748ebe79ce250c2343914d 100644 (file)
@@ -3033,6 +3033,7 @@ Released 2018-09-13
 [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
 [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
 [`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
+[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
 [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
 [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
 [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
index fdafdffca6438df238d0618a3f76034d84513ce3..8661a86775887285bc953c4ec7afbbb68a313830 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.58"
+version = "0.1.59"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
index 281480b8d94914c2d1835a1fb7d2d1fb62620b18..0661c2803864c15545b050f15a2ddb9a3fd43634 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_lints"
-version = "0.1.58"
+version = "0.1.59"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
index 521fc84ee9c375ae3d74416afd0b8a60bb1fc86c..b7f414742f1566bd69731f71ce323c6364c7b947 100644 (file)
@@ -2,7 +2,7 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::higher;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call};
+use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call, peel_blocks};
 use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
@@ -122,15 +122,7 @@ fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)
         if let ExprKind::Unary(UnOp::Not, expr) = cond.kind;
         // bind the first argument of the `assert!` macro
         if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr);
-        // block
-        if let ExprKind::Block(block, _) = then.kind;
-        if block.stmts.is_empty();
-        if let Some(block_expr) = &block.expr;
-        // inner block is optional. unwrap it if it exists, or use the expression as is otherwise.
-        if let Some(begin_panic_call) = match block_expr.kind {
-            ExprKind::Block(inner_block, _) => &inner_block.expr,
-            _ => &block.expr,
-        };
+        let begin_panic_call = peel_blocks(then);
         // function call
         if let Some(arg) = match_panic_call(cx, begin_panic_call);
         // bind the second argument of the `assert!` macro if it exists
index afb317421d0773aed78d0fdef2dc27089a6949d1..657c1f88c7db5055bd8714fe0329d9a9a9bde694 100644 (file)
@@ -2,7 +2,7 @@
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::match_type;
 use clippy_utils::visitors::is_local_used;
-use clippy_utils::{path_to_local_id, paths, peel_ref_operators, remove_blocks, strip_pat_refs};
+use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
@@ -55,7 +55,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
                        cx.typeck_results().expr_ty(filter_recv).peel_refs(),
                        &paths::SLICE_ITER);
             let operand_is_arg = |expr| {
-                let expr = peel_ref_operators(cx, remove_blocks(expr));
+                let expr = peel_ref_operators(cx, peel_blocks(expr));
                 path_to_local_id(expr, arg_id)
             };
             let needle = if operand_is_arg(l) {
index 626f9971f01e7d678cbc69c22a00d09841109cf7..c71e9f10f79ec95c331e71df7a20b254458c6721 100644 (file)
@@ -1,10 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::IfLetOrMatch;
 use clippy_utils::visitors::is_local_used;
-use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_ref_operators, SpanlessEq};
+use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq};
 use if_chain::if_chain;
 use rustc_hir::LangItem::OptionNone;
-use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind};
+use rustc_hir::{Arm, Expr, Guard, HirId, Pat, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::{MultiSpan, Span};
@@ -75,7 +75,7 @@ fn check_arm<'tcx>(
     outer_guard: Option<&'tcx Guard<'tcx>>,
     outer_else_body: Option<&'tcx Expr<'tcx>>,
 ) {
-    let inner_expr = strip_singleton_blocks(outer_then_body);
+    let inner_expr = peel_blocks_with_stmt(outer_then_body);
     if_chain! {
         if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr);
         if let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner {
@@ -138,20 +138,6 @@ fn check_arm<'tcx>(
     }
 }
 
-fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
-    while let ExprKind::Block(block, _) = expr.kind {
-        match (block.stmts, block.expr) {
-            ([stmt], None) => match stmt.kind {
-                StmtKind::Expr(e) | StmtKind::Semi(e) => expr = e,
-                _ => break,
-            },
-            ([], Some(e)) => expr = e,
-            _ => break,
-        }
-    }
-    expr
-}
-
 /// A "wild-like" arm has a wild (`_`) or `None` pattern and no guard. Such arms can be "collapsed"
 /// into a single wild arm without any significant loss in semantics or readability.
 fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
index 1aae4c81c73c0b3e85da93aa9651fb3672fb45c5..5a0b60fdfbc01924de5a09c3dc36d62a6b8592c1 100644 (file)
     /// `dbg!` macro is intended as a debugging tool. It
     /// should not be in version control.
     ///
+    /// ### Known problems
+    /// * The lint level is unaffected by crate attributes. The level can still
+    ///   be set for functions, modules and other items. To change the level for
+    ///   the entire crate, please use command line flags. More information and a
+    ///   configuration example can be found in [clippy#6610].
+    ///
+    /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
+    ///
     /// ### Example
     /// ```rust,ignore
     /// // Bad
index d0fab2b48fb074beba10b3e7b864d64eaab74549..eccb18982f30da8a74e4f8a8626a94ef7c5e3189 100644 (file)
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{is_automatically_derived, is_default_equivalent, remove_blocks};
+use clippy_utils::{is_automatically_derived, is_default_equivalent, peel_blocks};
 use rustc_hir::{
     def::{DefKind, Res},
     Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
@@ -95,7 +95,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
                         }
                     }
                 }
-                let should_emit = match remove_blocks(func_expr).kind {
+                let should_emit = match peel_blocks(func_expr).kind {
                     ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)),
                     ExprKind::Call(callee, args)
                         if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)),
index 4b232a26e5d0f69eae482e72c590ac0e2d834fb7..097cb65f56e4e8c63b760cb56bfb32aa30a59a0e 100644 (file)
@@ -3,7 +3,6 @@
 use clippy_utils::ty::{implements_trait, is_copy};
 use clippy_utils::{get_trait_def_id, is_automatically_derived, is_lint_allowed, match_def_path};
 use if_chain::if_chain;
-use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor};
 use rustc_hir::{
     BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety,
@@ -347,11 +346,6 @@ fn check_unsafe_derive_deserialize<'tcx>(
     trait_ref: &TraitRef<'_>,
     ty: Ty<'tcx>,
 ) {
-    fn item_from_def_id<'tcx>(cx: &LateContext<'tcx>, def_id: DefId) -> &'tcx Item<'tcx> {
-        let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
-        cx.tcx.hir().expect_item(hir_id)
-    }
-
     fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
         let mut visitor = UnsafeVisitor { cx, has_unsafe: false };
         walk_item(&mut visitor, item);
@@ -367,7 +361,7 @@ fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
         if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
         if cx.tcx.inherent_impls(def.did)
             .iter()
-            .map(|imp_did| item_from_def_id(cx, *imp_did))
+            .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local()))
             .any(|imp| has_unsafe(cx, imp));
         then {
             span_lint_and_help(
index 3df511ea8e780e20496d0636a3af3435b5fa001e..2de2bfc040b5306c6d62b7d137dd023823834d1d 100644 (file)
@@ -4,7 +4,7 @@
 };
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher;
-use clippy_utils::{eq_expr_value, get_parent_expr, in_constant, numeric_literal, sugg};
+use clippy_utils::{eq_expr_value, get_parent_expr, in_constant, numeric_literal, peel_blocks, sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
@@ -546,13 +546,9 @@ fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a
 
 fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
     if_chain! {
-        if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
-        if let ExprKind::Block(block, _) = then.kind;
-        if block.stmts.is_empty();
-        if let Some(if_body_expr) = block.expr;
-        if let Some(ExprKind::Block(else_block, _)) = r#else.map(|el| &el.kind);
-        if else_block.stmts.is_empty();
-        if let Some(else_body_expr) = else_block.expr;
+        if let Some(higher::If { cond, then, r#else: Some(r#else) }) = higher::If::hir(expr);
+        let if_body_expr = peel_blocks(then);
+        let else_body_expr = peel_blocks(r#else);
         if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr);
         then {
             let positive_abs_sugg = (
index 30d222bd7d27de9643047c51f282d05357aadba1..16e5c5ca603db450474ce1697cf4e110041bbc6e 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs};
+use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks};
 use if_chain::if_chain;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
@@ -77,10 +77,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
             if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind;
             if let ExprKind::Path(ref then_call_qpath) = then_call.kind;
             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 qpath) = els_expr.kind;
+            if let ExprKind::Path(ref qpath) = peel_blocks(els).kind;
             if is_lang_ctor(cx, qpath, OptionNone);
             if !stmts_contains_early_return(then_block.stmts);
             then {
index 4088c54623d36a3dc4187589ada0bf02e4345053..26a196aab5972ca577430009543fa05fa9d53251 100644 (file)
@@ -1,10 +1,9 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher;
-use clippy_utils::SpanlessEq;
+use clippy_utils::{higher, peel_blocks_with_stmt, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::{lang_items::LangItem, BinOpKind, Expr, ExprKind, QPath, StmtKind};
+use rustc_hir::{lang_items::LangItem, BinOpKind, Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
@@ -52,13 +51,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
             // Ensure that the binary operator is >, != and <
             if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node;
 
-            // Check if the true condition block has only one statement
-            if let ExprKind::Block(block, _) = then.kind;
-            if block.stmts.len() == 1 && block.expr.is_none();
-
             // Check if assign operation is done
-            if let StmtKind::Semi(e) = block.stmts[0].kind;
-            if let Some(target) = subtracts_one(cx, e);
+            if let Some(target) = subtracts_one(cx, then);
 
             // Extracting out the variable name
             if let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind;
@@ -138,8 +132,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
     }
 }
 
-fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &Expr<'a>) -> Option<&'a Expr<'a>> {
-    match expr.kind {
+fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
+    match peel_blocks_with_stmt(expr).kind {
         ExprKind::AssignOp(ref op1, target, value) => {
             if_chain! {
                 if BinOpKind::Sub == op1.node;
index 612240135ac637ecb9b6c937d3f747ac508345ad..85ca2cf308d96eced5b3d73f5ecd4ad2ab1ae679 100644 (file)
     LintId::of(methods::MANUAL_SPLIT_ONCE),
     LintId::of(methods::MANUAL_STR_REPEAT),
     LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
+    LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_IDENTITY),
     LintId::of(methods::NEEDLESS_SPLITN),
     LintId::of(methods::NEW_RET_NO_SELF),
     LintId::of(needless_bool::BOOL_COMPARISON),
     LintId::of(needless_bool::NEEDLESS_BOOL),
     LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
+    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
     LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
     LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
     LintId::of(needless_update::NEEDLESS_UPDATE),
     LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
     LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
     LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
-    LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
     LintId::of(octal_escapes::OCTAL_ESCAPES),
     LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
     LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
index dcc95cf6c074d357cc1c52467c8936edaba1dcca..a21ddf73a115e6a22a54dbfc2867abdb43cf0228 100644 (file)
@@ -41,6 +41,7 @@
     LintId::of(methods::MANUAL_FILTER_MAP),
     LintId::of(methods::MANUAL_FIND_MAP),
     LintId::of(methods::MANUAL_SPLIT_ONCE),
+    LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_IDENTITY),
     LintId::of(methods::NEEDLESS_SPLITN),
     LintId::of(methods::OPTION_AS_REF_DEREF),
index 19c35a5e5f401b713cb6cfbfb1e7ea9927be3aba..bb159e50373c22d156db89368fcbcb88b5607013 100644 (file)
     needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
     needless_continue::NEEDLESS_CONTINUE,
     needless_for_each::NEEDLESS_FOR_EACH,
+    needless_late_init::NEEDLESS_LATE_INIT,
     needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF,
     needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
     needless_question_mark::NEEDLESS_QUESTION_MARK,
index 59182fd8175d404bfbd9e68b070445419c7fa1bf..e3cf067001834a3b16b00d480584e2771e37ed2e 100644 (file)
@@ -18,6 +18,7 @@
     LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
     LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
     LintId::of(mutex_atomic::MUTEX_INTEGER),
+    LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
     LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
     LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
     LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
index 5ee7799f9c519647dd2e44282120d58b3beaf794..70a4a624378909dd1040fbe9997927c07fa6d8c2 100644 (file)
@@ -63,7 +63,6 @@
     LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
     LintId::of(methods::IMPLICIT_CLONE),
     LintId::of(methods::INEFFICIENT_TO_STRING),
-    LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_UNWRAP_OR),
     LintId::of(misc::FLOAT_CMP),
     LintId::of(misc::USED_UNDERSCORE_BINDING),
index f336441ea8420b380caf9335d7e3d39600c37628..ea87e7e7a73687bb37b9385396c6c4ebc5c2b3a2 100644 (file)
@@ -82,6 +82,7 @@
     LintId::of(misc_early::REDUNDANT_PATTERN),
     LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
     LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
+    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
     LintId::of(neg_multiply::NEG_MULTIPLY),
     LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
     LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
index 414bfc42fdfcdabe6fecf7e701c1474b61ccf774..10f8ae4b7f7fca8583cde6a976955579a3efc4e1 100644 (file)
@@ -15,7 +15,6 @@
     LintId::of(loops::MUT_RANGE_BOUND),
     LintId::of(methods::SUSPICIOUS_MAP),
     LintId::of(mut_key::MUTABLE_KEY_TYPE),
-    LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
     LintId::of(octal_escapes::OCTAL_ESCAPES),
     LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
     LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
index 3dafdf8f0d5e2f29beb2f536dc46cca2c56aa6ef..bd9710ec40750e23b89f2aed7911bbc00df09778 100644 (file)
@@ -299,6 +299,7 @@ macro_rules! declare_clippy_lint {
 mod needless_borrowed_ref;
 mod needless_continue;
 mod needless_for_each;
+mod needless_late_init;
 mod needless_option_as_deref;
 mod needless_pass_by_value;
 mod needless_question_mark;
@@ -851,6 +852,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || Box::new(format_args::FormatArgs));
     store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray));
     store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
+    store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
index fad3343d128510aca02466f18960f65a251932b2..0e2b78609c2c13c90f3ab022f477a183a75d6171 100644 (file)
@@ -29,7 +29,7 @@
     ///
     /// ### Known problems
     /// - We bail out if the function has a `where` clause where lifetimes
-    /// are mentioned due to potenial false positives.
+    /// are mentioned due to potential false positives.
     /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the
     /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.
     ///
index 5b6e27085d580986bd892ac46552dc6b2d253e11..d276c901059974a54c38d51601f26c3a6d070638 100644 (file)
@@ -3,11 +3,11 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher;
 use clippy_utils::visitors::is_local_used;
-use clippy_utils::{is_lang_ctor, path_to_local_id};
+use clippy_utils::{is_lang_ctor, path_to_local_id, peel_blocks_with_stmt};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionSome, ResultOk};
-use rustc_hir::{Expr, ExprKind, Pat, PatKind, StmtKind};
+use rustc_hir::{Expr, Pat, PatKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_span::source_map::Span;
@@ -21,71 +21,55 @@ pub(super) fn check<'tcx>(
     body: &'tcx Expr<'_>,
     span: Span,
 ) {
-    if let ExprKind::Block(block, _) = body.kind {
-        // Ensure the `if let` statement is the only expression or statement in the for-loop
-        let inner_expr = if block.stmts.len() == 1 && block.expr.is_none() {
-            let match_stmt = &block.stmts[0];
-            if let StmtKind::Semi(inner_expr) = match_stmt.kind {
-                Some(inner_expr)
-            } else {
-                None
-            }
-        } else if block.stmts.is_empty() {
-            block.expr
-        } else {
-            None
-        };
+    let inner_expr = peel_blocks_with_stmt(body);
+    if_chain! {
+        if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None })
+            = higher::IfLet::hir(cx, inner_expr);
+        // Ensure match_expr in `if let` statement is the same as the pat from the for-loop
+        if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
+        if path_to_local_id(let_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(ref qpath, _, _) = let_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;
+        // Ensure expr in `if let` is not used afterwards
+        if !is_local_used(cx, if_then, pat_hir_id);
+        then {
+            let if_let_type = if some_ctor { "Some" } else { "Ok" };
+            // Prepare the error message
+            let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type);
 
-        if_chain! {
-            if let Some(inner_expr) = inner_expr;
-            if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None })
-                = higher::IfLet::hir(cx, inner_expr);
-            // Ensure match_expr in `if let` statement is the same as the pat from the for-loop
-            if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
-            if path_to_local_id(let_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(ref qpath, _, _) = let_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;
-            // Ensure epxr in `if let` is not used afterwards
-            if !is_local_used(cx, if_then, pat_hir_id);
-            then {
-                let if_let_type = if some_ctor { "Some" } else { "Ok" };
-                // Prepare the error message
-                let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type);
-
-                // Prepare the help message
-                let mut applicability = Applicability::MaybeIncorrect;
-                let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
-                let copied = match cx.typeck_results().expr_ty(let_expr).kind() {
-                    ty::Ref(_, inner, _) => match inner.kind() {
-                        ty::Ref(..) => ".copied()",
-                        _ => ""
-                    }
+            // Prepare the help message
+            let mut applicability = Applicability::MaybeIncorrect;
+            let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
+            let copied = match cx.typeck_results().expr_ty(let_expr).kind() {
+                ty::Ref(_, inner, _) => match inner.kind() {
+                    ty::Ref(..) => ".copied()",
                     _ => ""
-                };
+                }
+                _ => ""
+            };
 
-                span_lint_and_then(
-                    cx,
-                    MANUAL_FLATTEN,
-                    span,
-                    &msg,
-                    |diag| {
-                        let sugg = format!("{}{}.flatten()", arg_snippet, copied);
-                        diag.span_suggestion(
-                            arg.span,
-                            "try",
-                            sugg,
-                            Applicability::MaybeIncorrect,
-                        );
-                        diag.span_help(
-                            inner_expr.span,
-                            "...and remove the `if let` statement in the for loop",
-                        );
-                    }
-                );
-            }
+            span_lint_and_then(
+                cx,
+                MANUAL_FLATTEN,
+                span,
+                &msg,
+                |diag| {
+                    let sugg = format!("{}{}.flatten()", arg_snippet, copied);
+                    diag.span_suggestion(
+                        arg.span,
+                        "try",
+                        sugg,
+                        Applicability::MaybeIncorrect,
+                    );
+                    diag.span_help(
+                        inner_expr.span,
+                        "...and remove the `if let` statement in the for loop",
+                    );
+                }
+            );
         }
     }
 }
index 86b7d6d989acc75fd6fc3dde238947886588aa51..68ffcd1abfb18240ce55c9bc8be3ddafa92af3fd 100644 (file)
@@ -92,9 +92,7 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult
 }
 
 fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult {
-    let stmts = block.stmts.iter().map(stmt_to_expr);
-    let expr = once(block.expr);
-    let mut iter = stmts.chain(expr).flatten();
+    let mut iter = block.stmts.iter().filter_map(stmt_to_expr).chain(block.expr);
     never_loop_expr_seq(&mut iter, main_loop_id)
 }
 
index 4d8ad566e6b1d84f6131560c29ead691ec55b99b..34a70ca76c6a2a81df2d5ab1e4b1ec35c4a673ed 100644 (file)
@@ -5,7 +5,7 @@
 use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function};
 use clippy_utils::{
     can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id,
-    peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
+    peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
 };
 use rustc_ast::util::parser::PREC_POSTFIX;
 use rustc_errors::Applicability;
@@ -307,16 +307,5 @@ fn get_some_expr(
 
 // Checks for the `None` value.
 fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
-    match expr.kind {
-        ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
-        ExprKind::Block(
-            Block {
-                stmts: [],
-                expr: Some(expr),
-                ..
-            },
-            _,
-        ) => is_none_expr(cx, expr),
-        _ => false,
-    }
+    matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
 }
index c2b78e21861d7d38b09eb4cf4e12254c58a0ce6d..174c7da28d3ec1f246deac0c3dd52274aed9e1f9 100644 (file)
@@ -1,8 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_trait_method;
-use clippy_utils::remove_blocks;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
+use clippy_utils::{is_trait_method, peel_blocks};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -60,7 +59,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
             if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind;
             then {
                 let closure_body = cx.tcx.hir().body(body_id);
-                let closure_expr = remove_blocks(&closure_body.value);
+                let closure_expr = peel_blocks(&closure_body.value);
                 match closure_body.params[0].pat.kind {
                     hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
                         hir::BindingAnnotation::Unannotated, .., name, None
index a84de3e079b3918b9c0bb98d91f84d2862278e46..58c686d95b3f91caed5e57466b807e33ca9ab8a7 100644 (file)
@@ -190,7 +190,7 @@ fn unit_closure<'tcx>(
 /// Anything else will return `a`.
 fn let_binding_name(cx: &LateContext<'_>, var_arg: &hir::Expr<'_>) -> String {
     match &var_arg.kind {
-        hir::ExprKind::Field(_, _) => snippet(cx, var_arg.span, "_").replace(".", "_"),
+        hir::ExprKind::Field(_, _) => snippet(cx, var_arg.span, "_").replace('.', "_"),
         hir::ExprKind::Path(_) => format!("_{}", snippet(cx, var_arg.span, "")),
         _ => "a".to_string(),
     }
index 08c23ca6d32bb00c58c68c44fdf260911c12362a..385232f4d7138c1ce68bbfb895024fa0cbcf37d0 100644 (file)
@@ -9,7 +9,7 @@
 use clippy_utils::visitors::is_local_used;
 use clippy_utils::{
     get_parent_expr, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs,
-    path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks,
+    path_to_local, path_to_local_id, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns,
     strip_pat_refs,
 };
 use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
@@ -659,7 +659,7 @@ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
                 QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
             if args.len() == 1;
             if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
-            let body = remove_blocks(arms[0].body);
+            let body = peel_blocks(arms[0].body);
             if path_to_local_id(body, arg);
 
             then {
@@ -724,7 +724,7 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp
             return;
         }
         let els = arms[1].body;
-        let els = if is_unit_expr(remove_blocks(els)) {
+        let els = if is_unit_expr(peel_blocks(els)) {
             None
         } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
             if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
@@ -1482,7 +1482,7 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A
 
     let matched_vars = ex.span;
     let bind_names = arms[0].pat.span;
-    let match_body = remove_blocks(arms[0].body);
+    let match_body = peel_blocks(arms[0].body);
     let mut snippet_body = if match_body.span.from_expansion() {
         Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string()
     } else {
@@ -1679,7 +1679,7 @@ fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotat
         if is_lang_ctor(cx, qpath, OptionSome);
         if let PatKind::Binding(rb, .., ident, _) = first_pat.kind;
         if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
-        if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind;
+        if let ExprKind::Call(e, args) = peel_blocks(arm.body).kind;
         if let ExprKind::Path(ref some_path) = e.kind;
         if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1;
         if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
index b7690cf9222cc8be9162ee59b8cff744b686a0e4..150bafc0f5db228f216127d3382c0be03e8ec353 100644 (file)
@@ -1,7 +1,7 @@
 use super::{contains_return, BIND_INSTEAD_OF_MAP};
 use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_with_macro_callsite};
-use clippy_utils::{remove_blocks, visitors::find_all_ret_expressions};
+use clippy_utils::{peel_blocks, visitors::find_all_ret_expressions};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -152,7 +152,7 @@ fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg:
         match arg.kind {
             hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => {
                 let closure_body = cx.tcx.hir().body(body_id);
-                let closure_expr = remove_blocks(&closure_body.value);
+                let closure_expr = peel_blocks(&closure_body.value);
 
                 if Self::lint_closure_autofixable(cx, expr, recv, closure_expr, closure_args_span) {
                     true
index c96c817bb8bdeb30806747c56ffcf5287de7e5d5..6d8733c08b43012b5870de5e382410baa783cbfb 100644 (file)
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{indent_of, reindent_multiline, snippet};
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_trait_method, path_to_local_id, remove_blocks, SpanlessEq};
+use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, SpanlessEq};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -25,7 +25,7 @@ fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy
         },
         hir::ExprKind::Closure(_, _, c, _, _) => {
             let body = cx.tcx.hir().body(*c);
-            let closure_expr = remove_blocks(&body.value);
+            let closure_expr = peel_blocks(&body.value);
             let arg_id = body.params[0].pat.hir_id;
             match closure_expr.kind {
                 hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, _, args, _) => {
index dd4ef6e4b58ee72ede7975985c9c76bfa0cb1cec..30d56113c6c102af9e81b9a9c86857bb491406f3 100644 (file)
@@ -9,7 +9,7 @@
 
 use super::ITER_CLONED_COLLECT;
 
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &'tcx hir::Expr<'_>) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, method_name: &str, expr: &hir::Expr<'_>, recv: &'tcx hir::Expr<'_>) {
     if_chain! {
         if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec);
         if let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv));
@@ -20,8 +20,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &'
                 cx,
                 ITER_CLONED_COLLECT,
                 to_replace,
-                "called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \
-                more readable",
+                &format!("called `iter().{}().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \
+                more readable", method_name),
                 "try",
                 ".to_vec()".to_string(),
                 Applicability::MachineApplicable,
index 6ab5015ca789098044e945571e279adcae317780..58ec221353567c7421e9794de3f3090c94b4befc 100644 (file)
     /// ```
     #[clippy::version = "1.31.0"]
     pub MAP_FLATTEN,
-    pedantic,
+    complexity,
     "using combinations of `flatten` and `map` which can usually be written as a single method call"
 }
 
@@ -2027,7 +2027,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
             return;
         }
         let name = impl_item.ident.name.as_str();
-        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
+        let parent = cx.tcx.hir().get_parent_did(impl_item.hir_id());
         let item = cx.tcx.hir().expect_item(parent);
         let self_ty = cx.tcx.type_of(item.def_id);
 
@@ -2204,7 +2204,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
             ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
             ("collect", []) => match method_call!(recv) {
-                Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2),
+                Some((name @ ("cloned" | "copied"), [recv2], _)) => {
+                    iter_cloned_collect::check(cx, name, expr, recv2);
+                },
                 Some(("map", [m_recv, m_arg], _)) => {
                     map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv);
                 },
index d3f40d2620805bde24f1ba3d39865a7d826ccb28..fa74a8f3dc363b8a7df525d59c5df03650153f4f 100644 (file)
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, remove_blocks};
+use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -53,7 +53,7 @@ pub(super) fn check<'tcx>(
             }),
         hir::ExprKind::Closure(_, _, body_id, _, _) => {
             let closure_body = cx.tcx.hir().body(body_id);
-            let closure_expr = remove_blocks(&closure_body.value);
+            let closure_expr = peel_blocks(&closure_body.value);
 
             match &closure_expr.kind {
                 hir::ExprKind::MethodCall(_, _, args, _) => {
index 0f2e58d8983faf7188b261d9109cf0a245f4da6a..5ed4ba94884e26b627874b7253608150eca9a0a9 100644 (file)
@@ -1,5 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::sugg::deref_closure_args;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{is_trait_method, strip_pat_refs};
 use if_chain::if_chain;
@@ -37,6 +38,7 @@ pub(super) fn check<'tcx>(
         if search_snippet.lines().count() <= 1 {
             // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()`
             // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()`
+            let mut applicability = Applicability::MachineApplicable;
             let any_search_snippet = if_chain! {
                 if search_method == "find";
                 if let hir::ExprKind::Closure(_, _, body_id, ..) = search_arg.kind;
@@ -45,9 +47,15 @@ pub(super) fn check<'tcx>(
                 then {
                     if let hir::PatKind::Ref(..) = closure_arg.pat.kind {
                         Some(search_snippet.replacen('&', "", 1))
-                    } else if let PatKind::Binding(_, _, ident, _) = strip_pat_refs(closure_arg.pat).kind {
-                        let name = &*ident.name.as_str();
-                        Some(search_snippet.replace(&format!("*{}", name), name))
+                    } else if let PatKind::Binding(..) = strip_pat_refs(closure_arg.pat).kind {
+                        // `find()` provides a reference to the item, but `any` does not,
+                        // so we should fix item usages for suggestion
+                        if let Some(closure_sugg) = deref_closure_args(cx, search_arg) {
+                            applicability = closure_sugg.applicability;
+                            Some(closure_sugg.suggestion)
+                        } else {
+                            Some(search_snippet.to_string())
+                        }
                     } else {
                         None
                     }
@@ -67,7 +75,7 @@ pub(super) fn check<'tcx>(
                         "any({})",
                         any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
                     ),
-                    Applicability::MachineApplicable,
+                    applicability,
                 );
             } else {
                 let iter = snippet(cx, search_recv.span, "..");
@@ -82,7 +90,7 @@ pub(super) fn check<'tcx>(
                         iter,
                         any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
                     ),
-                    Applicability::MachineApplicable,
+                    applicability,
                 );
             }
         } else {
index d313a3db479de6d51c5797985804403272cf32f0..bf9006c690621031f1056c6ee0ac8efb45577d39 100644 (file)
@@ -9,18 +9,21 @@
 
 use super::SINGLE_CHAR_PATTERN;
 
-const PATTERN_METHODS: [(&str, usize); 19] = [
+const PATTERN_METHODS: [(&str, usize); 24] = [
     ("contains", 1),
     ("starts_with", 1),
     ("ends_with", 1),
     ("find", 1),
     ("rfind", 1),
     ("split", 1),
+    ("split_inclusive", 1),
     ("rsplit", 1),
     ("split_terminator", 1),
     ("rsplit_terminator", 1),
     ("splitn", 2),
     ("rsplitn", 2),
+    ("split_once", 1),
+    ("rsplit_once", 1),
     ("matches", 1),
     ("rmatches", 1),
     ("match_indices", 1),
@@ -29,6 +32,8 @@
     ("strip_suffix", 1),
     ("trim_start_matches", 1),
     ("trim_end_matches", 1),
+    ("replace", 1),
+    ("replacen", 1),
 ];
 
 /// lint for length-1 `str`s for methods in `PATTERN_METHODS`
index 4c4034437da51601337af3aec9c36ca57a237ab0..47a811996085e59d95fa98475a33a0a367a9f1d8 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{is_trait_method, path_to_local_id, remove_blocks, strip_pat_refs};
+use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, strip_pat_refs};
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_errors::Applicability;
@@ -31,7 +31,7 @@ fn check_fold_with_op(
             // Extract the body of the closure passed to fold
             if let hir::ExprKind::Closure(_, _, body_id, _, _) = acc.kind;
             let closure_body = cx.tcx.hir().body(body_id);
-            let closure_expr = remove_blocks(&closure_body.value);
+            let closure_expr = peel_blocks(&closure_body.value);
 
             // Check if the closure body is of the form `acc <op> some_expr(x)`
             if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind;
index ba2ce73a1165eeb1ef3707d91b1d373ce78b3fe5..11ad881ee7b95bf1d29b58b89111e85a58b575b7 100644 (file)
@@ -66,7 +66,13 @@ pub(super) fn get_hint_if_single_char_arg(
                 // for regular string: "a"
                 &snip[1..(snip.len() - 1)]
             };
-            let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
+
+            let hint = format!("'{}'", match ch {
+                "'" => "\\'" ,
+                r"\" => "\\\\",
+                _ => ch,
+            });
+
             Some(hint)
         } else {
             None
index 3709f3948c2b5f541295eaeaa074d8595aed9256..d391fbecf82e14ea9b2cffde2267704bd6f8ecf4 100644 (file)
@@ -6,10 +6,10 @@
 use clippy_utils::higher;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{is_else_clause, is_expn_of};
+use clippy_utils::{get_parent_node, is_else_clause, is_expn_of, peel_blocks, peel_blocks_with_stmt};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
+use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Node, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Spanned;
 
 declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]);
 
+fn condition_needs_parentheses(e: &Expr<'_>) -> bool {
+    let mut inner = e;
+    while let ExprKind::Binary(_, i, _)
+    | ExprKind::Call(i, _)
+    | ExprKind::Cast(i, _)
+    | ExprKind::Type(i, _)
+    | ExprKind::Index(i, _) = inner.kind
+    {
+        if matches!(
+            i.kind,
+            ExprKind::Block(..)
+                | ExprKind::ConstBlock(..)
+                | ExprKind::If(..)
+                | ExprKind::Loop(..)
+                | ExprKind::Match(..)
+        ) {
+            return true;
+        }
+        inner = i;
+    }
+    false
+}
+
+fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
+    matches!(
+        get_parent_node(cx.tcx, id),
+        Some(Node::Stmt(..) | Node::Block(Block { stmts: &[], .. }))
+    )
+}
+
 impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         use self::Expression::{Bool, RetBool};
@@ -99,6 +129,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                     snip = snip.blockify();
                 }
 
+                if condition_needs_parentheses(cond) && is_parent_stmt(cx, e.hir_id) {
+                    snip = snip.maybe_par();
+                }
+
                 span_lint_and_sugg(
                     cx,
                     NEEDLESS_BOOL,
@@ -109,8 +143,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                     applicability,
                 );
             };
-            if let ExprKind::Block(then, _) = then.kind {
-                match (fetch_bool_block(then), fetch_bool_expr(r#else)) {
+            if let Some((a, b)) = fetch_bool_block(then).and_then(|a| Some((a, fetch_bool_block(r#else)?))) {
+                match (a, b) {
                     (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => {
                         span_lint(
                             cx,
@@ -133,8 +167,6 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                     (Bool(false), Bool(true)) => reduce(false, true),
                     _ => (),
                 }
-            } else {
-                panic!("IfExpr `then` node is not an `ExprKind::Block`");
             }
         }
     }
@@ -237,8 +269,6 @@ fn check_comparison<'a, 'tcx>(
     right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
     no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &str)>,
 ) {
-    use self::Expression::{Bool, Other};
-
     if let ExprKind::Binary(op, left_side, right_side) = e.kind {
         let (l_ty, r_ty) = (
             cx.typeck_results().expr_ty(left_side),
@@ -270,19 +300,19 @@ fn check_comparison<'a, 'tcx>(
             }
 
             match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
-                (Bool(true), Other) => left_true.map_or((), |(h, m)| {
+                (Some(true), None) => left_true.map_or((), |(h, m)| {
                     suggest_bool_comparison(cx, e, right_side, applicability, m, h);
                 }),
-                (Other, Bool(true)) => right_true.map_or((), |(h, m)| {
+                (None, Some(true)) => right_true.map_or((), |(h, m)| {
                     suggest_bool_comparison(cx, e, left_side, applicability, m, h);
                 }),
-                (Bool(false), Other) => left_false.map_or((), |(h, m)| {
+                (Some(false), None) => left_false.map_or((), |(h, m)| {
                     suggest_bool_comparison(cx, e, right_side, applicability, m, h);
                 }),
-                (Other, Bool(false)) => right_false.map_or((), |(h, m)| {
+                (None, Some(false)) => right_false.map_or((), |(h, m)| {
                     suggest_bool_comparison(cx, e, left_side, applicability, m, h);
                 }),
-                (Other, Other) => no_literal.map_or((), |(h, m)| {
+                (None, None) => no_literal.map_or((), |(h, m)| {
                     let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability);
                     let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability);
                     span_lint_and_sugg(
@@ -331,41 +361,20 @@ fn suggest_bool_comparison<'a, 'tcx>(
 enum Expression {
     Bool(bool),
     RetBool(bool),
-    Other,
 }
 
-fn fetch_bool_block(block: &Block<'_>) -> Expression {
-    match (&*block.stmts, block.expr.as_ref()) {
-        (&[], Some(e)) => fetch_bool_expr(&**e),
-        (&[ref e], None) => {
-            if let StmtKind::Semi(e) = e.kind {
-                if let ExprKind::Ret(_) = e.kind {
-                    fetch_bool_expr(e)
-                } else {
-                    Expression::Other
-                }
-            } else {
-                Expression::Other
-            }
-        },
-        _ => Expression::Other,
+fn fetch_bool_block(expr: &Expr<'_>) -> Option<Expression> {
+    match peel_blocks_with_stmt(expr).kind {
+        ExprKind::Ret(Some(ret)) => Some(Expression::RetBool(fetch_bool_expr(ret)?)),
+        _ => Some(Expression::Bool(fetch_bool_expr(expr)?)),
     }
 }
 
-fn fetch_bool_expr(expr: &Expr<'_>) -> Expression {
-    match expr.kind {
-        ExprKind::Block(block, _) => fetch_bool_block(block),
-        ExprKind::Lit(ref lit_ptr) => {
-            if let LitKind::Bool(value) = lit_ptr.node {
-                Expression::Bool(value)
-            } else {
-                Expression::Other
-            }
-        },
-        ExprKind::Ret(Some(expr)) => match fetch_bool_expr(expr) {
-            Expression::Bool(value) => Expression::RetBool(value),
-            _ => Expression::Other,
-        },
-        _ => Expression::Other,
+fn fetch_bool_expr(expr: &Expr<'_>) -> Option<bool> {
+    if let ExprKind::Lit(ref lit_ptr) = peel_blocks(expr).kind {
+        if let LitKind::Bool(value) = lit_ptr.node {
+            return Some(value);
+        }
     }
+    None
 }
diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs
new file mode 100644 (file)
index 0000000..e0522f3
--- /dev/null
@@ -0,0 +1,349 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::path_to_local;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::visitors::{expr_visitor, is_local_used};
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for late initializations that can be replaced by a `let` statement
+    /// with an initializer.
+    ///
+    /// ### Why is this bad?
+    /// Assigning in the `let` statement is less repetitive.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let a;
+    /// a = 1;
+    ///
+    /// let b;
+    /// match 3 {
+    ///     0 => b = "zero",
+    ///     1 => b = "one",
+    ///     _ => b = "many",
+    /// }
+    ///
+    /// let c;
+    /// if true {
+    ///     c = 1;
+    /// } else {
+    ///     c = -1;
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let a = 1;
+    ///
+    /// let b = match 3 {
+    ///     0 => "zero",
+    ///     1 => "one",
+    ///     _ => "many",
+    /// };
+    ///
+    /// let c = if true {
+    ///     1
+    /// } else {
+    ///     -1
+    /// };
+    /// ```
+    #[clippy::version = "1.58.0"]
+    pub NEEDLESS_LATE_INIT,
+    style,
+    "late initializations that can be replaced by a `let` statement with an initializer"
+}
+declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]);
+
+fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool {
+    let mut seen = false;
+    expr_visitor(cx, |expr| {
+        if let ExprKind::Assign(..) = expr.kind {
+            seen = true;
+        }
+
+        !seen
+    })
+    .visit_stmt(stmt);
+
+    seen
+}
+
+#[derive(Debug)]
+struct LocalAssign {
+    lhs_id: HirId,
+    lhs_span: Span,
+    rhs_span: Span,
+    span: Span,
+}
+
+impl LocalAssign {
+    fn from_expr(expr: &Expr<'_>, span: Span) -> Option<Self> {
+        if let ExprKind::Assign(lhs, rhs, _) = expr.kind {
+            if lhs.span.from_expansion() {
+                return None;
+            }
+
+            Some(Self {
+                lhs_id: path_to_local(lhs)?,
+                lhs_span: lhs.span,
+                rhs_span: rhs.span.source_callsite(),
+                span,
+            })
+        } else {
+            None
+        }
+    }
+
+    fn new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, binding_id: HirId) -> Option<LocalAssign> {
+        let assign = match expr.kind {
+            ExprKind::Block(Block { expr: Some(expr), .. }, _) => Self::from_expr(expr, expr.span),
+            ExprKind::Block(block, _) => {
+                if_chain! {
+                    if let Some((last, other_stmts)) = block.stmts.split_last();
+                    if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = last.kind;
+
+                    let assign = Self::from_expr(expr, last.span)?;
+
+                    // avoid visiting if not needed
+                    if assign.lhs_id == binding_id;
+                    if other_stmts.iter().all(|stmt| !contains_assign_expr(cx, stmt));
+
+                    then {
+                        Some(assign)
+                    } else {
+                        None
+                    }
+                }
+            },
+            ExprKind::Assign(..) => Self::from_expr(expr, expr.span),
+            _ => None,
+        }?;
+
+        if assign.lhs_id == binding_id {
+            Some(assign)
+        } else {
+            None
+        }
+    }
+}
+
+fn assignment_suggestions<'tcx>(
+    cx: &LateContext<'tcx>,
+    binding_id: HirId,
+    exprs: impl IntoIterator<Item = &'tcx Expr<'tcx>>,
+) -> Option<(Applicability, Vec<(Span, String)>)> {
+    let mut assignments = Vec::new();
+
+    for expr in exprs {
+        let ty = cx.typeck_results().expr_ty(expr);
+
+        if ty.is_never() {
+            continue;
+        }
+        if !ty.is_unit() {
+            return None;
+        }
+
+        let assign = LocalAssign::new(cx, expr, binding_id)?;
+
+        assignments.push(assign);
+    }
+
+    let suggestions = assignments
+        .into_iter()
+        .map(|assignment| Some((assignment.span, snippet_opt(cx, assignment.rhs_span)?)))
+        .collect::<Option<Vec<(Span, String)>>>()?;
+
+    let applicability = if suggestions.len() > 1 {
+        // multiple suggestions don't work with rustfix in multipart_suggest
+        // https://github.com/rust-lang/rustfix/issues/141
+        Applicability::Unspecified
+    } else {
+        Applicability::MachineApplicable
+    };
+    Some((applicability, suggestions))
+}
+
+struct Usage<'tcx> {
+    stmt: &'tcx Stmt<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    needs_semi: bool,
+}
+
+fn first_usage<'tcx>(
+    cx: &LateContext<'tcx>,
+    binding_id: HirId,
+    local_stmt_id: HirId,
+    block: &'tcx Block<'tcx>,
+) -> Option<Usage<'tcx>> {
+    block
+        .stmts
+        .iter()
+        .skip_while(|stmt| stmt.hir_id != local_stmt_id)
+        .skip(1)
+        .find(|&stmt| is_local_used(cx, stmt, binding_id))
+        .and_then(|stmt| match stmt.kind {
+            StmtKind::Expr(expr) => Some(Usage {
+                stmt,
+                expr,
+                needs_semi: true,
+            }),
+            StmtKind::Semi(expr) => Some(Usage {
+                stmt,
+                expr,
+                needs_semi: false,
+            }),
+            _ => None,
+        })
+}
+
+fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &Local<'_>) -> Option<String> {
+    let span = local.span.with_hi(match local.ty {
+        // let <pat>: <ty>;
+        // ~~~~~~~~~~~~~~~
+        Some(ty) => ty.span.hi(),
+        // let <pat>;
+        // ~~~~~~~~~
+        None => local.pat.span.hi(),
+    });
+
+    snippet_opt(cx, span)
+}
+
+fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    local: &'tcx Local<'tcx>,
+    local_stmt: &'tcx Stmt<'tcx>,
+    block: &'tcx Block<'tcx>,
+    binding_id: HirId,
+) -> Option<()> {
+    let usage = first_usage(cx, binding_id, local_stmt.hir_id, block)?;
+    let binding_name = cx.tcx.hir().opt_name(binding_id)?;
+    let let_snippet = local_snippet_without_semicolon(cx, local)?;
+
+    match usage.expr.kind {
+        ExprKind::Assign(..) => {
+            let assign = LocalAssign::new(cx, usage.expr, binding_id)?;
+
+            span_lint_and_then(
+                cx,
+                NEEDLESS_LATE_INIT,
+                local_stmt.span,
+                "unneeded late initalization",
+                |diag| {
+                    diag.tool_only_span_suggestion(
+                        local_stmt.span,
+                        "remove the local",
+                        String::new(),
+                        Applicability::MachineApplicable,
+                    );
+
+                    diag.span_suggestion(
+                        assign.lhs_span,
+                        &format!("declare `{}` here", binding_name),
+                        let_snippet,
+                        Applicability::MachineApplicable,
+                    );
+                },
+            );
+        },
+        ExprKind::If(_, then_expr, Some(else_expr)) => {
+            let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?;
+
+            span_lint_and_then(
+                cx,
+                NEEDLESS_LATE_INIT,
+                local_stmt.span,
+                "unneeded late initalization",
+                |diag| {
+                    diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
+
+                    diag.span_suggestion_verbose(
+                        usage.stmt.span.shrink_to_lo(),
+                        &format!("declare `{}` here", binding_name),
+                        format!("{} = ", let_snippet),
+                        applicability,
+                    );
+
+                    diag.multipart_suggestion("remove the assignments from the branches", suggestions, applicability);
+
+                    if usage.needs_semi {
+                        diag.span_suggestion(
+                            usage.stmt.span.shrink_to_hi(),
+                            "add a semicolon after the `if` expression",
+                            ";".to_string(),
+                            applicability,
+                        );
+                    }
+                },
+            );
+        },
+        ExprKind::Match(_, arms, MatchSource::Normal) => {
+            let (applicability, suggestions) = assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?;
+
+            span_lint_and_then(
+                cx,
+                NEEDLESS_LATE_INIT,
+                local_stmt.span,
+                "unneeded late initalization",
+                |diag| {
+                    diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
+
+                    diag.span_suggestion_verbose(
+                        usage.stmt.span.shrink_to_lo(),
+                        &format!("declare `{}` here", binding_name),
+                        format!("{} = ", let_snippet),
+                        applicability,
+                    );
+
+                    diag.multipart_suggestion(
+                        "remove the assignments from the `match` arms",
+                        suggestions,
+                        applicability,
+                    );
+
+                    if usage.needs_semi {
+                        diag.span_suggestion(
+                            usage.stmt.span.shrink_to_hi(),
+                            "add a semicolon after the `match` expression",
+                            ";".to_string(),
+                            applicability,
+                        );
+                    }
+                },
+            );
+        },
+        _ => {},
+    };
+
+    Some(())
+}
+
+impl LateLintPass<'tcx> for NeedlessLateInit {
+    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+        let mut parents = cx.tcx.hir().parent_iter(local.hir_id);
+
+        if_chain! {
+            if let Local {
+                init: None,
+                pat: &Pat {
+                    kind: PatKind::Binding(_, binding_id, _, None),
+                    ..
+                },
+                source: LocalSource::Normal,
+                ..
+            } = local;
+            if let Some((_, Node::Stmt(local_stmt))) = parents.next();
+            if let Some((_, Node::Block(block))) = parents.next();
+
+            then {
+                check(cx, local, local_stmt, block, binding_id);
+            }
+        }
+    }
+}
index cd54de3ee4b75cf567b0668ea3adb7e6776ec92f..9d5babc5de840d69e3f5941cc0459a5420fd2ed2 100644 (file)
@@ -1,5 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
 use clippy_utils::is_lint_allowed;
+use clippy_utils::peel_blocks;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::has_drop;
 use rustc_errors::Applicability;
@@ -114,7 +115,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     if expr.span.from_expansion() {
         return false;
     }
-    match expr.kind {
+    match peel_blocks(expr).kind {
         ExprKind::Lit(..) | ExprKind::Closure(..) => true,
         ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)),
         ExprKind::Index(a, b) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b),
@@ -150,9 +151,6 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
                 false
             }
         },
-        ExprKind::Block(block, _) => {
-            block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| has_no_effect(cx, expr))
-        },
         _ => false,
     }
 }
@@ -164,12 +162,13 @@ fn check_unnecessary_operation(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
         if !&reduced.iter().any(|e| e.span.from_expansion());
         then {
             if let ExprKind::Index(..) = &expr.kind {
-                let snippet;
-                if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) {
-                    snippet = format!("assert!({}.len() > {});", &arr, &func);
+                let snippet = if let (Some(arr), Some(func)) =
+                    (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span))
+                {
+                    format!("assert!({}.len() > {});", &arr, &func)
                 } else {
                     return;
-                }
+                };
                 span_lint_hir_and_then(
                     cx,
                     UNNECESSARY_OPERATION,
index df82775d50713d0c891c74cfe67e408c5baa9140..074ba9e92ba4dfcea5e8921afbe8139302efe7f1 100644 (file)
@@ -281,8 +281,8 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitIt
 
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
         if let ImplItemKind::Const(hir_ty, body_id) = &impl_item.kind {
-            let item_hir_id = cx.tcx.hir().get_parent_node(impl_item.hir_id());
-            let item = cx.tcx.hir().expect_item(item_hir_id);
+            let item_def_id = cx.tcx.hir().get_parent_did(impl_item.hir_id());
+            let item = cx.tcx.hir().expect_item(item_def_id);
 
             match &item.kind {
                 ItemKind::Impl(Impl {
index 9c07488bfe6ef39cd5bfcec0263719377de7f98a..203f03d3603c8058a19821b16519c2e16199978c 100644 (file)
@@ -1,35 +1,40 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::is_lint_allowed;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::{implements_trait, is_copy};
+use clippy_utils::{is_lint_allowed, match_def_path, paths};
 use rustc_ast::ImplPolarity;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{FieldDef, Item, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Warns about fields in struct implementing `Send` that are neither `Send` nor `Copy`.
+    /// This lint warns about a `Send` implementation for a type that
+    /// contains fields that are not safe to be sent across threads.
+    /// It tries to detect fields that can cause a soundness issue
+    /// when sent to another thread (e.g., `Rc`) while allowing `!Send` fields
+    /// that are expected to exist in a `Send` type, such as raw pointers.
     ///
     /// ### Why is this bad?
-    /// Sending the struct to another thread will transfer the ownership to
-    /// the new thread by dropping in the current thread during the transfer.
-    /// This causes soundness issues for non-`Send` fields, as they are also
-    /// dropped and might not be set up to handle this.
+    /// Sending the struct to another thread effectively sends all of its fields,
+    /// and the fields that do not implement `Send` can lead to soundness bugs
+    /// such as data races when accessed in a thread
+    /// that is different from the thread that created it.
     ///
     /// See:
     /// * [*The Rustonomicon* about *Send and Sync*](https://doc.rust-lang.org/nomicon/send-and-sync.html)
     /// * [The documentation of `Send`](https://doc.rust-lang.org/std/marker/trait.Send.html)
     ///
     /// ### Known Problems
-    /// Data structures that contain raw pointers may cause false positives.
-    /// They are sometimes safe to be sent across threads but do not implement
-    /// the `Send` trait. This lint has a heuristic to filter out basic cases
-    /// such as `Vec<*const T>`, but it's not perfect. Feel free to create an
-    /// issue if you have a suggestion on how this heuristic can be improved.
+    /// This lint relies on heuristics to distinguish types that are actually
+    /// unsafe to be sent across threads and `!Send` types that are expected to
+    /// exist in  `Send` type. Its rule can filter out basic cases such as
+    /// `Vec<*const T>`, but it's not perfect. Feel free to create an issue if
+    /// you have a suggestion on how this heuristic can be improved.
     ///
     /// ### Example
     /// ```rust,ignore
@@ -45,8 +50,8 @@
     /// or specify correct bounds on generic type parameters (`T: Send`).
     #[clippy::version = "1.57.0"]
     pub NON_SEND_FIELDS_IN_SEND_TY,
-    suspicious,
-    "there is field that does not implement `Send` in a `Send` struct"
+    nursery,
+    "there is a field that is not safe to be sent to another thread in a `Send` struct"
 }
 
 #[derive(Copy, Clone)]
@@ -77,6 +82,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         // single `AdtDef` may have multiple `Send` impls due to generic
         // parameters, and the lint is much easier to implement in this way.
         if_chain! {
+            if !in_external_macro(cx.tcx.sess, item.span);
             if let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send);
             if let ItemKind::Impl(hir_impl) = &item.kind;
             if let Some(trait_ref) = &hir_impl.of_trait;
@@ -118,14 +124,14 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
                         NON_SEND_FIELDS_IN_SEND_TY,
                         item.span,
                         &format!(
-                            "this implementation is unsound, as some fields in `{}` are `!Send`",
+                            "some fields in `{}` are not safe to be sent to another thread",
                             snippet(cx, hir_impl.self_ty.span, "Unknown")
                         ),
                         |diag| {
                             for field in non_send_fields {
                                 diag.span_note(
                                     field.def.span,
-                                    &format!("the type of field `{}` is `!Send`", field.def.ident.name),
+                                    &format!("it is not safe to send field `{}` to another thread", field.def.ident.name),
                                 );
 
                                 match field.generic_params.len() {
@@ -181,7 +187,7 @@ fn ty_allowed_without_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty
         return true;
     }
 
-    if is_copy(cx, ty) && !contains_raw_pointer(cx, ty) {
+    if is_copy(cx, ty) && !contains_pointer_like(cx, ty) {
         return true;
     }
 
@@ -201,7 +207,7 @@ fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t
             .all(|ty| ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait)),
         ty::Array(ty, _) | ty::Slice(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait),
         ty::Adt(_, substs) => {
-            if contains_raw_pointer(cx, ty) {
+            if contains_pointer_like(cx, ty) {
                 // descends only if ADT contains any raw pointers
                 substs.iter().all(|generic_arg| match generic_arg.unpack() {
                     GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait),
@@ -218,14 +224,20 @@ fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t
     }
 }
 
-/// Checks if the type contains any raw pointers in substs (including nested ones).
-fn contains_raw_pointer<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool {
+/// Checks if the type contains any pointer-like types in substs (including nested ones)
+fn contains_pointer_like<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool {
     for ty_node in target_ty.walk(cx.tcx) {
-        if_chain! {
-            if let GenericArgKind::Type(inner_ty) = ty_node.unpack();
-            if let ty::RawPtr(_) = inner_ty.kind();
-            then {
-                return true;
+        if let GenericArgKind::Type(inner_ty) = ty_node.unpack() {
+            match inner_ty.kind() {
+                ty::RawPtr(_) => {
+                    return true;
+                },
+                ty::Adt(adt_def, _) => {
+                    if match_def_path(cx, adt_def.did, &paths::PTR_NON_NULL) {
+                        return true;
+                    }
+                },
+                _ => (),
             }
         }
     }
index 3dcc9e26c9e2d9b43d1cbcb8509d09347f17b1db..a04d589f880fa9aca9e43677f0076129bc9cdd69 100644 (file)
@@ -112,7 +112,7 @@ fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, mac_braces: &'a Mac
         if snip.starts_with(&format!("{}!", name));
         if unnested_or_local();
         // make formatting consistent
-        let c = snip.replace(" ", "");
+        let c = snip.replace(' ', "");
         if !c.starts_with(&format!("{}!{}", name, braces.0));
         if !mac_braces.done.contains(&span.ctxt().outer_expn_data().call_site);
         then {
index 262be17f61751d495d862f8d628e13c0a0c1ae79..953de0f72a86bbdd7d8ec75c8d73d63b3e8d061c 100644 (file)
@@ -1,15 +1,14 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::higher;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{
-    can_move_expr_to_closure, eager_or_lazy, in_constant, is_else_clause, is_lang_ctor, peel_hir_expr_while,
-    CaptureKind,
+    can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_lang_ctor, peel_blocks,
+    peel_hir_expr_while, CaptureKind,
 };
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::OptionSome;
-use rustc_hir::{def::Res, BindingAnnotation, Block, Expr, ExprKind, Mutability, PatKind, Path, QPath, UnOp};
+use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, Mutability, PatKind, Path, QPath, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
@@ -86,28 +85,6 @@ struct OptionIfLetElseOccurence {
     none_expr: String,
 }
 
-/// Extracts the body of a given arm. If the arm contains only an expression,
-/// then it returns the expression. Otherwise, it returns the entire block
-fn extract_body_from_expr<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
-    if let ExprKind::Block(
-        Block {
-            stmts: block_stmts,
-            expr: Some(block_expr),
-            ..
-        },
-        _,
-    ) = expr.kind
-    {
-        if let [] = block_stmts {
-            Some(block_expr)
-        } else {
-            Some(expr)
-        }
-    } else {
-        None
-    }
-}
-
 fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String {
     format!(
         "{}{}",
@@ -135,7 +112,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
         if !is_result_ok(cx, let_expr); // Don't lint on Result::ok because a different lint does it already
         if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &let_pat.kind;
         if is_lang_ctor(cx, struct_qpath, OptionSome);
-        if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind;
+        if let PatKind::Binding(bind_annotation, _, id, None) = &inner_pat.kind;
         if let Some(some_captures) = can_move_expr_to_closure(cx, if_then);
         if let Some(none_captures) = can_move_expr_to_closure(cx, if_else);
         if some_captures
@@ -145,8 +122,8 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
 
         then {
             let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
-            let some_body = extract_body_from_expr(if_then)?;
-            let none_body = extract_body_from_expr(if_else)?;
+            let some_body = peel_blocks(if_then);
+            let none_body = peel_blocks(if_else);
             let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" };
             let capture_name = id.name.to_ident_string();
             let (as_ref, as_mut) = match &let_expr.kind {
index a5531993ee6ad36bb038edf750c1f22b993fc6ba..c765c8962cf70ee8ac533fe58940f38cbd74061a 100644 (file)
@@ -1,14 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher;
-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, path_to_local, path_to_local_id};
+use clippy_utils::{eq_expr_value, is_lang_ctor, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionNone, OptionSome, ResultOk};
-use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, PatKind, StmtKind};
+use rustc_hir::{BindingAnnotation, Expr, ExprKind, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
@@ -68,14 +67,8 @@ fn check_is_none_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>)
                 let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability);
                 let mut replacement: Option<String> = None;
                 if let Some(else_inner) = r#else {
-                    if_chain! {
-                        if let ExprKind::Block(block, None) = &else_inner.kind;
-                        if block.stmts.is_empty();
-                        if let Some(block_expr) = &block.expr;
-                        if eq_expr_value(cx, subject, block_expr);
-                        then {
-                            replacement = Some(format!("Some({}?)", receiver_str));
-                        }
+                    if eq_expr_value(cx, subject, peel_blocks(else_inner)) {
+                        replacement = Some(format!("Some({}?)", receiver_str));
                     }
                 } else if Self::moves_by_default(cx, subject)
                     && !matches!(subject.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
@@ -110,10 +103,7 @@ fn check_if_let_some_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'
 
             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) = if_then.kind;
-            if block.stmts.is_empty();
-            if let Some(trailing_expr) = &block.expr;
-            if path_to_local_id(trailing_expr, bind_id);
+            if path_to_local_id(peel_blocks(if_then), bind_id);
             then {
                 let mut applicability = Applicability::MachineApplicable;
                 let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
@@ -159,14 +149,7 @@ fn is_result(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
     }
 
     fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
-        match expression.kind {
-            ExprKind::Block(block, _) => {
-                if let Some(return_expression) = Self::return_expression(block) {
-                    return Self::expression_returns_none(cx, return_expression);
-                }
-
-                false
-            },
+        match peel_blocks_with_stmt(expression).kind {
             ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr),
             ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
             _ => false,
@@ -174,44 +157,12 @@ fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool
     }
 
     fn expression_returns_unmodified_err(cx: &LateContext<'_>, expr: &Expr<'_>, cond_expr: &Expr<'_>) -> bool {
-        match expr.kind {
-            ExprKind::Block(block, _) => {
-                if let Some(return_expression) = Self::return_expression(block) {
-                    return Self::expression_returns_unmodified_err(cx, return_expression, cond_expr);
-                }
-
-                false
-            },
+        match peel_blocks_with_stmt(expr).kind {
             ExprKind::Ret(Some(ret_expr)) => Self::expression_returns_unmodified_err(cx, ret_expr, cond_expr),
-            ExprKind::Path(_) => path_to_local(expr) == path_to_local(cond_expr),
+            ExprKind::Path(_) => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr),
             _ => false,
         }
     }
-
-    fn return_expression<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
-        // Check if last expression is a return statement. Then, return the expression
-        if_chain! {
-            if block.stmts.len() == 1;
-            if let Some(expr) = block.stmts.iter().last();
-            if let StmtKind::Semi(expr) = expr.kind;
-            if let ExprKind::Ret(Some(ret_expr)) = expr.kind;
-
-            then {
-                return Some(ret_expr);
-            }
-        }
-
-        // Check for `return` without a semicolon.
-        if_chain! {
-            if block.stmts.is_empty();
-            if let Some(ExprKind::Ret(Some(ret_expr))) = block.expr.as_ref().map(|e| &e.kind);
-            then {
-                return Some(ret_expr);
-            }
-        }
-
-        None
-    }
 }
 
 impl<'tcx> LateLintPass<'tcx> for QuestionMark {
index 3f38d12fb557ffa167d3ed09168e09424c8e6649..1bbaa104e60b17e8ab591e51c6c47fd27e45b258 100644 (file)
@@ -11,7 +11,7 @@
 
 declare_clippy_lint! {
     /// ### What it does
-    /// It lints if a struct has two method with same time:
+    /// It lints if a struct has two methods with the same name:
     /// one from a trait, another not from trait.
     ///
     /// ### Why is this bad?
@@ -100,7 +100,7 @@ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
                                         cx,
                                         SAME_NAME_METHOD,
                                         *impl_span,
-                                        "method's name is same to an existing method in a trait",
+                                        "method's name is the same as an existing method in a trait",
                                         |diag| {
                                             diag.span_note(
                                                 trait_method_span,
@@ -139,7 +139,7 @@ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
                                         cx,
                                         SAME_NAME_METHOD,
                                         impl_span,
-                                        "method's name is same to an existing method in a trait",
+                                        "method's name is the same as an existing method in a trait",
                                         |diag| {
                                             // TODO should we `span_note` on every trait?
                                             // iterate on trait_spans?
index dd73dbfe09e12b33c0e0d6bf427682414b0cb867..d386663e49858eda7d123d04dd2bad7fb0de7a82 100644 (file)
@@ -51,7 +51,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<
             _ => return,
         }
 
-        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
+        let parent = cx.tcx.hir().get_parent_did(impl_item.hir_id());
         let item = cx.tcx.hir().expect_item(parent);
         let self_ty = cx.tcx.type_of(item.def_id);
         let ret_ty = return_ty(cx, impl_item.hir_id());
@@ -76,7 +76,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<
             let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did);
             if let Some(Node::Item(x)) = cx.tcx.hir().find(self_id);
             let type_name = x.ident.name.as_str().to_lowercase();
-            if impl_item.ident.name.as_str() == type_name || impl_item.ident.name.as_str().replace("_", "") == type_name;
+            if impl_item.ident.name.as_str() == type_name || impl_item.ident.name.as_str().replace('_', "") == type_name;
 
             then {
                 span_lint(
index 368274440d5dcb23551ed30be7d4a8ba5f808ece..b03f4583365ea8ca3a68ffccab46bf435ea145cd 100644 (file)
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::SpanlessEq;
 use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths};
+use clippy_utils::{peel_blocks, SpanlessEq};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath};
@@ -201,7 +201,7 @@ fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 }
 
 fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
-    match src.kind {
+    match peel_blocks(src).kind {
         ExprKind::Binary(
             Spanned {
                 node: BinOpKind::Add, ..
@@ -209,9 +209,6 @@ fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
             left,
             _,
         ) => SpanlessEq::new(cx).eq_expr(target, left),
-        ExprKind::Block(block, _) => {
-            block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target))
-        },
         _ => false,
     }
 }
index be7431f11aad04af75a906aeb92c6a931caf3af9..fee01fb0bd186db7999e817c8f3751e0d2cf6d82 100644 (file)
@@ -1,13 +1,14 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::paths;
-use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::ty::{is_type_diagnostic_item, is_type_ref_to_diagnostic_item};
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::visitors::is_expr_unsafe;
+use clippy_utils::{get_parent_node, match_libc_symbol};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir as hir;
+use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, UnsafeSource};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::sym;
 
 declare_clippy_lint! {
     /// ### What it does
 declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]);
 
 impl LateLintPass<'tcx> for StrlenOnCStrings {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if expr.span.from_expansion() {
-            return;
-        }
-
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if_chain! {
-            if let hir::ExprKind::Call(func, [recv]) = expr.kind;
-            if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = func.kind;
-
-            if (&paths::LIBC_STRLEN).iter().map(|x| Symbol::intern(x)).eq(
-                path.segments.iter().map(|seg| seg.ident.name));
-            if let hir::ExprKind::MethodCall(path, _, args, _) = recv.kind;
-            if args.len() == 1;
-            if !args.iter().any(|e| e.span.from_expansion());
+            if !expr.span.from_expansion();
+            if let ExprKind::Call(func, [recv]) = expr.kind;
+            if let ExprKind::Path(path) = &func.kind;
+            if let Some(did) = cx.qpath_res(path, func.hir_id).opt_def_id();
+            if match_libc_symbol(cx, did, "strlen");
+            if let ExprKind::MethodCall(path, _, [self_arg], _) = recv.kind;
+            if !recv.span.from_expansion();
             if path.ident.name == sym::as_ptr;
             then {
-                let cstring = &args[0];
-                let ty = cx.typeck_results().expr_ty(cstring);
-                let val_name = snippet_with_macro_callsite(cx, cstring.span, "..");
-                let sugg = if is_type_diagnostic_item(cx, ty, sym::cstring_type){
-                    format!("{}.as_bytes().len()", val_name)
-                } else if is_type_ref_to_diagnostic_item(cx, ty, sym::CStr){
-                    format!("{}.to_bytes().len()", val_name)
+                let ctxt = expr.span.ctxt();
+                let span = match get_parent_node(cx.tcx, expr.hir_id) {
+                    Some(Node::Block(&Block {
+                        rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), span, ..
+                    }))
+                    if span.ctxt() == ctxt && !is_expr_unsafe(cx, self_arg) => {
+                        span
+                    }
+                    _ => expr.span,
+                };
+
+                let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
+                let mut app = Applicability::MachineApplicable;
+                let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
+                let method_name = if is_type_diagnostic_item(cx, ty, sym::cstring_type) {
+                    "as_bytes"
+                } else if is_type_diagnostic_item(cx, ty, sym::CStr) {
+                    "to_bytes"
                 } else {
                     return;
                 };
@@ -69,11 +76,11 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
                 span_lint_and_sugg(
                     cx,
                     STRLEN_ON_C_STRINGS,
-                    expr.span,
+                    span,
                     "using `libc::strlen` on a `CString` or `CStr` value",
-                    "try this (you might also need to get rid of `unsafe` block in some cases):",
-                    sugg,
-                    Applicability::Unspecified // Sometimes unnecessary `unsafe` block
+                    "try this",
+                    format!("{}.{}().len()", val_name, method_name),
+                    app,
                 );
             }
         }
index a514e8c44e2b6888b2930fa0ae03fa5a452d7c8e..afd7be89a4e289c94764d31c0eb27f12de4ff5de 100644 (file)
@@ -28,7 +28,7 @@
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for non-ASCII characters in string literals.
+    /// Checks for non-ASCII characters in string and char literals.
     ///
     /// ### Why is this bad?
     /// Yeah, we know, the 90's called and wanted their charset
@@ -75,7 +75,7 @@
 impl LateLintPass<'_> for Unicode {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
         if let ExprKind::Lit(ref lit) = expr.kind {
-            if let LitKind::Str(_, _) = lit.node {
+            if let LitKind::Str(_, _) | LitKind::Char(_) = lit.node {
                 check_str(cx, lit.span, expr.hir_id);
             }
         }
@@ -106,9 +106,9 @@ fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
             "invisible character detected",
             "consider replacing the string with",
             string
-                .replace("\u{200B}", "\\u{200B}")
-                .replace("\u{ad}", "\\u{AD}")
-                .replace("\u{2060}", "\\u{2060}"),
+                .replace('\u{200B}', "\\u{200B}")
+                .replace('\u{ad}', "\\u{AD}")
+                .replace('\u{2060}', "\\u{2060}"),
             Applicability::MachineApplicable,
         );
     }
index fd9d5b52e501f318199e5fa94ad9cf55dd79cad6..aa105580ee354833284b04ca99d6638183018a9b 100644 (file)
@@ -42,7 +42,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'_>)
         if impl_item.span.from_expansion() {
             return;
         }
-        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
+        let parent = cx.tcx.hir().get_parent_did(impl_item.hir_id());
         let parent_item = cx.tcx.hir().expect_item(parent);
         let assoc_item = cx.tcx.associated_item(impl_item.def_id);
         if_chain! {
index 1f97c8ba7e62cbba28398aa3e43624af357e18ba..81e9c2e15c97a19b8a4d7220b90576a998b06dc9 100644 (file)
@@ -1,11 +1,10 @@
 use clippy_utils::consts::{constant_simple, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::higher;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::match_type;
 use clippy_utils::{
-    is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, method_calls, path_to_res,
-    paths, SpanlessEq,
+    higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, method_calls,
+    path_to_res, paths, peel_blocks_with_stmt, SpanlessEq,
 };
 use if_chain::if_chain;
 use rustc_ast as ast;
@@ -662,10 +661,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
             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);
-            if let ExprKind::Block(block, _) = &body.value.kind;
-            let stmts = &block.stmts;
-            if stmts.len() == 1 && block.expr.is_none();
-            if let StmtKind::Semi(only_expr) = &stmts[0].kind;
+            let only_expr = peel_blocks_with_stmt(&body.value);
             if let ExprKind::MethodCall(ps, _, span_call_args, _) = &only_expr.kind;
             then {
                 let and_then_snippets = get_and_then_snippets(cx, and_then_args);
index 8051c58bad7a2711345098c08abcfa3f873df494..7707eebd6223e2cb01ab4b0b4bd559ab3fc7aa6c 100644 (file)
@@ -8,6 +8,11 @@
 //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
 //! a simple mistake)
 
+use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type};
+
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
+use clippy_utils::{last_path_segment, match_def_path, match_function_call, match_path, paths};
 use if_chain::if_chain;
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
 use std::io::prelude::*;
 use std::path::Path;
 
-use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type};
-use clippy_utils::{
-    diagnostics::span_lint, last_path_segment, match_def_path, match_function_call, match_path, paths, ty::match_type,
-    ty::walk_ptrs_ty_depth,
-};
-
 /// This is the output file of the lint collector.
 const OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
 /// These lints are excluded from the export.
index bfa9407285b54ae07f3d1240c593605857e6fbeb..5bf0cffdbad16a52983d0f7f48e2ad476c4cd8e4 100644 (file)
     /// application and might forget to remove those prints afterward.
     ///
     /// ### Known problems
-    /// Only catches `print!` and `println!` calls.
+    /// * Only catches `print!` and `println!` calls.
+    /// * The lint level is unaffected by crate attributes. The level can still
+    ///   be set for functions, modules and other items. To change the level for
+    ///   the entire crate, please use command line flags. More information and a
+    ///   configuration example can be found in [clippy#6610].
+    ///
+    /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
     ///
     /// ### Example
     /// ```rust
     /// application and might forget to remove those prints afterward.
     ///
     /// ### Known problems
-    /// Only catches `eprint!` and `eprintln!` calls.
+    /// * Only catches `eprint!` and `eprintln!` calls.
+    /// * The lint level is unaffected by crate attributes. The level can still
+    ///   be set for functions, modules and other items. To change the level for
+    ///   the entire crate, please use command line flags. More information and a
+    ///   configuration example can be found in [clippy#6610].
+    ///
+    /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
     ///
     /// ### Example
     /// ```rust
@@ -571,10 +583,10 @@ fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool)
             let replacement: String = match lit.token.kind {
                 LitKind::Integer | LitKind::Float | LitKind::Err => continue,
                 LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => {
-                    lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}")
+                    lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}")
                 },
                 LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => {
-                    lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}")
+                    lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}")
                 },
                 LitKind::StrRaw(_) | LitKind::Str | LitKind::ByteStrRaw(_) | LitKind::ByteStr => continue,
                 LitKind::Byte | LitKind::Char => match &*lit.token.symbol.as_str() {
index d99a3d9359e1f439ba973431874f2b2b3c3941d1..0ba0b59ed13daf6f93653f554b03f6a4d0a0076f 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_utils"
-version = "0.1.58"
+version = "0.1.59"
 edition = "2021"
 publish = false
 
index 3fdea55aaa1bf743d87b77cbf8b7f3a56cee8167..4e21436143503142ef43a798b79ccba5c8e2cf3e 100644 (file)
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
 use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
 use rustc_hir::{
-    def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, ForeignItem, GenericArgs,
-    HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node,
-    Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
-    UnOp,
+    def, Arm, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl,
+    ForeignItem, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local,
+    MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem,
+    TraitItemKind, TraitRef, TyKind, UnOp,
 };
 use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::exports::Export;
@@ -1224,6 +1224,70 @@ pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
     }
 }
 
+/// Removes blocks around an expression, only if the block contains just one expression
+/// and no statements. Unsafe blocks are not removed.
+///
+/// Examples:
+///  * `{}`               -> `{}`
+///  * `{ x }`            -> `x`
+///  * `{{ x }}`          -> `x`
+///  * `{ x; }`           -> `{ x; }`
+///  * `{ x; y }`         -> `{ x; y }`
+///  * `{ unsafe { x } }` -> `unsafe { x }`
+pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
+    while let ExprKind::Block(
+        Block {
+            stmts: [],
+            expr: Some(inner),
+            rules: BlockCheckMode::DefaultBlock,
+            ..
+        },
+        _,
+    ) = expr.kind
+    {
+        expr = inner;
+    }
+    expr
+}
+
+/// Removes blocks around an expression, only if the block contains just one expression
+/// or just one expression statement with a semicolon. Unsafe blocks are not removed.
+///
+/// Examples:
+///  * `{}`               -> `{}`
+///  * `{ x }`            -> `x`
+///  * `{ x; }`           -> `x`
+///  * `{{ x; }}`         -> `x`
+///  * `{ x; y }`         -> `{ x; y }`
+///  * `{ unsafe { x } }` -> `unsafe { x }`
+pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
+    while let ExprKind::Block(
+        Block {
+            stmts: [],
+            expr: Some(inner),
+            rules: BlockCheckMode::DefaultBlock,
+            ..
+        }
+        | Block {
+            stmts:
+                [
+                    Stmt {
+                        kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
+                        ..
+                    },
+                ],
+            expr: None,
+            rules: BlockCheckMode::DefaultBlock,
+            ..
+        },
+        _,
+    ) = expr.kind
+    {
+        expr = inner;
+    }
+    expr
+}
+
 /// Checks if the given expression is the else clause of either an `if` or `if let` expression.
 pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
     let mut iter = tcx.hir().parent_iter(expr.hir_id);
@@ -1405,20 +1469,6 @@ pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
     has_attr(attrs, sym::automatically_derived)
 }
 
-/// Remove blocks around an expression.
-///
-/// Ie. `x`, `{ x }` and `{{{{ x }}}}` all give `x`. `{ x; y }` and `{}` return
-/// themselves.
-pub fn remove_blocks<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
-    while let ExprKind::Block(block, ..) = expr.kind {
-        match (block.stmts.is_empty(), block.expr.as_ref()) {
-            (true, Some(e)) => expr = e,
-            _ => break,
-        }
-    }
-    expr
-}
-
 pub fn is_self(slf: &Param<'_>) -> bool {
     if let PatKind::Binding(.., name, _) = slf.pat.kind {
         name.name == kw::SelfLower
@@ -1597,6 +1647,14 @@ pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -
     syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
 }
 
+/// Checks if the given `DefId` matches the `libc` item.
+pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
+    let path = cx.get_def_path(did);
+    // libc is meant to be used as a flat list of names, but they're all actually defined in different
+    // modules based on the target platform. Ignore everything but crate name and the item name.
+    path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
+}
+
 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)
index b04d17364264a032041b2208b7225c4bd4107f0f..6171823abbbd04fa7142aecaad1231c654150ff2 100644 (file)
@@ -86,7 +86,6 @@
 pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
 #[cfg(feature = "internal-lints")]
 pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
-pub const LIBC_STRLEN: [&str; 2] = ["libc", "strlen"];
 #[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))]
 pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
 pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
 pub const WRITE_MACRO: [&str; 3] = ["core", "macros", "write"];
 #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
 pub const WRITELN_MACRO: [&str; 3] = ["core", "macros", "writeln"];
+pub const PTR_NON_NULL: [&str; 4] = ["core", "ptr", "non_null", "NonNull"];
index 01fb944cc36f64dcfd266726ce70bf4aa6ba5418..872942685f0d126cb01dfb6e492aab6b7d5a74e0 100644 (file)
@@ -1,19 +1,27 @@
 //! Contains utility functions to generate suggestions.
 #![deny(clippy::missing_docs_in_private_items)]
 
-use crate::higher;
-use crate::source::{snippet, snippet_opt, snippet_with_context, snippet_with_macro_callsite};
+use crate::source::{
+    snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite,
+};
+use crate::{get_parent_expr_for_hir, higher};
 use rustc_ast::util::parser::AssocOp;
 use rustc_ast::{ast, token};
 use rustc_ast_pretty::pprust::token_kind_to_string;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
+use rustc_hir::{ExprKind, HirId, MutTy, TyKind};
+use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{EarlyContext, LateContext, LintContext};
-use rustc_span::source_map::{CharPos, Span};
-use rustc_span::{BytePos, Pos, SyntaxContext};
+use rustc_middle::hir::place::ProjectionKind;
+use rustc_middle::mir::{FakeReadCause, Mutability};
+use rustc_middle::ty;
+use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext};
+use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 use std::borrow::Cow;
 use std::convert::TryInto;
 use std::fmt::Display;
+use std::iter;
 use std::ops::{Add, Neg, Not, Sub};
 
 /// A helper type to build suggestion correctly handling parentheses.
@@ -716,6 +724,267 @@ fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability:
     }
 }
 
+/// Suggestion results for handling closure
+/// args dereferencing and borrowing
+pub struct DerefClosure {
+    /// confidence on the built suggestion
+    pub applicability: Applicability,
+    /// gradually built suggestion
+    pub suggestion: String,
+}
+
+/// Build suggestion gradually by handling closure arg specific usages,
+/// such as explicit deref and borrowing cases.
+/// Returns `None` if no such use cases have been triggered in closure body
+///
+/// note: this only works on single line immutable closures with exactly one input parameter.
+pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<'_>) -> Option<DerefClosure> {
+    if let hir::ExprKind::Closure(_, fn_decl, body_id, ..) = closure.kind {
+        let closure_body = cx.tcx.hir().body(body_id);
+        // is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`)
+        // a type annotation is present if param `kind` is different from `TyKind::Infer`
+        let closure_arg_is_type_annotated_double_ref = if let TyKind::Rptr(_, MutTy { ty, .. }) = fn_decl.inputs[0].kind
+        {
+            matches!(ty.kind, TyKind::Rptr(_, MutTy { .. }))
+        } else {
+            false
+        };
+
+        let mut visitor = DerefDelegate {
+            cx,
+            closure_span: closure.span,
+            closure_arg_is_type_annotated_double_ref,
+            next_pos: closure.span.lo(),
+            suggestion_start: String::new(),
+            applicability: Applicability::MaybeIncorrect,
+        };
+
+        let fn_def_id = cx.tcx.hir().local_def_id(closure.hir_id);
+        cx.tcx.infer_ctxt().enter(|infcx| {
+            ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results())
+                .consume_body(closure_body);
+        });
+
+        if !visitor.suggestion_start.is_empty() {
+            return Some(DerefClosure {
+                applicability: visitor.applicability,
+                suggestion: visitor.finish(),
+            });
+        }
+    }
+    None
+}
+
+/// Visitor struct used for tracking down
+/// dereferencing and borrowing of closure's args
+struct DerefDelegate<'a, 'tcx> {
+    /// The late context of the lint
+    cx: &'a LateContext<'tcx>,
+    /// The span of the input closure to adapt
+    closure_span: Span,
+    /// Indicates if the arg of the closure is a type annotated double reference
+    closure_arg_is_type_annotated_double_ref: bool,
+    /// last position of the span to gradually build the suggestion
+    next_pos: BytePos,
+    /// starting part of the gradually built suggestion
+    suggestion_start: String,
+    /// confidence on the built suggestion
+    applicability: Applicability,
+}
+
+impl DerefDelegate<'_, 'tcx> {
+    /// build final suggestion:
+    /// - create the ending part of suggestion
+    /// - concatenate starting and ending parts
+    /// - potentially remove needless borrowing
+    pub fn finish(&mut self) -> String {
+        let end_span = Span::new(self.next_pos, self.closure_span.hi(), self.closure_span.ctxt(), None);
+        let end_snip = snippet_with_applicability(self.cx, end_span, "..", &mut self.applicability);
+        let sugg = format!("{}{}", self.suggestion_start, end_snip);
+        if self.closure_arg_is_type_annotated_double_ref {
+            sugg.replacen('&', "", 1)
+        } else {
+            sugg
+        }
+    }
+
+    /// indicates whether the function from `parent_expr` takes its args by double reference
+    fn func_takes_arg_by_double_ref(&self, parent_expr: &'tcx hir::Expr<'_>, cmt_hir_id: HirId) -> bool {
+        let (call_args, inputs) = match parent_expr.kind {
+            ExprKind::MethodCall(_, _, call_args, _) => {
+                if let Some(method_did) = self.cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) {
+                    (call_args, self.cx.tcx.fn_sig(method_did).skip_binder().inputs())
+                } else {
+                    return false;
+                }
+            },
+            ExprKind::Call(func, call_args) => {
+                let typ = self.cx.typeck_results().expr_ty(func);
+                (call_args, typ.fn_sig(self.cx.tcx).skip_binder().inputs())
+            },
+            _ => return false,
+        };
+
+        iter::zip(call_args, inputs)
+            .any(|(arg, ty)| arg.hir_id == cmt_hir_id && matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref()))
+    }
+}
+
+impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
+    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
+
+    #[allow(clippy::too_many_lines)]
+    fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
+        if let PlaceBase::Local(id) = cmt.place.base {
+            let map = self.cx.tcx.hir();
+            let span = map.span(cmt.hir_id);
+            let start_span = Span::new(self.next_pos, span.lo(), span.ctxt(), None);
+            let mut start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability);
+
+            // identifier referring to the variable currently triggered (i.e.: `fp`)
+            let ident_str = map.name(id).to_string();
+            // full identifier that includes projection (i.e.: `fp.field`)
+            let ident_str_with_proj = snippet(self.cx, span, "..").to_string();
+
+            if cmt.place.projections.is_empty() {
+                // handle item without any projection, that needs an explicit borrowing
+                // i.e.: suggest `&x` instead of `x`
+                self.suggestion_start.push_str(&format!("{}&{}", start_snip, ident_str));
+            } else {
+                // cases where a parent `Call` or `MethodCall` is using the item
+                // i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()`
+                //
+                // Note about method calls:
+                // - compiler automatically dereference references if the target type is a reference (works also for
+                //   function call)
+                // - `self` arguments in the case of `x.is_something()` are also automatically (de)referenced, and
+                //   no projection should be suggested
+                if let Some(parent_expr) = get_parent_expr_for_hir(self.cx, cmt.hir_id) {
+                    match &parent_expr.kind {
+                        // given expression is the self argument and will be handled completely by the compiler
+                        // i.e.: `|x| x.is_something()`
+                        ExprKind::MethodCall(_, _, [self_expr, ..], _) if self_expr.hir_id == cmt.hir_id => {
+                            self.suggestion_start
+                                .push_str(&format!("{}{}", start_snip, ident_str_with_proj));
+                            self.next_pos = span.hi();
+                            return;
+                        },
+                        // item is used in a call
+                        // i.e.: `Call`: `|x| please(x)` or `MethodCall`: `|x| [1, 2, 3].contains(x)`
+                        ExprKind::Call(_, [call_args @ ..]) | ExprKind::MethodCall(_, _, [_, call_args @ ..], _) => {
+                            let expr = self.cx.tcx.hir().expect_expr(cmt.hir_id);
+                            let arg_ty_kind = self.cx.typeck_results().expr_ty(expr).kind();
+
+                            if matches!(arg_ty_kind, ty::Ref(_, _, Mutability::Not)) {
+                                // suggest ampersand if call function is taking args by double reference
+                                let takes_arg_by_double_ref =
+                                    self.func_takes_arg_by_double_ref(parent_expr, cmt.hir_id);
+
+                                // compiler will automatically dereference field or index projection, so no need
+                                // to suggest ampersand, but full identifier that includes projection is required
+                                let has_field_or_index_projection =
+                                    cmt.place.projections.iter().any(|proj| {
+                                        matches!(proj.kind, ProjectionKind::Field(..) | ProjectionKind::Index)
+                                    });
+
+                                // no need to bind again if the function doesn't take arg by double ref
+                                // and if the item is already a double ref
+                                let ident_sugg = if !call_args.is_empty()
+                                    && !takes_arg_by_double_ref
+                                    && (self.closure_arg_is_type_annotated_double_ref || has_field_or_index_projection)
+                                {
+                                    let ident = if has_field_or_index_projection {
+                                        ident_str_with_proj
+                                    } else {
+                                        ident_str
+                                    };
+                                    format!("{}{}", start_snip, ident)
+                                } else {
+                                    format!("{}&{}", start_snip, ident_str)
+                                };
+                                self.suggestion_start.push_str(&ident_sugg);
+                                self.next_pos = span.hi();
+                                return;
+                            }
+
+                            self.applicability = Applicability::Unspecified;
+                        },
+                        _ => (),
+                    }
+                }
+
+                let mut replacement_str = ident_str;
+                let mut projections_handled = false;
+                cmt.place.projections.iter().enumerate().for_each(|(i, proj)| {
+                    match proj.kind {
+                        // Field projection like `|v| v.foo`
+                        // no adjustment needed here, as field projections are handled by the compiler
+                        ProjectionKind::Field(..) => match cmt.place.ty_before_projection(i).kind() {
+                            ty::Adt(..) | ty::Tuple(_) => {
+                                replacement_str = ident_str_with_proj.clone();
+                                projections_handled = true;
+                            },
+                            _ => (),
+                        },
+                        // Index projection like `|x| foo[x]`
+                        // the index is dropped so we can't get it to build the suggestion,
+                        // so the span is set-up again to get more code, using `span.hi()` (i.e.: `foo[x]`)
+                        // instead of `span.lo()` (i.e.: `foo`)
+                        ProjectionKind::Index => {
+                            let start_span = Span::new(self.next_pos, span.hi(), span.ctxt(), None);
+                            start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability);
+                            replacement_str.clear();
+                            projections_handled = true;
+                        },
+                        // note: unable to trigger `Subslice` kind in tests
+                        ProjectionKind::Subslice => (),
+                        ProjectionKind::Deref => {
+                            // Explicit derefs are typically handled later on, but
+                            // some items do not need explicit deref, such as array accesses,
+                            // so we mark them as already processed
+                            // i.e.: don't suggest `*sub[1..4].len()` for `|sub| sub[1..4].len() == 3`
+                            if let ty::Ref(_, inner, _) = cmt.place.ty_before_projection(i).kind() {
+                                if matches!(inner.kind(), ty::Ref(_, innermost, _) if innermost.is_array()) {
+                                    projections_handled = true;
+                                }
+                            }
+                        },
+                    }
+                });
+
+                // handle `ProjectionKind::Deref` by removing one explicit deref
+                // if no special case was detected (i.e.: suggest `*x` instead of `**x`)
+                if !projections_handled {
+                    let last_deref = cmt
+                        .place
+                        .projections
+                        .iter()
+                        .rposition(|proj| proj.kind == ProjectionKind::Deref);
+
+                    if let Some(pos) = last_deref {
+                        let mut projections = cmt.place.projections.clone();
+                        projections.truncate(pos);
+
+                        for item in projections {
+                            if item.kind == ProjectionKind::Deref {
+                                replacement_str = format!("*{}", replacement_str);
+                            }
+                        }
+                    }
+                }
+
+                self.suggestion_start
+                    .push_str(&format!("{}{}", start_snip, replacement_str));
+            }
+            self.next_pos = span.hi();
+        }
+    }
+
+    fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
+
+    fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
+}
+
 #[cfg(test)]
 mod test {
     use super::Sugg;
index 823df5cb7517eaeb641da7100dce261df64544df..4bfd3c64b9c361acb61001600277e3064bbb2ff3 100644 (file)
@@ -1,8 +1,10 @@
 use crate::path_to_local_id;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{Arm, Block, Body, BodyId, Expr, ExprKind, HirId, Stmt, UnOp};
+use rustc_hir::intravisit::{self, walk_block, walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::{
+    Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, Unsafety,
+};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
 use rustc_middle::ty;
@@ -317,3 +319,64 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
     v.visit_expr(e);
     v.is_const
 }
+
+/// Checks if the given expression performs an unsafe operation outside of an unsafe block.
+pub fn is_expr_unsafe(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
+    struct V<'a, 'tcx> {
+        cx: &'a LateContext<'tcx>,
+        is_unsafe: bool,
+    }
+    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
+        type Map = Map<'tcx>;
+        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+            NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+        }
+        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+            if self.is_unsafe {
+                return;
+            }
+            match e.kind {
+                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => {
+                    self.is_unsafe = true;
+                },
+                ExprKind::MethodCall(..)
+                    if self
+                        .cx
+                        .typeck_results()
+                        .type_dependent_def_id(e.hir_id)
+                        .map_or(false, |id| self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe) =>
+                {
+                    self.is_unsafe = true;
+                },
+                ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
+                    ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
+                    ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
+                    _ => walk_expr(self, e),
+                },
+                ExprKind::Path(ref p)
+                    if self
+                        .cx
+                        .qpath_res(p, e.hir_id)
+                        .opt_def_id()
+                        .map_or(false, |id| self.cx.tcx.is_mutable_static(id)) =>
+                {
+                    self.is_unsafe = true;
+                },
+                _ => walk_expr(self, e),
+            }
+        }
+        fn visit_block(&mut self, b: &'tcx Block<'_>) {
+            if !matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
+                walk_block(self, b);
+            }
+        }
+        fn visit_nested_item(&mut self, id: ItemId) {
+            if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind {
+                self.is_unsafe = i.unsafety == Unsafety::Unsafe;
+            }
+        }
+    }
+    let mut v = V { cx, is_unsafe: false };
+    v.visit_expr(e);
+    v.is_unsafe
+}
index f02d0b18ddc7ee63a2200e1b09725da0c0d30c68..27969b0d655dd738de563cb53c94165a682e9660 100644 (file)
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2021-11-23"
+channel = "nightly-2021-12-02"
 components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
index b07f9dd3df30e02b4ae6f95ac8d802905fe9855c..49eecf18b4c4f83f5fadacb8f6ad593bc2969cac 100644 (file)
@@ -1,86 +1,86 @@
-error: this implementation is unsound, as some fields in `NoGeneric` are `!Send`
+error: some fields in `NoGeneric` are not safe to be sent to another thread
   --> $DIR/test.rs:11:1
    |
 LL | unsafe impl Send for NoGeneric {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::non-send-fields-in-send-ty` implied by `-D warnings`
-note: the type of field `rc_is_not_send` is `!Send`
+note: it is not safe to send field `rc_is_not_send` to another thread
   --> $DIR/test.rs:8:5
    |
 LL |     rc_is_not_send: Rc<String>,
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: use a thread-safe type that implements `Send`
 
-error: this implementation is unsound, as some fields in `MultiField<T>` are `!Send`
+error: some fields in `MultiField<T>` are not safe to be sent to another thread
   --> $DIR/test.rs:19:1
    |
 LL | unsafe impl<T> Send for MultiField<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: the type of field `field1` is `!Send`
+note: it is not safe to send field `field1` to another thread
   --> $DIR/test.rs:14:5
    |
 LL |     field1: T,
    |     ^^^^^^^^^
    = help: add `T: Send` bound in `Send` impl
-note: the type of field `field2` is `!Send`
+note: it is not safe to send field `field2` to another thread
   --> $DIR/test.rs:15:5
    |
 LL |     field2: T,
    |     ^^^^^^^^^
    = help: add `T: Send` bound in `Send` impl
-note: the type of field `field3` is `!Send`
+note: it is not safe to send field `field3` to another thread
   --> $DIR/test.rs:16:5
    |
 LL |     field3: T,
    |     ^^^^^^^^^
    = help: add `T: Send` bound in `Send` impl
 
-error: this implementation is unsound, as some fields in `MyOption<T>` are `!Send`
+error: some fields in `MyOption<T>` are not safe to be sent to another thread
   --> $DIR/test.rs:26:1
    |
 LL | unsafe impl<T> Send for MyOption<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: the type of field `0` is `!Send`
+note: it is not safe to send field `0` to another thread
   --> $DIR/test.rs:22:12
    |
 LL |     MySome(T),
    |            ^
    = help: add `T: Send` bound in `Send` impl
 
-error: this implementation is unsound, as some fields in `HeuristicTest` are `!Send`
+error: some fields in `HeuristicTest` are not safe to be sent to another thread
   --> $DIR/test.rs:41:1
    |
 LL | unsafe impl Send for HeuristicTest {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: the type of field `field1` is `!Send`
+note: it is not safe to send field `field1` to another thread
   --> $DIR/test.rs:34:5
    |
 LL |     field1: Vec<*const NonSend>,
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: use a thread-safe type that implements `Send`
-note: the type of field `field2` is `!Send`
+note: it is not safe to send field `field2` to another thread
   --> $DIR/test.rs:35:5
    |
 LL |     field2: [*const NonSend; 3],
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: use a thread-safe type that implements `Send`
-note: the type of field `field3` is `!Send`
+note: it is not safe to send field `field3` to another thread
   --> $DIR/test.rs:36:5
    |
 LL |     field3: (*const NonSend, *const NonSend, *const NonSend),
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: use a thread-safe type that implements `Send`
-note: the type of field `field4` is `!Send`
+note: it is not safe to send field `field4` to another thread
   --> $DIR/test.rs:37:5
    |
 LL |     field4: (*const NonSend, Rc<u8>),
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: use a thread-safe type that implements `Send`
-note: the type of field `field5` is `!Send`
+note: it is not safe to send field `field5` to another thread
   --> $DIR/test.rs:38:5
    |
 LL |     field5: Vec<Vec<*const NonSend>>,
index 439884b7d274aef2d3cd92637a0b8847e1bedbd8..0d7713aa9a2780794fcc118a02af3f50b18276ec 100644 (file)
@@ -24,6 +24,9 @@ LL | | }
 error[E0308]: mismatched types
   --> $DIR/ice-6250.rs:12:14
    |
+LL |     for reference in vec![1, 2, 3] {
+   |         --------- expected due to the type of this binding
+...
 LL |         Some(reference) = cache.data.get(key) {
    |              ^^^^^^^^^ expected integer, found `&i32`
    |
index f5ff9eed7a1a662eb620119fbe008acebb73ef47..d3571eaf0d7be19de449329da567f9ae58bb24ba 100644 (file)
@@ -6,7 +6,6 @@
 
 #[warn(clippy::all)]
 fn main() {
-    // TODO: do somethjing with swap
     let mut a = 42;
     let mut b = 1337;
 
index 079f1828cf93a68c091160818220b4f5c569b876..48152d8ad7743c890a8100f4d8fd7a98aada0915 100644 (file)
@@ -1,5 +1,5 @@
 error: this looks like you are trying to swap `a` and `b`
-  --> $DIR/no_std_swap.rs:13:5
+  --> $DIR/no_std_swap.rs:12:5
    |
 LL | /     a = b;
 LL | |     b = a;
index 39cc58cd298439d0c06756b5f68090234b9414b1..9b862133580459199a7fa746cfbef677ded7c678 100644 (file)
@@ -23,4 +23,7 @@ fn main() {
     // Issue #6808
     let arr: [u8; 64] = [0; 64];
     let _: Vec<_> = arr.to_vec();
+
+    // Issue #6703
+    let _: Vec<isize> = v.to_vec();
 }
index c2a036ec09f1e80cea582f0b49c746497e95e3ce..639f50665f2aaff6da9f7ccd07933a05f89325eb 100644 (file)
@@ -26,4 +26,7 @@ fn main() {
     // Issue #6808
     let arr: [u8; 64] = [0; 64];
     let _: Vec<_> = arr.iter().cloned().collect();
+
+    // Issue #6703
+    let _: Vec<isize> = v.iter().copied().collect();
 }
index e1df61794cecee7273dfac211f425fec0f924680..b2cc497bf433ae608a644e88f1fc9f986c033b8b 100644 (file)
@@ -28,5 +28,11 @@ error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling
 LL |     let _: Vec<_> = arr.iter().cloned().collect();
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
 
-error: aborting due to 4 previous errors
+error: called `iter().copied().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
+  --> $DIR/iter_cloned_collect.rs:31:26
+   |
+LL |     let _: Vec<isize> = v.iter().copied().collect();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
+
+error: aborting due to 5 previous errors
 
index 2d8f3c2f0e7aacf69c5d1b0808e4f38117cfa311..c5cb2eb1fe1c2e66a727e317a0a13ffa169b8df2 100644 (file)
@@ -3,7 +3,8 @@
     unused_assignments,
     clippy::similar_names,
     clippy::blacklisted_name,
-    clippy::branches_sharing_code
+    clippy::branches_sharing_code,
+    clippy::needless_late_init
 )]
 #![warn(clippy::useless_let_if_seq)]
 
index 9cf2e10a5ee56940e5a06d103a6bcd01323dbf05..271ccce681c9fe4bb5a05f6a976bb16b7a894f91 100644 (file)
@@ -1,5 +1,5 @@
 error: `if _ { .. } else { .. }` is an expression
-  --> $DIR/let_if_seq.rs:65:5
+  --> $DIR/let_if_seq.rs:66:5
    |
 LL | /     let mut foo = 0;
 LL | |     if f() {
@@ -11,7 +11,7 @@ LL | |     }
    = note: you might not need `mut` at all
 
 error: `if _ { .. } else { .. }` is an expression
-  --> $DIR/let_if_seq.rs:70:5
+  --> $DIR/let_if_seq.rs:71:5
    |
 LL | /     let mut bar = 0;
 LL | |     if f() {
@@ -25,7 +25,7 @@ LL | |     }
    = note: you might not need `mut` at all
 
 error: `if _ { .. } else { .. }` is an expression
-  --> $DIR/let_if_seq.rs:78:5
+  --> $DIR/let_if_seq.rs:79:5
    |
 LL | /     let quz;
 LL | |     if f() {
@@ -36,7 +36,7 @@ LL | |     }
    | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };`
 
 error: `if _ { .. } else { .. }` is an expression
-  --> $DIR/let_if_seq.rs:107:5
+  --> $DIR/let_if_seq.rs:108:5
    |
 LL | /     let mut baz = 0;
 LL | |     if f() {
index f7ed72a11cf684b64f8d584cbaa990ae4aa0fa39..b2bc97f4744a5dc749ac2bed1dae58bf1efe5788 100644 (file)
@@ -19,8 +19,7 @@ fn max(self, x: u64) -> NotOrd {
 }
 
 fn main() {
-    let x;
-    x = 2usize;
+    let x = 2usize;
     min(1, max(3, x));
     min(max(3, x), 1);
     max(min(x, 1), 3);
@@ -35,9 +34,7 @@ fn main() {
     let y = 2isize;
     min(max(y, -1), 3);
 
-    let s;
-    s = "Hello";
-
+    let s = "Hello";
     min("Apple", max("Zoo", s));
     max(min(s, "Apple"), "Zoo");
 
index 9f8e26fa406f0e59c7458ec5b54ca234386abf6d..c70b77eabbd8515abd0d0bab9a1cd869166a007a 100644 (file)
@@ -1,5 +1,5 @@
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:24:5
+  --> $DIR/min_max.rs:23:5
    |
 LL |     min(1, max(3, x));
    |     ^^^^^^^^^^^^^^^^^
@@ -7,73 +7,73 @@ LL |     min(1, max(3, x));
    = note: `-D clippy::min-max` implied by `-D warnings`
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:25:5
+  --> $DIR/min_max.rs:24:5
    |
 LL |     min(max(3, x), 1);
    |     ^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:26:5
+  --> $DIR/min_max.rs:25:5
    |
 LL |     max(min(x, 1), 3);
    |     ^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:27:5
+  --> $DIR/min_max.rs:26:5
    |
 LL |     max(3, min(x, 1));
    |     ^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:29:5
+  --> $DIR/min_max.rs:28:5
    |
 LL |     my_max(3, my_min(x, 1));
    |     ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:41:5
+  --> $DIR/min_max.rs:38:5
    |
 LL |     min("Apple", max("Zoo", s));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:42:5
+  --> $DIR/min_max.rs:39:5
    |
 LL |     max(min(s, "Apple"), "Zoo");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:47:5
+  --> $DIR/min_max.rs:44:5
    |
 LL |     x.min(1).max(3);
    |     ^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:48:5
+  --> $DIR/min_max.rs:45:5
    |
 LL |     x.max(3).min(1);
    |     ^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:49:5
+  --> $DIR/min_max.rs:46:5
    |
 LL |     f.max(3f32).min(1f32);
    |     ^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:55:5
+  --> $DIR/min_max.rs:52:5
    |
 LL |     max(x.min(1), 3);
    |     ^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:58:5
+  --> $DIR/min_max.rs:55:5
    |
 LL |     s.max("Zoo").min("Apple");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this `min`/`max` combination leads to constant result
-  --> $DIR/min_max.rs:59:5
+  --> $DIR/min_max.rs:56:5
    |
 LL |     s.min("Apple").max("Zoo");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
index a2e3988daff1bdc41d38db637a331abf9721c987..85da1f4e10437cb6a3669e47b3dc3a35f5494913 100644 (file)
@@ -53,6 +53,7 @@ fn main() {
     needless_bool(x);
     needless_bool2(x);
     needless_bool3(x);
+    needless_bool_condition();
 }
 
 fn bool_ret3(x: bool) -> bool {
@@ -98,3 +99,19 @@ fn needless_bool_in_the_suggestion_wraps_the_predicate_of_if_else_statement_in_b
         true
     } else { !returns_bool() };
 }
+
+unsafe fn no(v: u8) -> u8 {
+    v
+}
+
+#[allow(clippy::unnecessary_operation)]
+fn needless_bool_condition() -> bool {
+    (unsafe { no(4) } & 1 != 0);
+    let _brackets_unneeded = unsafe { no(4) } & 1 != 0;
+    fn foo() -> bool {
+        // parentheses are needed here
+        (unsafe { no(4) } & 1 != 0)
+    }
+
+    foo()
+}
index 75805e85789194dbde588fb70e85d7f487d4d16b..add606302511b89dea3099e5fd13848ab0835f70 100644 (file)
@@ -65,6 +65,7 @@ fn main() {
     needless_bool(x);
     needless_bool2(x);
     needless_bool3(x);
+    needless_bool_condition();
 }
 
 fn bool_ret3(x: bool) -> bool {
@@ -130,3 +131,23 @@ fn needless_bool_in_the_suggestion_wraps_the_predicate_of_if_else_statement_in_b
         true
     };
 }
+
+unsafe fn no(v: u8) -> u8 {
+    v
+}
+
+#[allow(clippy::unnecessary_operation)]
+fn needless_bool_condition() -> bool {
+    if unsafe { no(4) } & 1 != 0 {
+        true
+    } else {
+        false
+    };
+    let _brackets_unneeded = if unsafe { no(4) } & 1 != 0 { true } else { false };
+    fn foo() -> bool {
+        // parentheses are needed here
+        if unsafe { no(4) } & 1 != 0 { true } else { false }
+    }
+
+    foo()
+}
index 1fa12add16739df015d536a9a49c242450473809..22c0a7bb491c6d495d47a9b418348ff85a79c185 100644 (file)
@@ -31,7 +31,7 @@ LL | |     };
    | |_____^ help: you can reduce it to: `!(x && y)`
 
 error: this if-then-else expression returns a bool literal
-  --> $DIR/fixable.rs:71:5
+  --> $DIR/fixable.rs:72:5
    |
 LL | /     if x {
 LL | |         return true;
@@ -41,7 +41,7 @@ LL | |     };
    | |_____^ help: you can reduce it to: `return x`
 
 error: this if-then-else expression returns a bool literal
-  --> $DIR/fixable.rs:79:5
+  --> $DIR/fixable.rs:80:5
    |
 LL | /     if x {
 LL | |         return false;
@@ -51,7 +51,7 @@ LL | |     };
    | |_____^ help: you can reduce it to: `return !x`
 
 error: this if-then-else expression returns a bool literal
-  --> $DIR/fixable.rs:87:5
+  --> $DIR/fixable.rs:88:5
    |
 LL | /     if x && y {
 LL | |         return true;
@@ -61,7 +61,7 @@ LL | |     };
    | |_____^ help: you can reduce it to: `return x && y`
 
 error: this if-then-else expression returns a bool literal
-  --> $DIR/fixable.rs:95:5
+  --> $DIR/fixable.rs:96:5
    |
 LL | /     if x && y {
 LL | |         return false;
@@ -71,7 +71,7 @@ LL | |     };
    | |_____^ help: you can reduce it to: `return !(x && y)`
 
 error: equality checks against true are unnecessary
-  --> $DIR/fixable.rs:103:8
+  --> $DIR/fixable.rs:104:8
    |
 LL |     if x == true {};
    |        ^^^^^^^^^ help: try simplifying it as shown: `x`
@@ -79,25 +79,25 @@ LL |     if x == true {};
    = note: `-D clippy::bool-comparison` implied by `-D warnings`
 
 error: equality checks against false can be replaced by a negation
-  --> $DIR/fixable.rs:107:8
+  --> $DIR/fixable.rs:108:8
    |
 LL |     if x == false {};
    |        ^^^^^^^^^^ help: try simplifying it as shown: `!x`
 
 error: equality checks against true are unnecessary
-  --> $DIR/fixable.rs:117:8
+  --> $DIR/fixable.rs:118:8
    |
 LL |     if x == true {};
    |        ^^^^^^^^^ help: try simplifying it as shown: `x`
 
 error: equality checks against false can be replaced by a negation
-  --> $DIR/fixable.rs:118:8
+  --> $DIR/fixable.rs:119:8
    |
 LL |     if x == false {};
    |        ^^^^^^^^^^ help: try simplifying it as shown: `!x`
 
 error: this if-then-else expression returns a bool literal
-  --> $DIR/fixable.rs:127:12
+  --> $DIR/fixable.rs:128:12
    |
 LL |       } else if returns_bool() {
    |  ____________^
@@ -107,5 +107,27 @@ LL | |         true
 LL | |     };
    | |_____^ help: you can reduce it to: `{ !returns_bool() }`
 
-error: aborting due to 12 previous errors
+error: this if-then-else expression returns a bool literal
+  --> $DIR/fixable.rs:141:5
+   |
+LL | /     if unsafe { no(4) } & 1 != 0 {
+LL | |         true
+LL | |     } else {
+LL | |         false
+LL | |     };
+   | |_____^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)`
+
+error: this if-then-else expression returns a bool literal
+  --> $DIR/fixable.rs:146:30
+   |
+LL |     let _brackets_unneeded = if unsafe { no(4) } & 1 != 0 { true } else { false };
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `unsafe { no(4) } & 1 != 0`
+
+error: this if-then-else expression returns a bool literal
+  --> $DIR/fixable.rs:149:9
+   |
+LL |         if unsafe { no(4) } & 1 != 0 { true } else { false }
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)`
+
+error: aborting due to 15 previous errors
 
diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs
new file mode 100644 (file)
index 0000000..6fdb4cc
--- /dev/null
@@ -0,0 +1,167 @@
+#![allow(unused)]
+
+fn main() {
+    let a;
+    let n = 1;
+    match n {
+        1 => a = "one",
+        _ => {
+            a = "two";
+        },
+    }
+
+    let b;
+    if n == 3 {
+        b = "four";
+    } else {
+        b = "five"
+    }
+
+    let c;
+    if let Some(n) = Some(5) {
+        c = n;
+    } else {
+        c = -50;
+    }
+
+    let d;
+    if true {
+        let temp = 5;
+        d = temp;
+    } else {
+        d = 15;
+    }
+
+    let e;
+    if true {
+        e = format!("{} {}", a, b);
+    } else {
+        e = format!("{}", c);
+    }
+
+    println!("{}", a);
+}
+
+async fn in_async() -> &'static str {
+    async fn f() -> &'static str {
+        "one"
+    }
+
+    let a;
+    let n = 1;
+    match n {
+        1 => a = f().await,
+        _ => {
+            a = "two";
+        },
+    }
+
+    a
+}
+
+const fn in_const() -> &'static str {
+    const fn f() -> &'static str {
+        "one"
+    }
+
+    let a;
+    let n = 1;
+    match n {
+        1 => a = f(),
+        _ => {
+            a = "two";
+        },
+    }
+
+    a
+}
+
+fn does_not_lint() {
+    let z;
+    if false {
+        z = 1;
+    }
+
+    let x;
+    let y;
+    if true {
+        x = 1;
+    } else {
+        y = 1;
+    }
+
+    let mut x;
+    if true {
+        x = 5;
+        x = 10 / x;
+    } else {
+        x = 2;
+    }
+
+    let x;
+    let _ = match 1 {
+        1 => x = 10,
+        _ => x = 20,
+    };
+
+    // using tuples would be possible, but not always preferable
+    let x;
+    let y;
+    if true {
+        x = 1;
+        y = 2;
+    } else {
+        x = 3;
+        y = 4;
+    }
+
+    // could match with a smarter heuristic to avoid multiple assignments
+    let x;
+    if true {
+        let mut y = 5;
+        y = 6;
+        x = y;
+    } else {
+        x = 2;
+    }
+
+    let (x, y);
+    if true {
+        x = 1;
+    } else {
+        x = 2;
+    }
+    y = 3;
+
+    macro_rules! assign {
+        ($i:ident) => {
+            $i = 1;
+        };
+    }
+    let x;
+    assign!(x);
+
+    let x;
+    if true {
+        assign!(x);
+    } else {
+        x = 2;
+    }
+
+    macro_rules! in_macro {
+        () => {
+            let x;
+            x = 1;
+
+            let x;
+            if true {
+                x = 1;
+            } else {
+                x = 2;
+            }
+        };
+    }
+    in_macro!();
+
+    println!("{}", x);
+}
diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr
new file mode 100644 (file)
index 0000000..cbb7538
--- /dev/null
@@ -0,0 +1,150 @@
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:4:5
+   |
+LL |     let a;
+   |     ^^^^^^
+   |
+   = note: `-D clippy::needless-late-init` implied by `-D warnings`
+help: declare `a` here
+   |
+LL |     let a = match n {
+   |     +++++++
+help: remove the assignments from the `match` arms
+   |
+LL ~         1 => "one",
+LL |         _ => {
+LL ~             "two"
+   |
+help: add a semicolon after the `match` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:13:5
+   |
+LL |     let b;
+   |     ^^^^^^
+   |
+help: declare `b` here
+   |
+LL |     let b = if n == 3 {
+   |     +++++++
+help: remove the assignments from the branches
+   |
+LL ~         "four"
+LL |     } else {
+LL ~         "five"
+   |
+help: add a semicolon after the `if` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:20:5
+   |
+LL |     let c;
+   |     ^^^^^^
+   |
+help: declare `c` here
+   |
+LL |     let c = if let Some(n) = Some(5) {
+   |     +++++++
+help: remove the assignments from the branches
+   |
+LL ~         n
+LL |     } else {
+LL ~         -50
+   |
+help: add a semicolon after the `if` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:27:5
+   |
+LL |     let d;
+   |     ^^^^^^
+   |
+help: declare `d` here
+   |
+LL |     let d = if true {
+   |     +++++++
+help: remove the assignments from the branches
+   |
+LL ~         temp
+LL |     } else {
+LL ~         15
+   |
+help: add a semicolon after the `if` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:35:5
+   |
+LL |     let e;
+   |     ^^^^^^
+   |
+help: declare `e` here
+   |
+LL |     let e = if true {
+   |     +++++++
+help: remove the assignments from the branches
+   |
+LL ~         format!("{} {}", a, b)
+LL |     } else {
+LL ~         format!("{}", c)
+   |
+help: add a semicolon after the `if` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:50:5
+   |
+LL |     let a;
+   |     ^^^^^^
+   |
+help: declare `a` here
+   |
+LL |     let a = match n {
+   |     +++++++
+help: remove the assignments from the `match` arms
+   |
+LL ~         1 => f().await,
+LL |         _ => {
+LL ~             "two"
+   |
+help: add a semicolon after the `match` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init.rs:67:5
+   |
+LL |     let a;
+   |     ^^^^^^
+   |
+help: declare `a` here
+   |
+LL |     let a = match n {
+   |     +++++++
+help: remove the assignments from the `match` arms
+   |
+LL ~         1 => f(),
+LL |         _ => {
+LL ~             "two"
+   |
+help: add a semicolon after the `match` expression
+   |
+LL |     };
+   |      +
+
+error: aborting due to 7 previous errors
+
diff --git a/tests/ui/needless_late_init_fixable.fixed b/tests/ui/needless_late_init_fixable.fixed
new file mode 100644 (file)
index 0000000..32d5d04
--- /dev/null
@@ -0,0 +1,38 @@
+// run-rustfix
+
+#![allow(unused, clippy::assign_op_pattern)]
+
+fn main() {
+    
+    let a = "zero";
+
+    
+    
+    let b = 1;
+    let c = 2;
+
+    
+    let d: usize = 1;
+
+    
+    let mut e = 1;
+    e = 2;
+
+    
+    let f = match 1 {
+        1 => "three",
+        _ => return,
+    }; // has semi
+
+    
+    let g: usize = if true {
+        5
+    } else {
+        panic!();
+    };
+
+    
+    let h = format!("{}", e);
+
+    println!("{}", a);
+}
diff --git a/tests/ui/needless_late_init_fixable.rs b/tests/ui/needless_late_init_fixable.rs
new file mode 100644 (file)
index 0000000..6bc85f6
--- /dev/null
@@ -0,0 +1,38 @@
+// run-rustfix
+
+#![allow(unused, clippy::assign_op_pattern)]
+
+fn main() {
+    let a;
+    a = "zero";
+
+    let b;
+    let c;
+    b = 1;
+    c = 2;
+
+    let d: usize;
+    d = 1;
+
+    let mut e;
+    e = 1;
+    e = 2;
+
+    let f;
+    match 1 {
+        1 => f = "three",
+        _ => return,
+    }; // has semi
+
+    let g: usize;
+    if true {
+        g = 5;
+    } else {
+        panic!();
+    }
+
+    let h;
+    h = format!("{}", e);
+
+    println!("{}", a);
+}
diff --git a/tests/ui/needless_late_init_fixable.stderr b/tests/ui/needless_late_init_fixable.stderr
new file mode 100644 (file)
index 0000000..a0ce4f8
--- /dev/null
@@ -0,0 +1,103 @@
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:6:5
+   |
+LL |     let a;
+   |     ^^^^^^
+   |
+   = note: `-D clippy::needless-late-init` implied by `-D warnings`
+help: declare `a` here
+   |
+LL |     let a = "zero";
+   |     ~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:9:5
+   |
+LL |     let b;
+   |     ^^^^^^
+   |
+help: declare `b` here
+   |
+LL |     let b = 1;
+   |     ~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:10:5
+   |
+LL |     let c;
+   |     ^^^^^^
+   |
+help: declare `c` here
+   |
+LL |     let c = 2;
+   |     ~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:14:5
+   |
+LL |     let d: usize;
+   |     ^^^^^^^^^^^^^
+   |
+help: declare `d` here
+   |
+LL |     let d: usize = 1;
+   |     ~~~~~~~~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:17:5
+   |
+LL |     let mut e;
+   |     ^^^^^^^^^^
+   |
+help: declare `e` here
+   |
+LL |     let mut e = 1;
+   |     ~~~~~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:21:5
+   |
+LL |     let f;
+   |     ^^^^^^
+   |
+help: declare `f` here
+   |
+LL |     let f = match 1 {
+   |     +++++++
+help: remove the assignments from the `match` arms
+   |
+LL |         1 => "three",
+   |              ~~~~~~~
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:27:5
+   |
+LL |     let g: usize;
+   |     ^^^^^^^^^^^^^
+   |
+help: declare `g` here
+   |
+LL |     let g: usize = if true {
+   |     ++++++++++++++
+help: remove the assignments from the branches
+   |
+LL |         5
+   |
+help: add a semicolon after the `if` expression
+   |
+LL |     };
+   |      +
+
+error: unneeded late initalization
+  --> $DIR/needless_late_init_fixable.rs:34:5
+   |
+LL |     let h;
+   |     ^^^^^^
+   |
+help: declare `h` here
+   |
+LL |     let h = format!("{}", e);
+   |     ~~~~~
+
+error: aborting due to 8 previous errors
+
index eca7f5e5655913785f3e98cf94d3a179549e74ba..828248d922f890837bd239447d494039eecaff3a 100644 (file)
@@ -69,6 +69,11 @@ pub enum MyOption<T> {
 
 unsafe impl<T> Send for MyOption<T> {}
 
+// Test types that contain `NonNull` instead of raw pointers (#8045)
+pub struct WrappedNonNull(UnsafeCell<NonNull<()>>);
+
+unsafe impl Send for WrappedNonNull {}
+
 // Multiple type parameters
 pub struct MultiParam<A, B> {
     vec: Vec<(A, B)>,
index 8b8a1d16d9bb96172bd747f011814853230725b9..60df4e226e4fa47078c43dc3bb66e1ff30e3e6e3 100644 (file)
-error: this implementation is unsound, as some fields in `RingBuffer<T>` are `!Send`
+error: some fields in `RingBuffer<T>` are not safe to be sent to another thread
   --> $DIR/non_send_fields_in_send_ty.rs:16:1
    |
 LL | unsafe impl<T> Send for RingBuffer<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::non-send-fields-in-send-ty` implied by `-D warnings`
-note: the type of field `data` is `!Send`
+note: it is not safe to send field `data` to another thread
   --> $DIR/non_send_fields_in_send_ty.rs:11:5
    |
 LL |     data: Vec<UnsafeCell<T>>,
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
    = help: add bounds on type parameter `T` that satisfy `Vec<UnsafeCell<T>>: Send`
 
-error: this implementation is unsound, as some fields in `MvccRwLock<T>` are `!Send`
+error: some fields in `MvccRwLock<T>` are not safe to be sent to another thread
   --> $DIR/non_send_fields_in_send_ty.rs:24:1
    |
 LL | unsafe impl<T> Send for MvccRwLock<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: the type of field `lock` is `!Send`
+note: it is not safe to send field `lock` to another thread
   --> $DIR/non_send_fields_in_send_ty.rs:21:5
    |
 LL |     lock: Mutex<Box<T>>,
    |     ^^^^^^^^^^^^^^^^^^^
    = help: add bounds on type parameter `T` that satisfy `Mutex<Box<T>>: Send`
 
-error: this implementation is unsound, as some fields in `ArcGuard<RC, T>` are `!Send`
+error: some fields in `ArcGuard<RC, T>` are not safe to be sent to another thread
   --> $DIR/non_send_fields_in_send_ty.rs:32:1
    |
 LL | unsafe impl<RC, T: Send> Send for ArcGuard<RC, T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: the type of field `head` is `!Send`
+note: it is not safe to send field `head` to another thread
   --> $DIR/non_send_fields_in_send_ty.rs:29:5
    |
 LL |     head: Arc<RC>,
    |     ^^^^^^^^^^^^^
    = help: add bounds on type parameter `RC` that satisfy `Arc<RC>: Send`
 
-error: this implementation is unsound, as some fields in `DeviceHandle<T>` are `!Send`
+error: some fields in `DeviceHandle<T>` are not safe to be sent to another thread
   --> $DIR/non_send_fields_in_send_ty.rs:48:1
    |
 LL | unsafe impl<T: UsbContext> Send for DeviceHandle<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: the type of field `context` is `!Send`
+note: it is not safe to send field `context` to another thread
   --> $DIR/non_send_fields_in_send_ty.rs:44:5
    |
 LL |     context: T,
    |     ^^^^^^^^^^
    = help: add `T: Send` bound in `Send` impl
 
-error: this implementation is unsound, as some fields in `NoGeneric` are `!Send`
+error: some fields in `NoGeneric` are not safe to be sent to another thread
   --> $DIR/non_send_fields_in_send_ty.rs:55:1
    |
 LL | unsafe impl Send for NoGeneric {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: the type of field `rc_is_not_send` is `!Send`
+note: it is not safe to send field `rc_is_not_send` to another thread
   --> $DIR/non_send_fields_in_send_ty.rs:52:5
    |
 LL |     rc_is_not_send: Rc<String>,
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: use a thread-safe type that implements `Send`
 
-error: this implementation is unsound, as some fields in `MultiField<T>` are `!Send`
+error: some fields in `MultiField<T>` are not safe to be sent to another thread
   --> $DIR/non_send_fields_in_send_ty.rs:63:1
    |
 LL | unsafe impl<T> Send for MultiField<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: the type of field `field1` is `!Send`
+note: it is not safe to send field `field1` to another thread
   --> $DIR/non_send_fields_in_send_ty.rs:58:5
    |
 LL |     field1: T,
    |     ^^^^^^^^^
    = help: add `T: Send` bound in `Send` impl
-note: the type of field `field2` is `!Send`
+note: it is not safe to send field `field2` to another thread
   --> $DIR/non_send_fields_in_send_ty.rs:59:5
    |
 LL |     field2: T,
    |     ^^^^^^^^^
    = help: add `T: Send` bound in `Send` impl
-note: the type of field `field3` is `!Send`
+note: it is not safe to send field `field3` to another thread
   --> $DIR/non_send_fields_in_send_ty.rs:60:5
    |
 LL |     field3: T,
    |     ^^^^^^^^^
    = help: add `T: Send` bound in `Send` impl
 
-error: this implementation is unsound, as some fields in `MyOption<T>` are `!Send`
+error: some fields in `MyOption<T>` are not safe to be sent to another thread
   --> $DIR/non_send_fields_in_send_ty.rs:70:1
    |
 LL | unsafe impl<T> Send for MyOption<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: the type of field `0` is `!Send`
+note: it is not safe to send field `0` to another thread
   --> $DIR/non_send_fields_in_send_ty.rs:66:12
    |
 LL |     MySome(T),
    |            ^
    = help: add `T: Send` bound in `Send` impl
 
-error: this implementation is unsound, as some fields in `MultiParam<A, B>` are `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:77:1
+error: some fields in `MultiParam<A, B>` are not safe to be sent to another thread
+  --> $DIR/non_send_fields_in_send_ty.rs:82:1
    |
 LL | unsafe impl<A, B> Send for MultiParam<A, B> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: the type of field `vec` is `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:74:5
+note: it is not safe to send field `vec` to another thread
+  --> $DIR/non_send_fields_in_send_ty.rs:79:5
    |
 LL |     vec: Vec<(A, B)>,
    |     ^^^^^^^^^^^^^^^^
    = help: add bounds on type parameters `A, B` that satisfy `Vec<(A, B)>: Send`
 
-error: this implementation is unsound, as some fields in `HeuristicTest` are `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:95:1
+error: some fields in `HeuristicTest` are not safe to be sent to another thread
+  --> $DIR/non_send_fields_in_send_ty.rs:100:1
    |
 LL | unsafe impl Send for HeuristicTest {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: the type of field `field4` is `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:90:5
+note: it is not safe to send field `field4` to another thread
+  --> $DIR/non_send_fields_in_send_ty.rs:95:5
    |
 LL |     field4: (*const NonSend, Rc<u8>),
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = help: use a thread-safe type that implements `Send`
 
-error: this implementation is unsound, as some fields in `AttrTest3<T>` are `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:114:1
+error: some fields in `AttrTest3<T>` are not safe to be sent to another thread
+  --> $DIR/non_send_fields_in_send_ty.rs:119:1
    |
 LL | unsafe impl<T> Send for AttrTest3<T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: the type of field `0` is `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:109:11
+note: it is not safe to send field `0` to another thread
+  --> $DIR/non_send_fields_in_send_ty.rs:114:11
    |
 LL |     Enum2(T),
    |           ^
    = help: add `T: Send` bound in `Send` impl
 
-error: this implementation is unsound, as some fields in `Complex<P, u32>` are `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:122:1
+error: some fields in `Complex<P, u32>` are not safe to be sent to another thread
+  --> $DIR/non_send_fields_in_send_ty.rs:127:1
    |
 LL | unsafe impl<P> Send for Complex<P, u32> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: the type of field `field1` is `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:118:5
+note: it is not safe to send field `field1` to another thread
+  --> $DIR/non_send_fields_in_send_ty.rs:123:5
    |
 LL |     field1: A,
    |     ^^^^^^^^^
    = help: add `P: Send` bound in `Send` impl
 
-error: this implementation is unsound, as some fields in `Complex<Q, MutexGuard<'static, bool>>` are `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:125:1
+error: some fields in `Complex<Q, MutexGuard<'static, bool>>` are not safe to be sent to another thread
+  --> $DIR/non_send_fields_in_send_ty.rs:130:1
    |
 LL | unsafe impl<Q: Send> Send for Complex<Q, MutexGuard<'static, bool>> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: the type of field `field2` is `!Send`
-  --> $DIR/non_send_fields_in_send_ty.rs:119:5
+note: it is not safe to send field `field2` to another thread
+  --> $DIR/non_send_fields_in_send_ty.rs:124:5
    |
 LL |     field2: B,
    |     ^^^^^^^^^
index 642c77460a3407076ed9be98fa17eeb94feb1196..0141fb7856d06abf4a27857b45e9a86f756ca4f3 100644 (file)
@@ -1,5 +1,6 @@
 // aux-build:macro_rules.rs
 #![warn(clippy::option_env_unwrap)]
+#![allow(clippy::map_flatten)]
 
 #[macro_use]
 extern crate macro_rules;
index e6a58b0b2b752f3cca87b1a42816c2900a37953b..885ac096cc8f763088d577bdcd7cfed630d71bc0 100644 (file)
@@ -1,5 +1,5 @@
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:17:13
+  --> $DIR/option_env_unwrap.rs:18:13
    |
 LL |     let _ = option_env!("PATH").unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     let _ = option_env!("PATH").unwrap();
    = help: consider using the `env!` macro instead
 
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:18:13
+  --> $DIR/option_env_unwrap.rs:19:13
    |
 LL |     let _ = option_env!("PATH").expect("environment variable PATH isn't set");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL |     let _ = option_env!("PATH").expect("environment variable PATH isn't set
    = help: consider using the `env!` macro instead
 
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:9:9
+  --> $DIR/option_env_unwrap.rs:10:9
    |
 LL |         option_env!($env).unwrap()
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -28,7 +28,7 @@ LL |     let _ = option_env_unwrap!("PATH");
    = note: this error originates in the macro `option_env_unwrap` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:12:9
+  --> $DIR/option_env_unwrap.rs:13:9
    |
 LL |         option_env!($env).expect($message)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -40,7 +40,7 @@ LL |     let _ = option_env_unwrap!("PATH", "environment variable PATH isn't set
    = note: this error originates in the macro `option_env_unwrap` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:21:13
+  --> $DIR/option_env_unwrap.rs:22:13
    |
 LL |     let _ = option_env_unwrap_external!("PATH");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -49,7 +49,7 @@ LL |     let _ = option_env_unwrap_external!("PATH");
    = note: this error originates in the macro `option_env_unwrap_external` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: this will panic at run-time if the environment variable doesn't exist at compile-time
-  --> $DIR/option_env_unwrap.rs:22:13
+  --> $DIR/option_env_unwrap.rs:23:13
    |
 LL |     let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index f9d1825ade05483c44351a1527efcbf77cdf786a..b20f73f3110dee44367854c86225c6de50f81372 100644 (file)
@@ -1,8 +1,6 @@
-#![warn(clippy::option_filter_map)]
 // run-rustfix
-fn odds_out(x: i32) -> Option<i32> {
-    if x % 2 == 0 { Some(x) } else { None }
-}
+#![warn(clippy::option_filter_map)]
+#![allow(clippy::map_flatten)]
 
 fn main() {
     let _ = Some(Some(1)).flatten();
@@ -21,3 +19,7 @@ fn main() {
         .map(odds_out)
         .flatten();
 }
+
+fn odds_out(x: i32) -> Option<i32> {
+    if x % 2 == 0 { Some(x) } else { None }
+}
index 588e1ccccce20149fc5750b4c9c57575cf846f7d..7abaaa0fb83b35883da6233109796da4ba708502 100644 (file)
@@ -1,8 +1,6 @@
-#![warn(clippy::option_filter_map)]
 // run-rustfix
-fn odds_out(x: i32) -> Option<i32> {
-    if x % 2 == 0 { Some(x) } else { None }
-}
+#![warn(clippy::option_filter_map)]
+#![allow(clippy::map_flatten)]
 
 fn main() {
     let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap);
@@ -23,3 +21,7 @@ fn main() {
         .filter(|o| o.is_some())
         .map(|o| o.unwrap());
 }
+
+fn odds_out(x: i32) -> Option<i32> {
+    if x % 2 == 0 { Some(x) } else { None }
+}
index 31a82969d5a1c9bd0a9e4716a14b2f98e1c9a2a7..4a030ac9ab045440cbc69b85688cb3d0159d1ccb 100644 (file)
@@ -1,5 +1,5 @@
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:8:27
+  --> $DIR/option_filter_map.rs:6:27
    |
 LL |     let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap);
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
@@ -7,37 +7,37 @@ LL |     let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap);
    = note: `-D clippy::option-filter-map` implied by `-D warnings`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:9:27
+  --> $DIR/option_filter_map.rs:7:27
    |
 LL |     let _ = Some(Some(1)).filter(|o| o.is_some()).map(|o| o.unwrap());
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:10:35
+  --> $DIR/option_filter_map.rs:8:35
    |
 LL |     let _ = Some(1).map(odds_out).filter(Option::is_some).map(Option::unwrap);
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:11:35
+  --> $DIR/option_filter_map.rs:9:35
    |
 LL |     let _ = Some(1).map(odds_out).filter(|o| o.is_some()).map(|o| o.unwrap());
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:13:39
+  --> $DIR/option_filter_map.rs:11:39
    |
 LL |     let _ = vec![Some(1)].into_iter().filter(Option::is_some).map(Option::unwrap);
    |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:14:39
+  --> $DIR/option_filter_map.rs:12:39
    |
 LL |     let _ = vec![Some(1)].into_iter().filter(|o| o.is_some()).map(|o| o.unwrap());
    |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:18:10
+  --> $DIR/option_filter_map.rs:16:10
    |
 LL |           .filter(Option::is_some)
    |  __________^
@@ -45,7 +45,7 @@ LL | |         .map(Option::unwrap);
    | |____________________________^ help: consider using `flatten` instead: `flatten()`
 
 error: `filter` for `Some` followed by `unwrap`
-  --> $DIR/option_filter_map.rs:23:10
+  --> $DIR/option_filter_map.rs:21:10
    |
 LL |           .filter(|o| o.is_some())
    |  __________^
index ce3093c542ae0b7bfad86c1c10e665db25761cfa..7790c816481d1b78bbc2320c3b1cf57d36a3242b 100644 (file)
@@ -86,6 +86,19 @@ fn pattern_to_vec(pattern: &str) -> Vec<String> {
         .collect::<Vec<_>>()
 }
 
+enum DummyEnum {
+    One(u8),
+    Two,
+}
+
+// should not warn since there is a compled complex subpat
+// see #7991
+fn complex_subpat() -> DummyEnum {
+    let x = Some(DummyEnum::One(1));
+    let _ = if let Some(_one @ DummyEnum::One(..)) = x { 1 } else { 2 };
+    DummyEnum::Two
+}
+
 fn main() {
     let optional = Some(5);
     let _ = optional.map_or(5, |x| x + 2);
@@ -159,4 +172,5 @@ fn main() {
     }
 
     let _ = pattern_to_vec("hello world");
+    let _ = complex_subpat();
 }
index c228b2f43d10c41420440776d50a9f6c9c47d945..3d9f76ee4a6b570430f18538518b18cc33a47973 100644 (file)
@@ -109,6 +109,19 @@ fn pattern_to_vec(pattern: &str) -> Vec<String> {
         .collect::<Vec<_>>()
 }
 
+enum DummyEnum {
+    One(u8),
+    Two,
+}
+
+// should not warn since there is a compled complex subpat
+// see #7991
+fn complex_subpat() -> DummyEnum {
+    let x = Some(DummyEnum::One(1));
+    let _ = if let Some(_one @ DummyEnum::One(..)) = x { 1 } else { 2 };
+    DummyEnum::Two
+}
+
 fn main() {
     let optional = Some(5);
     let _ = if let Some(x) = optional { x + 2 } else { 5 };
@@ -188,4 +201,5 @@ async fn _f2() {
     }
 
     let _ = pattern_to_vec("hello world");
+    let _ = complex_subpat();
 }
index 4e64cd7cdb1d649a73edfaedf1a97bce0ac0b742..546131ceb5b633688ae9113df9a96f41e373fcd1 100644 (file)
@@ -153,13 +153,13 @@ LL | |             }
    | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:114:13
+  --> $DIR/option_if_let_else.rs:127:13
    |
 LL |     let _ = if let Some(x) = optional { x + 2 } else { 5 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:123:13
+  --> $DIR/option_if_let_else.rs:136:13
    |
 LL |       let _ = if let Some(x) = Some(0) {
    |  _____________^
@@ -181,13 +181,13 @@ LL ~         });
    |
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:151:13
+  --> $DIR/option_if_let_else.rs:164:13
    |
 LL |     let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)`
 
 error: use Option::map_or instead of an if let/else
-  --> $DIR/option_if_let_else.rs:155:13
+  --> $DIR/option_if_let_else.rs:168:13
    |
 LL |       let _ = if let Some(x) = Some(0) {
    |  _____________^
index 9b4f2f1f57934dbd8332c2932bfed5f30836626a..55a8c26215e9e3356542133f08af2986df90b980 100644 (file)
@@ -37,4 +37,13 @@ fn should_not_lint() {
         Some(_) => (),
         _ => (),
     }
+
+    const FOO: &str = "foo";
+
+    fn foo(s: &str) -> i32 {
+        match s {
+            FOO => 1,
+            _ => 0,
+        }
+    }
 }
index e93469e5f556bd55737c0628961e540e7caf5be1..13ce0f32d4bb14b7e6f2777be15ee32fc6e7e52b 100644 (file)
@@ -136,6 +136,24 @@ fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
     Ok(y)
 }
 
+// see issue #8019
+pub enum NotOption {
+    None,
+    First,
+    AfterFirst,
+}
+
+fn obj(_: i32) -> Result<(), NotOption> {
+    Err(NotOption::First)
+}
+
+fn f() -> NotOption {
+    if obj(2).is_err() {
+        return NotOption::None;
+    }
+    NotOption::First
+}
+
 fn main() {
     some_func(Some(42));
     some_func(None);
@@ -157,4 +175,5 @@ fn main() {
     func();
 
     let _ = result_func(Ok(42));
+    let _ = f();
 }
index dd179e9bee8f87d57c6623617d0b646fb154c684..60590fd931188042efee4d99058ea3dac6ecd219 100644 (file)
@@ -168,6 +168,24 @@ fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
     Ok(y)
 }
 
+// see issue #8019
+pub enum NotOption {
+    None,
+    First,
+    AfterFirst,
+}
+
+fn obj(_: i32) -> Result<(), NotOption> {
+    Err(NotOption::First)
+}
+
+fn f() -> NotOption {
+    if obj(2).is_err() {
+        return NotOption::None;
+    }
+    NotOption::First
+}
+
 fn main() {
     some_func(Some(42));
     some_func(None);
@@ -189,4 +207,5 @@ fn main() {
     func();
 
     let _ = result_func(Ok(42));
+    let _ = f();
 }
index 1f4864b72895bf10142574e214bdfe352eba896a..5612827bd39331f5804d5866c8c534ceab0d1436 100644 (file)
@@ -1,6 +1,7 @@
 // non rustfixable, see redundant_closure_call_fixable.rs
 
 #![warn(clippy::redundant_closure_call)]
+#![allow(clippy::needless_late_init)]
 
 fn main() {
     let mut i = 1;
index 7c8865f1bd375e02a14c5ab99fadd78c88c0f83e..4eca43a2b599ab149a41f67dbd2f7ab875b277d4 100644 (file)
@@ -1,5 +1,5 @@
 error: closure called just once immediately after it was declared
-  --> $DIR/redundant_closure_call_late.rs:15:5
+  --> $DIR/redundant_closure_call_late.rs:16:5
    |
 LL |     i = redun_closure();
    |     ^^^^^^^^^^^^^^^^^^^
@@ -7,13 +7,13 @@ LL |     i = redun_closure();
    = note: `-D clippy::redundant-closure-call` implied by `-D warnings`
 
 error: closure called just once immediately after it was declared
-  --> $DIR/redundant_closure_call_late.rs:19:5
+  --> $DIR/redundant_closure_call_late.rs:20:5
    |
 LL |     i = shadowed_closure();
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
 error: closure called just once immediately after it was declared
-  --> $DIR/redundant_closure_call_late.rs:21:5
+  --> $DIR/redundant_closure_call_late.rs:22:5
    |
 LL |     i = shadowed_closure();
    |     ^^^^^^^^^^^^^^^^^^^^^^
index e8a6e940c01cdbebd76dfcceb066bafea11d97cc..64f566735cd95c366eb04b65ffdbe563ea57ee1e 100644 (file)
@@ -1,5 +1,5 @@
 #![warn(clippy::redundant_else)]
-#![allow(clippy::needless_return, clippy::if_same_then_else)]
+#![allow(clippy::needless_return, clippy::if_same_then_else, clippy::needless_late_init)]
 
 fn main() {
     loop {
index 0f9139b41b99dae76e7d06f5d1f8d900ec82b784..c32c3dd9880f6af59f7578462a76149179560f54 100644 (file)
@@ -1,4 +1,4 @@
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
   --> $DIR/same_name_method.rs:20:13
    |
 LL |             fn foo() {}
@@ -11,7 +11,7 @@ note: existing `foo` defined here
 LL |             fn foo() {}
    |             ^^^^^^^^^^^
 
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
   --> $DIR/same_name_method.rs:44:13
    |
 LL |             fn foo() {}
@@ -23,7 +23,7 @@ note: existing `foo` defined here
 LL |             fn foo() {}
    |             ^^^^^^^^^^^
 
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
   --> $DIR/same_name_method.rs:58:13
    |
 LL |             fn foo() {}
@@ -35,7 +35,7 @@ note: existing `foo` defined here
 LL |         impl T1 for S {}
    |         ^^^^^^^^^^^^^^^^
 
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
   --> $DIR/same_name_method.rs:70:13
    |
 LL |             fn foo() {}
@@ -47,7 +47,7 @@ note: existing `foo` defined here
 LL |         impl T1 for S {}
    |         ^^^^^^^^^^^^^^^^
 
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
   --> $DIR/same_name_method.rs:34:13
    |
 LL |             fn clone() {}
index 72bc6ef35d317b02f9c5300affc0e6923fdd7790..72f335153c1b1282ec812f1fe09b56d6f97a2e48 100644 (file)
@@ -36,6 +36,9 @@ fn main() {
     // check that we don't lint if `find()` is called with
     // `Pattern` that is not a string
     let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_some();
+
+    let some_closure = |x: &u32| *x == 0;
+    let _ = (0..1).find(some_closure).is_some();
 }
 
 #[rustfmt::skip]
@@ -70,4 +73,7 @@ fn is_none() {
     // check that we don't lint if `find()` is called with
     // `Pattern` that is not a string
     let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_none();
+
+    let some_closure = |x: &u32| *x == 0;
+    let _ = (0..1).find(some_closure).is_none();
 }
index f3c758e451ef1436047b24be7d696156b4c075fb..54760545bcedc38cfc536fc63ea90b305048a1a4 100644 (file)
@@ -35,8 +35,14 @@ LL | |                    ).is_some();
    |
    = help: this is more succinctly expressed by calling `any()`
 
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some.rs:41:20
+   |
+LL |     let _ = (0..1).find(some_closure).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(some_closure)`
+
 error: called `is_none()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some.rs:48:13
+  --> $DIR/search_is_some.rs:51:13
    |
 LL |       let _ = v.iter().find(|&x| {
    |  _____________^
@@ -48,7 +54,7 @@ LL | |                    ).is_none();
    = help: this is more succinctly expressed by calling `any()` with negation
 
 error: called `is_none()` after searching an `Iterator` with `position`
-  --> $DIR/search_is_some.rs:54:13
+  --> $DIR/search_is_some.rs:57:13
    |
 LL |       let _ = v.iter().position(|&x| {
    |  _____________^
@@ -60,7 +66,7 @@ LL | |                    ).is_none();
    = help: this is more succinctly expressed by calling `any()` with negation
 
 error: called `is_none()` after searching an `Iterator` with `rposition`
-  --> $DIR/search_is_some.rs:60:13
+  --> $DIR/search_is_some.rs:63:13
    |
 LL |       let _ = v.iter().rposition(|&x| {
    |  _____________^
@@ -71,5 +77,11 @@ LL | |                    ).is_none();
    |
    = help: this is more succinctly expressed by calling `any()` with negation
 
-error: aborting due to 6 previous errors
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some.rs:78:13
+   |
+LL |     let _ = (0..1).find(some_closure).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(some_closure)`
+
+error: aborting due to 8 previous errors
 
diff --git a/tests/ui/search_is_some_fixable.fixed b/tests/ui/search_is_some_fixable.fixed
deleted file mode 100644 (file)
index 62ff16f..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-// run-rustfix
-#![allow(dead_code)]
-#![warn(clippy::search_is_some)]
-
-fn main() {
-    let v = vec![3, 2, 1, 0, -1, -2, -3];
-    let y = &&42;
-
-    // Check `find().is_some()`, single-line case.
-    let _ = v.iter().any(|x| *x < 0);
-    let _ = (0..1).any(|x| **y == x); // one dereference less
-    let _ = (0..1).any(|x| x == 0);
-    let _ = v.iter().any(|x| *x == 0);
-
-    // Check `position().is_some()`, single-line case.
-    let _ = v.iter().any(|&x| x < 0);
-
-    // Check `rposition().is_some()`, single-line case.
-    let _ = v.iter().any(|&x| x < 0);
-
-    let s1 = String::from("hello world");
-    let s2 = String::from("world");
-    // caller of `find()` is a `&`static str`
-    let _ = "hello world".contains("world");
-    let _ = "hello world".contains(&s2);
-    let _ = "hello world".contains(&s2[2..]);
-    // caller of `find()` is a `String`
-    let _ = s1.contains("world");
-    let _ = s1.contains(&s2);
-    let _ = s1.contains(&s2[2..]);
-    // caller of `find()` is slice of `String`
-    let _ = s1[2..].contains("world");
-    let _ = s1[2..].contains(&s2);
-    let _ = s1[2..].contains(&s2[2..]);
-}
-
-fn is_none() {
-    let v = vec![3, 2, 1, 0, -1, -2, -3];
-    let y = &&42;
-
-    // Check `find().is_none()`, single-line case.
-    let _ = !v.iter().any(|x| *x < 0);
-    let _ = !(0..1).any(|x| **y == x); // one dereference less
-    let _ = !(0..1).any(|x| x == 0);
-    let _ = !v.iter().any(|x| *x == 0);
-
-    // Check `position().is_none()`, single-line case.
-    let _ = !v.iter().any(|&x| x < 0);
-
-    // Check `rposition().is_none()`, single-line case.
-    let _ = !v.iter().any(|&x| x < 0);
-
-    let s1 = String::from("hello world");
-    let s2 = String::from("world");
-
-    // caller of `find()` is a `&`static str`
-    let _ = !"hello world".contains("world");
-    let _ = !"hello world".contains(&s2);
-    let _ = !"hello world".contains(&s2[2..]);
-    // caller of `find()` is a `String`
-    let _ = !s1.contains("world");
-    let _ = !s1.contains(&s2);
-    let _ = !s1.contains(&s2[2..]);
-    // caller of `find()` is slice of `String`
-    let _ = !s1[2..].contains("world");
-    let _ = !s1[2..].contains(&s2);
-    let _ = !s1[2..].contains(&s2[2..]);
-}
diff --git a/tests/ui/search_is_some_fixable.rs b/tests/ui/search_is_some_fixable.rs
deleted file mode 100644 (file)
index 8407f71..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-// run-rustfix
-#![allow(dead_code)]
-#![warn(clippy::search_is_some)]
-
-fn main() {
-    let v = vec![3, 2, 1, 0, -1, -2, -3];
-    let y = &&42;
-
-    // Check `find().is_some()`, single-line case.
-    let _ = v.iter().find(|&x| *x < 0).is_some();
-    let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
-    let _ = (0..1).find(|x| *x == 0).is_some();
-    let _ = v.iter().find(|x| **x == 0).is_some();
-
-    // Check `position().is_some()`, single-line case.
-    let _ = v.iter().position(|&x| x < 0).is_some();
-
-    // Check `rposition().is_some()`, single-line case.
-    let _ = v.iter().rposition(|&x| x < 0).is_some();
-
-    let s1 = String::from("hello world");
-    let s2 = String::from("world");
-    // caller of `find()` is a `&`static str`
-    let _ = "hello world".find("world").is_some();
-    let _ = "hello world".find(&s2).is_some();
-    let _ = "hello world".find(&s2[2..]).is_some();
-    // caller of `find()` is a `String`
-    let _ = s1.find("world").is_some();
-    let _ = s1.find(&s2).is_some();
-    let _ = s1.find(&s2[2..]).is_some();
-    // caller of `find()` is slice of `String`
-    let _ = s1[2..].find("world").is_some();
-    let _ = s1[2..].find(&s2).is_some();
-    let _ = s1[2..].find(&s2[2..]).is_some();
-}
-
-fn is_none() {
-    let v = vec![3, 2, 1, 0, -1, -2, -3];
-    let y = &&42;
-
-    // Check `find().is_none()`, single-line case.
-    let _ = v.iter().find(|&x| *x < 0).is_none();
-    let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
-    let _ = (0..1).find(|x| *x == 0).is_none();
-    let _ = v.iter().find(|x| **x == 0).is_none();
-
-    // Check `position().is_none()`, single-line case.
-    let _ = v.iter().position(|&x| x < 0).is_none();
-
-    // Check `rposition().is_none()`, single-line case.
-    let _ = v.iter().rposition(|&x| x < 0).is_none();
-
-    let s1 = String::from("hello world");
-    let s2 = String::from("world");
-
-    // caller of `find()` is a `&`static str`
-    let _ = "hello world".find("world").is_none();
-    let _ = "hello world".find(&s2).is_none();
-    let _ = "hello world".find(&s2[2..]).is_none();
-    // caller of `find()` is a `String`
-    let _ = s1.find("world").is_none();
-    let _ = s1.find(&s2).is_none();
-    let _ = s1.find(&s2[2..]).is_none();
-    // caller of `find()` is slice of `String`
-    let _ = s1[2..].find("world").is_none();
-    let _ = s1[2..].find(&s2).is_none();
-    let _ = s1[2..].find(&s2[2..]).is_none();
-}
diff --git a/tests/ui/search_is_some_fixable.stderr b/tests/ui/search_is_some_fixable.stderr
deleted file mode 100644 (file)
index bd1b695..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-error: called `is_some()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:10:22
-   |
-LL |     let _ = v.iter().find(|&x| *x < 0).is_some();
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x < 0)`
-   |
-   = note: `-D clippy::search-is-some` implied by `-D warnings`
-
-error: called `is_some()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:11:20
-   |
-LL |     let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)`
-
-error: called `is_some()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:12:20
-   |
-LL |     let _ = (0..1).find(|x| *x == 0).is_some();
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)`
-
-error: called `is_some()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:13:22
-   |
-LL |     let _ = v.iter().find(|x| **x == 0).is_some();
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)`
-
-error: called `is_some()` after searching an `Iterator` with `position`
-  --> $DIR/search_is_some_fixable.rs:16:22
-   |
-LL |     let _ = v.iter().position(|&x| x < 0).is_some();
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
-
-error: called `is_some()` after searching an `Iterator` with `rposition`
-  --> $DIR/search_is_some_fixable.rs:19:22
-   |
-LL |     let _ = v.iter().rposition(|&x| x < 0).is_some();
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:24:27
-   |
-LL |     let _ = "hello world".find("world").is_some();
-   |                           ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:25:27
-   |
-LL |     let _ = "hello world".find(&s2).is_some();
-   |                           ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:26:27
-   |
-LL |     let _ = "hello world".find(&s2[2..]).is_some();
-   |                           ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:28:16
-   |
-LL |     let _ = s1.find("world").is_some();
-   |                ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:29:16
-   |
-LL |     let _ = s1.find(&s2).is_some();
-   |                ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:30:16
-   |
-LL |     let _ = s1.find(&s2[2..]).is_some();
-   |                ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:32:21
-   |
-LL |     let _ = s1[2..].find("world").is_some();
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:33:21
-   |
-LL |     let _ = s1[2..].find(&s2).is_some();
-   |                     ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
-
-error: called `is_some()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:34:21
-   |
-LL |     let _ = s1[2..].find(&s2[2..]).is_some();
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
-
-error: called `is_none()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:42:13
-   |
-LL |     let _ = v.iter().find(|&x| *x < 0).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x < 0)`
-
-error: called `is_none()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:43:13
-   |
-LL |     let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| **y == x)`
-
-error: called `is_none()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:44:13
-   |
-LL |     let _ = (0..1).find(|x| *x == 0).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| x == 0)`
-
-error: called `is_none()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some_fixable.rs:45:13
-   |
-LL |     let _ = v.iter().find(|x| **x == 0).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x == 0)`
-
-error: called `is_none()` after searching an `Iterator` with `position`
-  --> $DIR/search_is_some_fixable.rs:48:13
-   |
-LL |     let _ = v.iter().position(|&x| x < 0).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
-
-error: called `is_none()` after searching an `Iterator` with `rposition`
-  --> $DIR/search_is_some_fixable.rs:51:13
-   |
-LL |     let _ = v.iter().rposition(|&x| x < 0).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:57:13
-   |
-LL |     let _ = "hello world".find("world").is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains("world")`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:58:13
-   |
-LL |     let _ = "hello world".find(&s2).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2)`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:59:13
-   |
-LL |     let _ = "hello world".find(&s2[2..]).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2[2..])`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:61:13
-   |
-LL |     let _ = s1.find("world").is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains("world")`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:62:13
-   |
-LL |     let _ = s1.find(&s2).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2)`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:63:13
-   |
-LL |     let _ = s1.find(&s2[2..]).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2[2..])`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:65:13
-   |
-LL |     let _ = s1[2..].find("world").is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains("world")`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:66:13
-   |
-LL |     let _ = s1[2..].find(&s2).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2)`
-
-error: called `is_none()` after calling `find()` on a string
-  --> $DIR/search_is_some_fixable.rs:67:13
-   |
-LL |     let _ = s1[2..].find(&s2[2..]).is_none();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2[2..])`
-
-error: aborting due to 30 previous errors
-
diff --git a/tests/ui/search_is_some_fixable_none.fixed b/tests/ui/search_is_some_fixable_none.fixed
new file mode 100644 (file)
index 0000000..6831fb2
--- /dev/null
@@ -0,0 +1,216 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+    let v = vec![3, 2, 1, 0, -1, -2, -3];
+    let y = &&42;
+
+    // Check `find().is_none()`, single-line case.
+    let _ = !v.iter().any(|x| *x < 0);
+    let _ = !(0..1).any(|x| **y == x); // one dereference less
+    let _ = !(0..1).any(|x| x == 0);
+    let _ = !v.iter().any(|x| *x == 0);
+    let _ = !(4..5).any(|x| x == 1 || x == 3 || x == 5);
+    let _ = !(1..3).any(|x| [1, 2, 3].contains(&x));
+    let _ = !(1..3).any(|x| x == 0 || [1, 2, 3].contains(&x));
+    let _ = !(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0);
+    let _ = !(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1);
+
+    // Check `position().is_none()`, single-line case.
+    let _ = !v.iter().any(|&x| x < 0);
+
+    // Check `rposition().is_none()`, single-line case.
+    let _ = !v.iter().any(|&x| x < 0);
+
+    let s1 = String::from("hello world");
+    let s2 = String::from("world");
+
+    // caller of `find()` is a `&`static str`
+    let _ = !"hello world".contains("world");
+    let _ = !"hello world".contains(&s2);
+    let _ = !"hello world".contains(&s2[2..]);
+    // caller of `find()` is a `String`
+    let _ = !s1.contains("world");
+    let _ = !s1.contains(&s2);
+    let _ = !s1.contains(&s2[2..]);
+    // caller of `find()` is slice of `String`
+    let _ = !s1[2..].contains("world");
+    let _ = !s1[2..].contains(&s2);
+    let _ = !s1[2..].contains(&s2[2..]);
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+    struct Player {
+        hand: Vec<usize>,
+    }
+    fn filter() {
+        let p = Player {
+            hand: vec![1, 2, 3, 4, 5],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|c| !filter_hand.iter().any(|cc| c == &cc))
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    struct PlayerTuple {
+        hand: Vec<(usize, char)>,
+    }
+    fn filter_tuple() {
+        let p = PlayerTuple {
+            hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|(c, _)| !filter_hand.iter().any(|cc| c == cc))
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    fn field_projection() {
+        struct Foo {
+            foo: i32,
+            bar: u32,
+        }
+        let vfoo = vec![Foo { foo: 1, bar: 2 }];
+        let _ = !vfoo.iter().any(|v| v.foo == 1 && v.bar == 2);
+
+        let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+        let _ = !vfoo
+            .iter().any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2);
+    }
+
+    fn index_projection() {
+        let vfoo = vec![[0, 1, 2, 3]];
+        let _ = !vfoo.iter().any(|a| a[0] == 42);
+    }
+
+    #[allow(clippy::match_like_matches_macro)]
+    fn slice_projection() {
+        let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+        let _ = !vfoo.iter().any(|sub| sub[1..4].len() == 3);
+    }
+
+    fn please(x: &u32) -> bool {
+        *x == 9
+    }
+
+    fn deref_enough(x: u32) -> bool {
+        x == 78
+    }
+
+    fn arg_no_deref(x: &&u32) -> bool {
+        **x == 78
+    }
+
+    fn more_projections() {
+        let x = 19;
+        let ppx: &u32 = &x;
+        let _ = ![ppx].iter().any(|ppp_x: &&u32| please(ppp_x));
+        let _ = ![String::from("Hey hey")].iter().any(|s| s.len() == 2);
+
+        let v = vec![3, 2, 1, 0];
+        let _ = !v.iter().any(|x| deref_enough(*x));
+        let _ = !v.iter().any(|x: &u32| deref_enough(*x));
+
+        #[allow(clippy::redundant_closure)]
+        let _ = !v.iter().any(|x| arg_no_deref(&x));
+        #[allow(clippy::redundant_closure)]
+        let _ = !v.iter().any(|x: &u32| arg_no_deref(&x));
+    }
+
+    fn field_index_projection() {
+        struct FooDouble {
+            bar: Vec<Vec<i32>>,
+        }
+        struct Foo {
+            bar: Vec<i32>,
+        }
+        struct FooOuter {
+            inner: Foo,
+            inner_double: FooDouble,
+        }
+        let vfoo = vec![FooOuter {
+            inner: Foo { bar: vec![0, 1, 2, 3] },
+            inner_double: FooDouble {
+                bar: vec![vec![0, 1, 2, 3]],
+            },
+        }];
+        let _ = !vfoo
+            .iter().any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2);
+    }
+
+    fn index_field_projection() {
+        struct Foo {
+            bar: i32,
+        }
+        struct FooOuter {
+            inner: Vec<Foo>,
+        }
+        let vfoo = vec![FooOuter {
+            inner: vec![Foo { bar: 0 }],
+        }];
+        let _ = !vfoo.iter().any(|v| v.inner[0].bar == 2);
+    }
+
+    fn double_deref_index_projection() {
+        let vfoo = vec![&&[0, 1, 2, 3]];
+        let _ = !vfoo.iter().any(|x| (**x)[0] == 9);
+    }
+
+    fn method_call_by_ref() {
+        struct Foo {
+            bar: u32,
+        }
+        impl Foo {
+            pub fn by_ref(&self, x: &u32) -> bool {
+                *x == self.bar
+            }
+        }
+        let vfoo = vec![Foo { bar: 1 }];
+        let _ = !vfoo.iter().any(|v| v.by_ref(&v.bar));
+    }
+
+    fn ref_bindings() {
+        let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+        let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+    }
+
+    fn test_string_1(s: &str) -> bool {
+        s.is_empty()
+    }
+
+    fn test_u32_1(s: &u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn test_u32_2(s: u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn projection_in_args_test() {
+        // Index projections
+        let lst = &[String::from("Hello"), String::from("world")];
+        let v: Vec<&[String]> = vec![lst];
+        let _ = !v.iter().any(|s| s[0].is_empty());
+        let _ = !v.iter().any(|s| test_string_1(&s[0]));
+
+        // Field projections
+        struct FieldProjection<'a> {
+            field: &'a u32,
+        }
+        let field = 123456789;
+        let instance = FieldProjection { field: &field };
+        let v = vec![instance];
+        let _ = !v.iter().any(|fp| fp.field.is_power_of_two());
+        let _ = !v.iter().any(|fp| test_u32_1(fp.field));
+        let _ = !v.iter().any(|fp| test_u32_2(*fp.field));
+    }
+}
diff --git a/tests/ui/search_is_some_fixable_none.rs b/tests/ui/search_is_some_fixable_none.rs
new file mode 100644 (file)
index 0000000..778f4f6
--- /dev/null
@@ -0,0 +1,222 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+    let v = vec![3, 2, 1, 0, -1, -2, -3];
+    let y = &&42;
+
+    // Check `find().is_none()`, single-line case.
+    let _ = v.iter().find(|&x| *x < 0).is_none();
+    let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
+    let _ = (0..1).find(|x| *x == 0).is_none();
+    let _ = v.iter().find(|x| **x == 0).is_none();
+    let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_none();
+    let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_none();
+    let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_none();
+    let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_none();
+    let _ = (1..3)
+        .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+        .is_none();
+
+    // Check `position().is_none()`, single-line case.
+    let _ = v.iter().position(|&x| x < 0).is_none();
+
+    // Check `rposition().is_none()`, single-line case.
+    let _ = v.iter().rposition(|&x| x < 0).is_none();
+
+    let s1 = String::from("hello world");
+    let s2 = String::from("world");
+
+    // caller of `find()` is a `&`static str`
+    let _ = "hello world".find("world").is_none();
+    let _ = "hello world".find(&s2).is_none();
+    let _ = "hello world".find(&s2[2..]).is_none();
+    // caller of `find()` is a `String`
+    let _ = s1.find("world").is_none();
+    let _ = s1.find(&s2).is_none();
+    let _ = s1.find(&s2[2..]).is_none();
+    // caller of `find()` is slice of `String`
+    let _ = s1[2..].find("world").is_none();
+    let _ = s1[2..].find(&s2).is_none();
+    let _ = s1[2..].find(&s2[2..]).is_none();
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+    struct Player {
+        hand: Vec<usize>,
+    }
+    fn filter() {
+        let p = Player {
+            hand: vec![1, 2, 3, 4, 5],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|c| filter_hand.iter().find(|cc| c == cc).is_none())
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    struct PlayerTuple {
+        hand: Vec<(usize, char)>,
+    }
+    fn filter_tuple() {
+        let p = PlayerTuple {
+            hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_none())
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    fn field_projection() {
+        struct Foo {
+            foo: i32,
+            bar: u32,
+        }
+        let vfoo = vec![Foo { foo: 1, bar: 2 }];
+        let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_none();
+
+        let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+        let _ = vfoo
+            .iter()
+            .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+            .is_none();
+    }
+
+    fn index_projection() {
+        let vfoo = vec![[0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|a| a[0] == 42).is_none();
+    }
+
+    #[allow(clippy::match_like_matches_macro)]
+    fn slice_projection() {
+        let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_none();
+    }
+
+    fn please(x: &u32) -> bool {
+        *x == 9
+    }
+
+    fn deref_enough(x: u32) -> bool {
+        x == 78
+    }
+
+    fn arg_no_deref(x: &&u32) -> bool {
+        **x == 78
+    }
+
+    fn more_projections() {
+        let x = 19;
+        let ppx: &u32 = &x;
+        let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_none();
+        let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_none();
+
+        let v = vec![3, 2, 1, 0];
+        let _ = v.iter().find(|x| deref_enough(**x)).is_none();
+        let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_none();
+
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().find(|x| arg_no_deref(x)).is_none();
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_none();
+    }
+
+    fn field_index_projection() {
+        struct FooDouble {
+            bar: Vec<Vec<i32>>,
+        }
+        struct Foo {
+            bar: Vec<i32>,
+        }
+        struct FooOuter {
+            inner: Foo,
+            inner_double: FooDouble,
+        }
+        let vfoo = vec![FooOuter {
+            inner: Foo { bar: vec![0, 1, 2, 3] },
+            inner_double: FooDouble {
+                bar: vec![vec![0, 1, 2, 3]],
+            },
+        }];
+        let _ = vfoo
+            .iter()
+            .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+            .is_none();
+    }
+
+    fn index_field_projection() {
+        struct Foo {
+            bar: i32,
+        }
+        struct FooOuter {
+            inner: Vec<Foo>,
+        }
+        let vfoo = vec![FooOuter {
+            inner: vec![Foo { bar: 0 }],
+        }];
+        let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_none();
+    }
+
+    fn double_deref_index_projection() {
+        let vfoo = vec![&&[0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_none();
+    }
+
+    fn method_call_by_ref() {
+        struct Foo {
+            bar: u32,
+        }
+        impl Foo {
+            pub fn by_ref(&self, x: &u32) -> bool {
+                *x == self.bar
+            }
+        }
+        let vfoo = vec![Foo { bar: 1 }];
+        let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none();
+    }
+
+    fn ref_bindings() {
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none();
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none();
+    }
+
+    fn test_string_1(s: &String) -> bool {
+        s.is_empty()
+    }
+
+    fn test_u32_1(s: &u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn test_u32_2(s: u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn projection_in_args_test() {
+        // Index projections
+        let lst = &[String::from("Hello"), String::from("world")];
+        let v: Vec<&[String]> = vec![lst];
+        let _ = v.iter().find(|s| s[0].is_empty()).is_none();
+        let _ = v.iter().find(|s| test_string_1(&s[0])).is_none();
+
+        // Field projections
+        struct FieldProjection<'a> {
+            field: &'a u32,
+        }
+        let field = 123456789;
+        let instance = FieldProjection { field: &field };
+        let v = vec![instance];
+        let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none();
+        let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none();
+        let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none();
+    }
+}
diff --git a/tests/ui/search_is_some_fixable_none.stderr b/tests/ui/search_is_some_fixable_none.stderr
new file mode 100644 (file)
index 0000000..7c5e5eb
--- /dev/null
@@ -0,0 +1,293 @@
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:10:13
+   |
+LL |     let _ = v.iter().find(|&x| *x < 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x < 0)`
+   |
+   = note: `-D clippy::search-is-some` implied by `-D warnings`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:11:13
+   |
+LL |     let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| **y == x)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:12:13
+   |
+LL |     let _ = (0..1).find(|x| *x == 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:13:13
+   |
+LL |     let _ = v.iter().find(|x| **x == 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:14:13
+   |
+LL |     let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(4..5).any(|x| x == 1 || x == 3 || x == 5)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:15:13
+   |
+LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:16:13
+   |
+LL |     let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| x == 0 || [1, 2, 3].contains(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:17:13
+   |
+LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:18:13
+   |
+LL |       let _ = (1..3)
+   |  _____________^
+LL | |         .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+LL | |         .is_none();
+   | |__________________^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)`
+
+error: called `is_none()` after searching an `Iterator` with `position`
+  --> $DIR/search_is_some_fixable_none.rs:23:13
+   |
+LL |     let _ = v.iter().position(|&x| x < 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
+
+error: called `is_none()` after searching an `Iterator` with `rposition`
+  --> $DIR/search_is_some_fixable_none.rs:26:13
+   |
+LL |     let _ = v.iter().rposition(|&x| x < 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:32:13
+   |
+LL |     let _ = "hello world".find("world").is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:33:13
+   |
+LL |     let _ = "hello world".find(&s2).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:34:13
+   |
+LL |     let _ = "hello world".find(&s2[2..]).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2[2..])`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:36:13
+   |
+LL |     let _ = s1.find("world").is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:37:13
+   |
+LL |     let _ = s1.find(&s2).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:38:13
+   |
+LL |     let _ = s1.find(&s2[2..]).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2[2..])`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:40:13
+   |
+LL |     let _ = s1[2..].find("world").is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:41:13
+   |
+LL |     let _ = s1[2..].find(&s2).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_none.rs:42:13
+   |
+LL |     let _ = s1[2..].find(&s2[2..]).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2[2..])`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:58:25
+   |
+LL |             .filter(|c| filter_hand.iter().find(|cc| c == cc).is_none())
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!filter_hand.iter().any(|cc| c == &cc)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:74:30
+   |
+LL |             .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_none())
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!filter_hand.iter().any(|cc| c == cc)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:85:17
+   |
+LL |         let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.foo == 1 && v.bar == 2)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:88:17
+   |
+LL |           let _ = vfoo
+   |  _________________^
+LL | |             .iter()
+LL | |             .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+LL | |             .is_none();
+   | |______________________^
+   |
+help: use `!_.any()` instead
+   |
+LL ~         let _ = !vfoo
+LL ~             .iter().any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2);
+   |
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:96:17
+   |
+LL |         let _ = vfoo.iter().find(|a| a[0] == 42).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|a| a[0] == 42)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:102:17
+   |
+LL |         let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|sub| sub[1..4].len() == 3)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:120:17
+   |
+LL |         let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![ppx].iter().any(|ppp_x: &&u32| please(ppp_x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:121:17
+   |
+LL |         let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![String::from("Hey hey")].iter().any(|s| s.len() == 2)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:124:17
+   |
+LL |         let _ = v.iter().find(|x| deref_enough(**x)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| deref_enough(*x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:125:17
+   |
+LL |         let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x: &u32| deref_enough(*x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:128:17
+   |
+LL |         let _ = v.iter().find(|x| arg_no_deref(x)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| arg_no_deref(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:130:17
+   |
+LL |         let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x: &u32| arg_no_deref(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:150:17
+   |
+LL |           let _ = vfoo
+   |  _________________^
+LL | |             .iter()
+LL | |             .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+LL | |             .is_none();
+   | |______________________^
+   |
+help: use `!_.any()` instead
+   |
+LL ~         let _ = !vfoo
+LL ~             .iter().any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2);
+   |
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:166:17
+   |
+LL |         let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.inner[0].bar == 2)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:171:17
+   |
+LL |         let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|x| (**x)[0] == 9)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:184:17
+   |
+LL |         let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.by_ref(&v.bar))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:188:17
+   |
+LL |         let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:189:17
+   |
+LL |         let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)`
+
+error: writing `&String` instead of `&str` involves a new object where a slice will do
+  --> $DIR/search_is_some_fixable_none.rs:192:25
+   |
+LL |     fn test_string_1(s: &String) -> bool {
+   |                         ^^^^^^^ help: change this to: `&str`
+   |
+   = note: `-D clippy::ptr-arg` implied by `-D warnings`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:208:17
+   |
+LL |         let _ = v.iter().find(|s| s[0].is_empty()).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|s| s[0].is_empty())`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:209:17
+   |
+LL |         let _ = v.iter().find(|s| test_string_1(&s[0])).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|s| test_string_1(&s[0]))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:218:17
+   |
+LL |         let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| fp.field.is_power_of_two())`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:219:17
+   |
+LL |         let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_1(fp.field))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_none.rs:220:17
+   |
+LL |         let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_2(*fp.field))`
+
+error: aborting due to 44 previous errors
+
diff --git a/tests/ui/search_is_some_fixable_some.fixed b/tests/ui/search_is_some_fixable_some.fixed
new file mode 100644 (file)
index 0000000..7c940a2
--- /dev/null
@@ -0,0 +1,218 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+    let v = vec![3, 2, 1, 0, -1, -2, -3];
+    let y = &&42;
+
+    // Check `find().is_some()`, single-line case.
+    let _ = v.iter().any(|x| *x < 0);
+    let _ = (0..1).any(|x| **y == x); // one dereference less
+    let _ = (0..1).any(|x| x == 0);
+    let _ = v.iter().any(|x| *x == 0);
+    let _ = (4..5).any(|x| x == 1 || x == 3 || x == 5);
+    let _ = (1..3).any(|x| [1, 2, 3].contains(&x));
+    let _ = (1..3).any(|x| x == 0 || [1, 2, 3].contains(&x));
+    let _ = (1..3).any(|x| [1, 2, 3].contains(&x) || x == 0);
+    let _ = (1..3)
+        .any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1);
+
+    // Check `position().is_some()`, single-line case.
+    let _ = v.iter().any(|&x| x < 0);
+
+    // Check `rposition().is_some()`, single-line case.
+    let _ = v.iter().any(|&x| x < 0);
+
+    let s1 = String::from("hello world");
+    let s2 = String::from("world");
+    // caller of `find()` is a `&`static str`
+    let _ = "hello world".contains("world");
+    let _ = "hello world".contains(&s2);
+    let _ = "hello world".contains(&s2[2..]);
+    // caller of `find()` is a `String`
+    let _ = s1.contains("world");
+    let _ = s1.contains(&s2);
+    let _ = s1.contains(&s2[2..]);
+    // caller of `find()` is slice of `String`
+    let _ = s1[2..].contains("world");
+    let _ = s1[2..].contains(&s2);
+    let _ = s1[2..].contains(&s2[2..]);
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+    struct Player {
+        hand: Vec<usize>,
+    }
+    fn filter() {
+        let p = Player {
+            hand: vec![1, 2, 3, 4, 5],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|c| filter_hand.iter().any(|cc| c == &cc))
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    struct PlayerTuple {
+        hand: Vec<(usize, char)>,
+    }
+    fn filter_tuple() {
+        let p = PlayerTuple {
+            hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|(c, _)| filter_hand.iter().any(|cc| c == cc))
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    fn field_projection() {
+        struct Foo {
+            foo: i32,
+            bar: u32,
+        }
+        let vfoo = vec![Foo { foo: 1, bar: 2 }];
+        let _ = vfoo.iter().any(|v| v.foo == 1 && v.bar == 2);
+
+        let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+        let _ = vfoo
+            .iter()
+            .any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2);
+    }
+
+    fn index_projection() {
+        let vfoo = vec![[0, 1, 2, 3]];
+        let _ = vfoo.iter().any(|a| a[0] == 42);
+    }
+
+    #[allow(clippy::match_like_matches_macro)]
+    fn slice_projection() {
+        let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+        let _ = vfoo.iter().any(|sub| sub[1..4].len() == 3);
+    }
+
+    fn please(x: &u32) -> bool {
+        *x == 9
+    }
+
+    fn deref_enough(x: u32) -> bool {
+        x == 78
+    }
+
+    fn arg_no_deref(x: &&u32) -> bool {
+        **x == 78
+    }
+
+    fn more_projections() {
+        let x = 19;
+        let ppx: &u32 = &x;
+        let _ = [ppx].iter().any(|ppp_x: &&u32| please(ppp_x));
+        let _ = [String::from("Hey hey")].iter().any(|s| s.len() == 2);
+
+        let v = vec![3, 2, 1, 0];
+        let _ = v.iter().any(|x| deref_enough(*x));
+        let _ = v.iter().any(|x: &u32| deref_enough(*x));
+
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().any(|x| arg_no_deref(&x));
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().any(|x: &u32| arg_no_deref(&x));
+    }
+
+    fn field_index_projection() {
+        struct FooDouble {
+            bar: Vec<Vec<i32>>,
+        }
+        struct Foo {
+            bar: Vec<i32>,
+        }
+        struct FooOuter {
+            inner: Foo,
+            inner_double: FooDouble,
+        }
+        let vfoo = vec![FooOuter {
+            inner: Foo { bar: vec![0, 1, 2, 3] },
+            inner_double: FooDouble {
+                bar: vec![vec![0, 1, 2, 3]],
+            },
+        }];
+        let _ = vfoo
+            .iter()
+            .any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2);
+    }
+
+    fn index_field_projection() {
+        struct Foo {
+            bar: i32,
+        }
+        struct FooOuter {
+            inner: Vec<Foo>,
+        }
+        let vfoo = vec![FooOuter {
+            inner: vec![Foo { bar: 0 }],
+        }];
+        let _ = vfoo.iter().any(|v| v.inner[0].bar == 2);
+    }
+
+    fn double_deref_index_projection() {
+        let vfoo = vec![&&[0, 1, 2, 3]];
+        let _ = vfoo.iter().any(|x| (**x)[0] == 9);
+    }
+
+    fn method_call_by_ref() {
+        struct Foo {
+            bar: u32,
+        }
+        impl Foo {
+            pub fn by_ref(&self, x: &u32) -> bool {
+                *x == self.bar
+            }
+        }
+        let vfoo = vec![Foo { bar: 1 }];
+        let _ = vfoo.iter().any(|v| v.by_ref(&v.bar));
+    }
+
+    fn ref_bindings() {
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+    }
+
+    fn test_string_1(s: &str) -> bool {
+        s.is_empty()
+    }
+
+    fn test_u32_1(s: &u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn test_u32_2(s: u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn projection_in_args_test() {
+        // Index projections
+        let lst = &[String::from("Hello"), String::from("world")];
+        let v: Vec<&[String]> = vec![lst];
+        let _ = v.iter().any(|s| s[0].is_empty());
+        let _ = v.iter().any(|s| test_string_1(&s[0]));
+
+        // Field projections
+        struct FieldProjection<'a> {
+            field: &'a u32,
+        }
+        let field = 123456789;
+        let instance = FieldProjection { field: &field };
+        let v = vec![instance];
+        let _ = v.iter().any(|fp| fp.field.is_power_of_two());
+        let _ = v.iter().any(|fp| test_u32_1(fp.field));
+        let _ = v.iter().any(|fp| test_u32_2(*fp.field));
+    }
+}
diff --git a/tests/ui/search_is_some_fixable_some.rs b/tests/ui/search_is_some_fixable_some.rs
new file mode 100644 (file)
index 0000000..241641f
--- /dev/null
@@ -0,0 +1,221 @@
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+    let v = vec![3, 2, 1, 0, -1, -2, -3];
+    let y = &&42;
+
+    // Check `find().is_some()`, single-line case.
+    let _ = v.iter().find(|&x| *x < 0).is_some();
+    let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
+    let _ = (0..1).find(|x| *x == 0).is_some();
+    let _ = v.iter().find(|x| **x == 0).is_some();
+    let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_some();
+    let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_some();
+    let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_some();
+    let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_some();
+    let _ = (1..3)
+        .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+        .is_some();
+
+    // Check `position().is_some()`, single-line case.
+    let _ = v.iter().position(|&x| x < 0).is_some();
+
+    // Check `rposition().is_some()`, single-line case.
+    let _ = v.iter().rposition(|&x| x < 0).is_some();
+
+    let s1 = String::from("hello world");
+    let s2 = String::from("world");
+    // caller of `find()` is a `&`static str`
+    let _ = "hello world".find("world").is_some();
+    let _ = "hello world".find(&s2).is_some();
+    let _ = "hello world".find(&s2[2..]).is_some();
+    // caller of `find()` is a `String`
+    let _ = s1.find("world").is_some();
+    let _ = s1.find(&s2).is_some();
+    let _ = s1.find(&s2[2..]).is_some();
+    // caller of `find()` is slice of `String`
+    let _ = s1[2..].find("world").is_some();
+    let _ = s1[2..].find(&s2).is_some();
+    let _ = s1[2..].find(&s2[2..]).is_some();
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+    struct Player {
+        hand: Vec<usize>,
+    }
+    fn filter() {
+        let p = Player {
+            hand: vec![1, 2, 3, 4, 5],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some())
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    struct PlayerTuple {
+        hand: Vec<(usize, char)>,
+    }
+    fn filter_tuple() {
+        let p = PlayerTuple {
+            hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+        };
+        let filter_hand = vec![5];
+        let _ = p
+            .hand
+            .iter()
+            .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some())
+            .map(|c| c.clone())
+            .collect::<Vec<_>>();
+    }
+
+    fn field_projection() {
+        struct Foo {
+            foo: i32,
+            bar: u32,
+        }
+        let vfoo = vec![Foo { foo: 1, bar: 2 }];
+        let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some();
+
+        let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+        let _ = vfoo
+            .iter()
+            .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+            .is_some();
+    }
+
+    fn index_projection() {
+        let vfoo = vec![[0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|a| a[0] == 42).is_some();
+    }
+
+    #[allow(clippy::match_like_matches_macro)]
+    fn slice_projection() {
+        let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some();
+    }
+
+    fn please(x: &u32) -> bool {
+        *x == 9
+    }
+
+    fn deref_enough(x: u32) -> bool {
+        x == 78
+    }
+
+    fn arg_no_deref(x: &&u32) -> bool {
+        **x == 78
+    }
+
+    fn more_projections() {
+        let x = 19;
+        let ppx: &u32 = &x;
+        let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some();
+        let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some();
+
+        let v = vec![3, 2, 1, 0];
+        let _ = v.iter().find(|x| deref_enough(**x)).is_some();
+        let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some();
+
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().find(|x| arg_no_deref(x)).is_some();
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some();
+    }
+
+    fn field_index_projection() {
+        struct FooDouble {
+            bar: Vec<Vec<i32>>,
+        }
+        struct Foo {
+            bar: Vec<i32>,
+        }
+        struct FooOuter {
+            inner: Foo,
+            inner_double: FooDouble,
+        }
+        let vfoo = vec![FooOuter {
+            inner: Foo { bar: vec![0, 1, 2, 3] },
+            inner_double: FooDouble {
+                bar: vec![vec![0, 1, 2, 3]],
+            },
+        }];
+        let _ = vfoo
+            .iter()
+            .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+            .is_some();
+    }
+
+    fn index_field_projection() {
+        struct Foo {
+            bar: i32,
+        }
+        struct FooOuter {
+            inner: Vec<Foo>,
+        }
+        let vfoo = vec![FooOuter {
+            inner: vec![Foo { bar: 0 }],
+        }];
+        let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some();
+    }
+
+    fn double_deref_index_projection() {
+        let vfoo = vec![&&[0, 1, 2, 3]];
+        let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some();
+    }
+
+    fn method_call_by_ref() {
+        struct Foo {
+            bar: u32,
+        }
+        impl Foo {
+            pub fn by_ref(&self, x: &u32) -> bool {
+                *x == self.bar
+            }
+        }
+        let vfoo = vec![Foo { bar: 1 }];
+        let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some();
+    }
+
+    fn ref_bindings() {
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some();
+        let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some();
+    }
+
+    fn test_string_1(s: &String) -> bool {
+        s.is_empty()
+    }
+
+    fn test_u32_1(s: &u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn test_u32_2(s: u32) -> bool {
+        s.is_power_of_two()
+    }
+
+    fn projection_in_args_test() {
+        // Index projections
+        let lst = &[String::from("Hello"), String::from("world")];
+        let v: Vec<&[String]> = vec![lst];
+        let _ = v.iter().find(|s| s[0].is_empty()).is_some();
+        let _ = v.iter().find(|s| test_string_1(&s[0])).is_some();
+
+        // Field projections
+        struct FieldProjection<'a> {
+            field: &'a u32,
+        }
+        let field = 123456789;
+        let instance = FieldProjection { field: &field };
+        let v = vec![instance];
+        let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some();
+        let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some();
+        let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
+    }
+}
diff --git a/tests/ui/search_is_some_fixable_some.stderr b/tests/ui/search_is_some_fixable_some.stderr
new file mode 100644 (file)
index 0000000..9212c6e
--- /dev/null
@@ -0,0 +1,276 @@
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:10:22
+   |
+LL |     let _ = v.iter().find(|&x| *x < 0).is_some();
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x < 0)`
+   |
+   = note: `-D clippy::search-is-some` implied by `-D warnings`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:11:20
+   |
+LL |     let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:12:20
+   |
+LL |     let _ = (0..1).find(|x| *x == 0).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:13:22
+   |
+LL |     let _ = v.iter().find(|x| **x == 0).is_some();
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:14:20
+   |
+LL |     let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 1 || x == 3 || x == 5)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:15:20
+   |
+LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:16:20
+   |
+LL |     let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0 || [1, 2, 3].contains(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:17:20
+   |
+LL |     let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_some();
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x) || x == 0)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:19:10
+   |
+LL |           .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+   |  __________^
+LL | |         .is_some();
+   | |__________________^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)`
+
+error: called `is_some()` after searching an `Iterator` with `position`
+  --> $DIR/search_is_some_fixable_some.rs:23:22
+   |
+LL |     let _ = v.iter().position(|&x| x < 0).is_some();
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
+
+error: called `is_some()` after searching an `Iterator` with `rposition`
+  --> $DIR/search_is_some_fixable_some.rs:26:22
+   |
+LL |     let _ = v.iter().rposition(|&x| x < 0).is_some();
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:31:27
+   |
+LL |     let _ = "hello world".find("world").is_some();
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:32:27
+   |
+LL |     let _ = "hello world".find(&s2).is_some();
+   |                           ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:33:27
+   |
+LL |     let _ = "hello world".find(&s2[2..]).is_some();
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:35:16
+   |
+LL |     let _ = s1.find("world").is_some();
+   |                ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:36:16
+   |
+LL |     let _ = s1.find(&s2).is_some();
+   |                ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:37:16
+   |
+LL |     let _ = s1.find(&s2[2..]).is_some();
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:39:21
+   |
+LL |     let _ = s1[2..].find("world").is_some();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:40:21
+   |
+LL |     let _ = s1[2..].find(&s2).is_some();
+   |                     ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
+
+error: called `is_some()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable_some.rs:41:21
+   |
+LL |     let _ = s1[2..].find(&s2[2..]).is_some();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:57:44
+   |
+LL |             .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some())
+   |                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|cc| c == &cc)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:73:49
+   |
+LL |             .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some())
+   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|cc| c == cc)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:84:29
+   |
+LL |         let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.foo == 1 && v.bar == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:89:14
+   |
+LL |               .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+   |  ______________^
+LL | |             .is_some();
+   | |______________________^ help: use `any()` instead: `any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:95:29
+   |
+LL |         let _ = vfoo.iter().find(|a| a[0] == 42).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|a| a[0] == 42)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:101:29
+   |
+LL |         let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|sub| sub[1..4].len() == 3)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:119:30
+   |
+LL |         let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some();
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|ppp_x: &&u32| please(ppp_x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:120:50
+   |
+LL |         let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some();
+   |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s.len() == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:123:26
+   |
+LL |         let _ = v.iter().find(|x| deref_enough(**x)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| deref_enough(*x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:124:26
+   |
+LL |         let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| deref_enough(*x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:127:26
+   |
+LL |         let _ = v.iter().find(|x| arg_no_deref(x)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| arg_no_deref(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:129:26
+   |
+LL |         let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:151:14
+   |
+LL |               .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+   |  ______________^
+LL | |             .is_some();
+   | |______________________^ help: use `any()` instead: `any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:165:29
+   |
+LL |         let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.inner[0].bar == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:170:29
+   |
+LL |         let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| (**x)[0] == 9)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:183:29
+   |
+LL |         let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some();
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.by_ref(&v.bar))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:187:55
+   |
+LL |         let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some();
+   |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:188:55
+   |
+LL |         let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some();
+   |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)`
+
+error: writing `&String` instead of `&str` involves a new object where a slice will do
+  --> $DIR/search_is_some_fixable_some.rs:191:25
+   |
+LL |     fn test_string_1(s: &String) -> bool {
+   |                         ^^^^^^^ help: change this to: `&str`
+   |
+   = note: `-D clippy::ptr-arg` implied by `-D warnings`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:207:26
+   |
+LL |         let _ = v.iter().find(|s| s[0].is_empty()).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s[0].is_empty())`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:208:26
+   |
+LL |         let _ = v.iter().find(|s| test_string_1(&s[0])).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| test_string_1(&s[0]))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:217:26
+   |
+LL |         let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| fp.field.is_power_of_two())`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:218:26
+   |
+LL |         let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_1(fp.field))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:219:26
+   |
+LL |         let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_2(*fp.field))`
+
+error: aborting due to 44 previous errors
+
index 1abd2b7883df0bd06f0fd5e456c4f29c6d350060..68e26726724b808425a6b53754bfa1d0047d4027 100644 (file)
@@ -17,6 +17,7 @@ fn main() {
     x.split('💣');
     // Can't use this lint for unicode code points which don't fit in a char
     x.split("❤️");
+    x.split_inclusive('x');
     x.contains('x');
     x.starts_with('x');
     x.ends_with('x');
@@ -27,6 +28,8 @@ fn main() {
     x.rsplit_terminator('x');
     x.splitn(2, 'x');
     x.rsplitn(2, 'x');
+    x.split_once('x');
+    x.rsplit_once('x');
     x.matches('x');
     x.rmatches('x');
     x.match_indices('x');
@@ -35,6 +38,8 @@ fn main() {
     x.trim_end_matches('x');
     x.strip_prefix('x');
     x.strip_suffix('x');
+    x.replace('x', "y");
+    x.replacen('x', "y", 3);
     // Make sure we escape characters correctly.
     x.split('\n');
     x.split('\'');
@@ -43,7 +48,7 @@ fn main() {
     let h = HashSet::<String>::new();
     h.contains("X"); // should not warn
 
-    x.replace(";", ",").split(','); // issue #2978
+    x.replace(';', ",").split(','); // issue #2978
     x.starts_with('\x03'); // issue #2996
 
     // Issue #3204
@@ -56,4 +61,7 @@ fn main() {
     x.split('a');
     x.split('\'');
     x.split('#');
+    // Must escape backslash in raw strings when converting to char #8060
+    x.split('\\');
+    x.split('\\');
 }
index e662bf34be2ceffb5ba409fe03613bbdb3710494..186202d78ec5aeae404064521c87393bb897857b 100644 (file)
@@ -17,6 +17,7 @@ fn main() {
     x.split("💣");
     // Can't use this lint for unicode code points which don't fit in a char
     x.split("❤️");
+    x.split_inclusive("x");
     x.contains("x");
     x.starts_with("x");
     x.ends_with("x");
@@ -27,6 +28,8 @@ fn main() {
     x.rsplit_terminator("x");
     x.splitn(2, "x");
     x.rsplitn(2, "x");
+    x.split_once("x");
+    x.rsplit_once("x");
     x.matches("x");
     x.rmatches("x");
     x.match_indices("x");
@@ -35,6 +38,8 @@ fn main() {
     x.trim_end_matches("x");
     x.strip_prefix("x");
     x.strip_suffix("x");
+    x.replace("x", "y");
+    x.replacen("x", "y", 3);
     // Make sure we escape characters correctly.
     x.split("\n");
     x.split("'");
@@ -43,7 +48,7 @@ fn main() {
     let h = HashSet::<String>::new();
     h.contains("X"); // should not warn
 
-    x.replace(";", ",").split(","); // issue #2978
+    x.replace(';', ",").split(","); // issue #2978
     x.starts_with("\x03"); // issue #2996
 
     // Issue #3204
@@ -56,4 +61,7 @@ fn main() {
     x.split(r###"a"###);
     x.split(r###"'"###);
     x.split(r###"#"###);
+    // Must escape backslash in raw strings when converting to char #8060
+    x.split(r#"\"#);
+    x.split(r"\");
 }
index 22d4b2d460fb047e217091ac1ff741c4a20e93bf..5564aac674d97624d1655857035c88163df88fd0 100644 (file)
@@ -25,172 +25,214 @@ LL |     x.split("💣");
    |             ^^^^ help: try using a `char` instead: `'💣'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:20:16
+  --> $DIR/single_char_pattern.rs:20:23
+   |
+LL |     x.split_inclusive("x");
+   |                       ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:21:16
    |
 LL |     x.contains("x");
    |                ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:21:19
+  --> $DIR/single_char_pattern.rs:22:19
    |
 LL |     x.starts_with("x");
    |                   ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:22:17
+  --> $DIR/single_char_pattern.rs:23:17
    |
 LL |     x.ends_with("x");
    |                 ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:23:12
+  --> $DIR/single_char_pattern.rs:24:12
    |
 LL |     x.find("x");
    |            ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:24:13
+  --> $DIR/single_char_pattern.rs:25:13
    |
 LL |     x.rfind("x");
    |             ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:25:14
+  --> $DIR/single_char_pattern.rs:26:14
    |
 LL |     x.rsplit("x");
    |              ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:26:24
+  --> $DIR/single_char_pattern.rs:27:24
    |
 LL |     x.split_terminator("x");
    |                        ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:27:25
+  --> $DIR/single_char_pattern.rs:28:25
    |
 LL |     x.rsplit_terminator("x");
    |                         ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:28:17
+  --> $DIR/single_char_pattern.rs:29:17
    |
 LL |     x.splitn(2, "x");
    |                 ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:29:18
+  --> $DIR/single_char_pattern.rs:30:18
    |
 LL |     x.rsplitn(2, "x");
    |                  ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:30:15
+  --> $DIR/single_char_pattern.rs:31:18
+   |
+LL |     x.split_once("x");
+   |                  ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:32:19
+   |
+LL |     x.rsplit_once("x");
+   |                   ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:33:15
    |
 LL |     x.matches("x");
    |               ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:31:16
+  --> $DIR/single_char_pattern.rs:34:16
    |
 LL |     x.rmatches("x");
    |                ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:32:21
+  --> $DIR/single_char_pattern.rs:35:21
    |
 LL |     x.match_indices("x");
    |                     ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:33:22
+  --> $DIR/single_char_pattern.rs:36:22
    |
 LL |     x.rmatch_indices("x");
    |                      ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:34:26
+  --> $DIR/single_char_pattern.rs:37:26
    |
 LL |     x.trim_start_matches("x");
    |                          ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:35:24
+  --> $DIR/single_char_pattern.rs:38:24
    |
 LL |     x.trim_end_matches("x");
    |                        ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:36:20
+  --> $DIR/single_char_pattern.rs:39:20
    |
 LL |     x.strip_prefix("x");
    |                    ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:37:20
+  --> $DIR/single_char_pattern.rs:40:20
    |
 LL |     x.strip_suffix("x");
    |                    ^^^ help: try using a `char` instead: `'x'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:39:13
+  --> $DIR/single_char_pattern.rs:41:15
+   |
+LL |     x.replace("x", "y");
+   |               ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:42:16
+   |
+LL |     x.replacen("x", "y", 3);
+   |                ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:44:13
    |
 LL |     x.split("/n");
    |             ^^^^ help: try using a `char` instead: `'/n'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:40:13
+  --> $DIR/single_char_pattern.rs:45:13
    |
 LL |     x.split("'");
    |             ^^^ help: try using a `char` instead: `'/''`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:41:13
+  --> $DIR/single_char_pattern.rs:46:13
    |
 LL |     x.split("/'");
    |             ^^^^ help: try using a `char` instead: `'/''`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:46:31
+  --> $DIR/single_char_pattern.rs:51:31
    |
-LL |     x.replace(";", ",").split(","); // issue #2978
+LL |     x.replace(';', ",").split(","); // issue #2978
    |                               ^^^ help: try using a `char` instead: `','`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:47:19
+  --> $DIR/single_char_pattern.rs:52:19
    |
 LL |     x.starts_with("/x03"); // issue #2996
    |                   ^^^^^^ help: try using a `char` instead: `'/x03'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:54:13
+  --> $DIR/single_char_pattern.rs:59:13
    |
 LL |     x.split(r"a");
    |             ^^^^ help: try using a `char` instead: `'a'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:55:13
+  --> $DIR/single_char_pattern.rs:60:13
    |
 LL |     x.split(r#"a"#);
    |             ^^^^^^ help: try using a `char` instead: `'a'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:56:13
+  --> $DIR/single_char_pattern.rs:61:13
    |
 LL |     x.split(r###"a"###);
    |             ^^^^^^^^^^ help: try using a `char` instead: `'a'`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:57:13
+  --> $DIR/single_char_pattern.rs:62:13
    |
 LL |     x.split(r###"'"###);
    |             ^^^^^^^^^^ help: try using a `char` instead: `'/''`
 
 error: single-character string constant used as pattern
-  --> $DIR/single_char_pattern.rs:58:13
+  --> $DIR/single_char_pattern.rs:63:13
    |
 LL |     x.split(r###"#"###);
    |             ^^^^^^^^^^ help: try using a `char` instead: `'#'`
 
-error: aborting due to 32 previous errors
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:65:13
+   |
+LL |     x.split(r#"/"#);
+   |             ^^^^^^ help: try using a `char` instead: `'/'`
+
+error: single-character string constant used as pattern
+  --> $DIR/single_char_pattern.rs:66:13
+   |
+LL |     x.split(r"/");
+   |             ^^^^ help: try using a `char` instead: `'/'`
+
+error: aborting due to 39 previous errors
 
diff --git a/tests/ui/strlen_on_c_strings.fixed b/tests/ui/strlen_on_c_strings.fixed
new file mode 100644 (file)
index 0000000..947a59b
--- /dev/null
@@ -0,0 +1,34 @@
+// run-rustfix
+
+#![warn(clippy::strlen_on_c_strings)]
+#![allow(dead_code)]
+#![feature(rustc_private)]
+extern crate libc;
+
+#[allow(unused)]
+use libc::strlen;
+use std::ffi::{CStr, CString};
+
+fn main() {
+    // CString
+    let cstring = CString::new("foo").expect("CString::new failed");
+    let _ = cstring.as_bytes().len();
+
+    // CStr
+    let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
+    let _ = cstr.to_bytes().len();
+
+    let _ = cstr.to_bytes().len();
+
+    let pcstr: *const &CStr = &cstr;
+    let _ = unsafe { (*pcstr).to_bytes().len() };
+
+    unsafe fn unsafe_identity<T>(x: T) -> T {
+        x
+    }
+    let _ = unsafe { unsafe_identity(cstr).to_bytes().len() };
+    let _ = unsafe { unsafe_identity(cstr) }.to_bytes().len();
+
+    let f: unsafe fn(_) -> _ = unsafe_identity;
+    let _ = unsafe { f(cstr).to_bytes().len() };
+}
index 21902fa8483f3e9913fedd1572daa511fee13477..1237f1ab03acd07ebe1f5037fd28eb07fddb7048 100644 (file)
@@ -1,16 +1,34 @@
+// run-rustfix
+
 #![warn(clippy::strlen_on_c_strings)]
 #![allow(dead_code)]
 #![feature(rustc_private)]
 extern crate libc;
 
+#[allow(unused)]
+use libc::strlen;
 use std::ffi::{CStr, CString};
 
 fn main() {
     // CString
     let cstring = CString::new("foo").expect("CString::new failed");
-    let len = unsafe { libc::strlen(cstring.as_ptr()) };
+    let _ = unsafe { libc::strlen(cstring.as_ptr()) };
 
     // CStr
     let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
-    let len = unsafe { libc::strlen(cstr.as_ptr()) };
+    let _ = unsafe { libc::strlen(cstr.as_ptr()) };
+
+    let _ = unsafe { strlen(cstr.as_ptr()) };
+
+    let pcstr: *const &CStr = &cstr;
+    let _ = unsafe { strlen((*pcstr).as_ptr()) };
+
+    unsafe fn unsafe_identity<T>(x: T) -> T {
+        x
+    }
+    let _ = unsafe { strlen(unsafe_identity(cstr).as_ptr()) };
+    let _ = unsafe { strlen(unsafe { unsafe_identity(cstr) }.as_ptr()) };
+
+    let f: unsafe fn(_) -> _ = unsafe_identity;
+    let _ = unsafe { strlen(f(cstr).as_ptr()) };
 }
index e0ca511557c5b594981270097a7161909b6fb319..296268a5f1df79988635dd896c9715e3215cb9c9 100644 (file)
@@ -1,25 +1,46 @@
 error: using `libc::strlen` on a `CString` or `CStr` value
-  --> $DIR/strlen_on_c_strings.rs:11:24
+  --> $DIR/strlen_on_c_strings.rs:15:13
    |
-LL |     let len = unsafe { libc::strlen(cstring.as_ptr()) };
-   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     let _ = unsafe { libc::strlen(cstring.as_ptr()) };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstring.as_bytes().len()`
    |
    = note: `-D clippy::strlen-on-c-strings` implied by `-D warnings`
-help: try this (you might also need to get rid of `unsafe` block in some cases):
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:19:13
+   |
+LL |     let _ = unsafe { libc::strlen(cstr.as_ptr()) };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstr.to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:21:13
+   |
+LL |     let _ = unsafe { strlen(cstr.as_ptr()) };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstr.to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:24:22
    |
-LL |     let len = unsafe { cstring.as_bytes().len() };
-   |                        ~~~~~~~~~~~~~~~~~~~~~~~~
+LL |     let _ = unsafe { strlen((*pcstr).as_ptr()) };
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*pcstr).to_bytes().len()`
 
 error: using `libc::strlen` on a `CString` or `CStr` value
-  --> $DIR/strlen_on_c_strings.rs:15:24
+  --> $DIR/strlen_on_c_strings.rs:29:22
    |
-LL |     let len = unsafe { libc::strlen(cstr.as_ptr()) };
-   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     let _ = unsafe { strlen(unsafe_identity(cstr).as_ptr()) };
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unsafe_identity(cstr).to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:30:13
    |
-help: try this (you might also need to get rid of `unsafe` block in some cases):
+LL |     let _ = unsafe { strlen(unsafe { unsafe_identity(cstr) }.as_ptr()) };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unsafe { unsafe_identity(cstr) }.to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+  --> $DIR/strlen_on_c_strings.rs:33:22
    |
-LL |     let len = unsafe { cstr.to_bytes().len() };
-   |                        ~~~~~~~~~~~~~~~~~~~~~
+LL |     let _ = unsafe { strlen(f(cstr).as_ptr()) };
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `f(cstr).to_bytes().len()`
 
-error: aborting due to 2 previous errors
+error: aborting due to 7 previous errors
 
index 1f596c312fe395783208b6ab34ffd8fab27d8204..e0a4eadce33bd109bfd67f1ac8212f363a896811 100644 (file)
@@ -20,8 +20,16 @@ fn uni() {
     print!("\u{DC}ben!"); // this is ok
 }
 
+// issue 8013
+#[warn(clippy::non_ascii_literal)]
+fn single_quote() {
+    const _EMPTY_BLOCK: char = '▱';
+    const _FULL_BLOCK: char = '▰';
+}
+
 fn main() {
     zero();
     uni();
     canon();
+    single_quote();
 }
index 3fca463c620b522659335917cf78e0a2a5baeb24..3f54e3880e7470a46c61d2b1b0b422eb8b0d604b 100644 (file)
@@ -34,5 +34,17 @@ LL |     print!("Üben!");
    |
    = note: `-D clippy::non-ascii-literal` implied by `-D warnings`
 
-error: aborting due to 5 previous errors
+error: literal non-ASCII character detected
+  --> $DIR/unicode.rs:26:32
+   |
+LL |     const _EMPTY_BLOCK: char = '▱';
+   |                                ^^^ help: consider replacing the string with: `'/u{25b1}'`
+
+error: literal non-ASCII character detected
+  --> $DIR/unicode.rs:27:31
+   |
+LL |     const _FULL_BLOCK: char = '▰';
+   |                               ^^^ help: consider replacing the string with: `'/u{25b0}'`
+
+error: aborting due to 7 previous errors
 
index b0a13f827d6cd9564291aef82567c2ad7e53748e..80c30393832c107b93b6c45b9033b3dbb9fd0799 100644 (file)
@@ -5,3 +5,8 @@ allow-unauthenticated = [
 ]
 
 [assign]
+
+# Allows shortcuts like `@rustbot ready`
+#
+# See https://github.com/rust-lang/triagebot/wiki/Shortcuts
+[shortcut]
index 58e8b4f4829fd48d897e8de8d66e6060e3fc26f6..f175700a3f479dcf912db12fcb587ed44ea3d94b 100644 (file)
@@ -336,7 +336,7 @@ Otherwise, have a great day =^.^=
                     </h2>
                 </header>
 
-                <ul class="list-group lint-docs" ng-class="{collapse: true, in: open[lint.id]}">
+                <div class="list-group lint-docs" ng-class="{collapse: true, in: open[lint.id]}">
                     <div class="list-group-item lint-doc-md" ng-bind-html="lint.docs | markdown"></div>
                     <div class="lint-additional-info-container">
                         <!-- Applicability -->
@@ -359,7 +359,7 @@ Otherwise, have a great day =^.^=
                             <a href="https://github.com/rust-lang/rust-clippy/blob/{{docVersion}}/clippy_lints/{{lint.id_span.path}}#L{{lint.id_span.line}}">View Source</a>
                         </div>
                     </div>
-                </ul>
+                </div>
             </article>
         </div>
     </div>