authorbors <bors@rust-lang.org> 2025-06-27 20:07:50 UTC
committerbors <bors@rust-lang.org> 2025-06-27 20:07:50 UTC
logbdaba05a953eb5abeba0011cdda2560d157aed2e
treebbdaecd0be314a364edfdd760923b4b2b9967d4b
parentfe5f3dedf7b4d6bea2cadb17343f747d70b4c66b
parentd9a4fd5d51d7c93d8c8b759c656395a0ccade198

Auto merge of #143064 - flip1995:clippy-subtree-update, r=GuillaumeGomez

Clippy subtree update r? `@Manishearth` Cargo.lock update due to version bump

157 files changed, 3174 insertions(+), 850 deletions(-)

Cargo.lock+10-4
......@@ -537,7 +537,7 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
537537
538538[[package]]
539539name = "clippy"
540version = "0.1.89"
540version = "0.1.90"
541541dependencies = [
542542 "anstream",
543543 "askama",
......@@ -547,6 +547,7 @@ dependencies = [
547547 "clippy_lints_internal",
548548 "clippy_utils",
549549 "color-print",
550 "declare_clippy_lint",
550551 "filetime",
551552 "futures",
552553 "if_chain",
......@@ -569,7 +570,7 @@ dependencies = [
569570
570571[[package]]
571572name = "clippy_config"
572version = "0.1.89"
573version = "0.1.90"
573574dependencies = [
574575 "clippy_utils",
575576 "itertools",
......@@ -592,12 +593,13 @@ dependencies = [
592593
593594[[package]]
594595name = "clippy_lints"
595version = "0.1.89"
596version = "0.1.90"
596597dependencies = [
597598 "arrayvec",
598599 "cargo_metadata 0.18.1",
599600 "clippy_config",
600601 "clippy_utils",
602 "declare_clippy_lint",
601603 "itertools",
602604 "quine-mc_cluskey",
603605 "regex-syntax 0.8.5",
......@@ -622,7 +624,7 @@ dependencies = [
622624
623625[[package]]
624626name = "clippy_utils"
625version = "0.1.89"
627version = "0.1.90"
626628dependencies = [
627629 "arrayvec",
628630 "itertools",
......@@ -931,6 +933,10 @@ dependencies = [
931933 "winapi",
932934]
933935
936[[package]]
937name = "declare_clippy_lint"
938version = "0.1.90"
939
934940[[package]]
935941name = "derive-where"
936942version = "1.4.0"
compiler/rustc_codegen_gcc/src/intrinsic/simd.rs+3-2
......@@ -61,7 +61,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
6161 let (len, _) = args[1].layout.ty.simd_size_and_type(bx.tcx());
6262
6363 let expected_int_bits = (len.max(8) - 1).next_power_of_two();
64 let expected_bytes = len / 8 + ((len % 8 > 0) as u64);
64 let expected_bytes = len / 8 + ((!len.is_multiple_of(8)) as u64);
6565
6666 let mask_ty = args[0].layout.ty;
6767 let mut mask = match *mask_ty.kind() {
......@@ -676,7 +676,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
676676 let elem_type = vector_type.get_element_type();
677677
678678 let expected_int_bits = in_len.max(8);
679 let expected_bytes = expected_int_bits / 8 + ((expected_int_bits % 8 > 0) as u64);
679 let expected_bytes =
680 expected_int_bits / 8 + ((!expected_int_bits.is_multiple_of(8)) as u64);
680681
681682 // FIXME(antoyo): that's not going to work for masks bigger than 128 bits.
682683 let result_type = bx.type_ix(expected_int_bits);
src/bootstrap/src/utils/render_tests.rs+3-1
......@@ -202,7 +202,9 @@ impl<'a> Renderer<'a> {
202202 }
203203
204204 fn render_test_outcome_terse(&mut self, outcome: Outcome<'_>, test: &TestOutcome) {
205 if self.terse_tests_in_line != 0 && self.terse_tests_in_line % TERSE_TESTS_PER_LINE == 0 {
205 if self.terse_tests_in_line != 0
206 && self.terse_tests_in_line.is_multiple_of(TERSE_TESTS_PER_LINE)
207 {
206208 if let Some(total) = self.tests_count {
207209 let total = total.to_string();
208210 let executed = format!("{:>width$}", self.executed_tests - 1, width = total.len());
src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml+3-1
......@@ -1,5 +1,7 @@
11name: New lint suggestion
2description: Suggest a new Clippy lint.
2description: |
3 Suggest a new Clippy lint (currently not accepting new lints)
4 Check out the Clippy book for more information about the feature freeze.
35labels: ["A-lint"]
46body:
57 - type: markdown
src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md+4
......@@ -32,6 +32,10 @@ order to get feedback.
3232
3333Delete this line and everything above before opening your PR.
3434
35Note that we are currently not taking in new PRs that add new lints. We are in a
36feature freeze. Check out the book for more information. If you open a
37feature-adding pull request, its review will be delayed.
38
3539---
3640
3741*Please write a short comment explaining your change (or "none" for internal only changes)*
src/tools/clippy/.github/workflows/feature_freeze.yml created+25
......@@ -0,0 +1,25 @@
1name: Feature freeze check
2
3on:
4 pull_request:
5 paths:
6 - 'clippy_lints/src/declared_lints.rs'
7
8jobs:
9 auto-comment:
10 runs-on: ubuntu-latest
11
12 steps:
13 - name: Check PR Changes
14 id: pr-changes
15 run: echo "::set-output name=changes::${{ toJson(github.event.pull_request.changed_files) }}"
16
17 - name: Create Comment
18 if: steps.pr-changes.outputs.changes != '[]'
19 run: |
20 # Use GitHub API to create a comment on the PR
21 PR_NUMBER=${{ github.event.pull_request.number }}
22 COMMENT="**Seems that you are trying to add a new lint!**\nWe are currently in a [feature freeze](https://doc.rust-lang.org/nightly/clippy/development/feature_freeze.html), so we are delaying all lint-adding PRs to August 1st and focusing on bugfixes.\nThanks a lot for your contribution, and sorry for the inconvenience.\nWith ❤ from the Clippy team"
23 GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
24 COMMENT_URL="https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments"
25 curl -s -H "Authorization: token ${GITHUB_TOKEN}" -X POST $COMMENT_URL -d "{\"body\":\"$COMMENT\"}"
src/tools/clippy/CHANGELOG.md+90-1
......@@ -6,7 +6,94 @@ document.
66
77## Unreleased / Beta / In Rust Nightly
88
9[1e5237f4...master](https://github.com/rust-lang/rust-clippy/compare/1e5237f4...master)
9[03a5b6b9...master](https://github.com/rust-lang/rust-clippy/compare/03a5b6b9...master)
10
11## Rust 1.88
12
13Current stable, released 2025-06-26
14
15[View all 126 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-03-21T10%3A30%3A57Z..2025-05-01T08%3A03%3A26Z+base%3Amaster)
16
17### New Lints
18
19* Added [`swap_with_temporary`] to `complexity` [#14046](https://github.com/rust-lang/rust-clippy/pull/14046)
20* Added [`redundant_test_prefix`] to `restriction` [#13710](https://github.com/rust-lang/rust-clippy/pull/13710)
21* Added [`manual_dangling_ptr`] to `style` [#14107](https://github.com/rust-lang/rust-clippy/pull/14107)
22* Added [`char_indices_as_byte_indices`] to `correctness` [#13435](https://github.com/rust-lang/rust-clippy/pull/13435)
23* Added [`manual_abs_diff`] to `complexity` [#14482](https://github.com/rust-lang/rust-clippy/pull/14482)
24* Added [`ignore_without_reason`] to `pedantic` [#13931](https://github.com/rust-lang/rust-clippy/pull/13931)
25
26### Moves and Deprecations
27
28* Moved [`uninlined_format_args`] to `style` (from `pedantic`)
29 [#14160](https://github.com/rust-lang/rust-clippy/pull/14160)
30* [`match_on_vec_items`] deprecated in favor of [`indexing_slicing`]
31 [#14217](https://github.com/rust-lang/rust-clippy/pull/14217)
32* Removed superseded lints: `transmute_float_to_int`, `transmute_int_to_char`,
33 `transmute_int_to_float`, `transmute_num_to_bytes` (now in rustc)
34 [#14703](https://github.com/rust-lang/rust-clippy/pull/14703)
35
36### Enhancements
37
38* Configuration renamed from `lint-inconsistent-struct-field-initializers`
39 to `check-inconsistent-struct-field-initializers`
40 [#14280](https://github.com/rust-lang/rust-clippy/pull/14280)
41* Paths in `disallowed_*` configurations are now validated
42 [#14397](https://github.com/rust-lang/rust-clippy/pull/14397)
43* [`borrow_as_ptr`] now lints implicit casts as well
44 [#14408](https://github.com/rust-lang/rust-clippy/pull/14408)
45* [`iter_kv_map`] now recognizes references on maps
46 [#14596](https://github.com/rust-lang/rust-clippy/pull/14596)
47* [`empty_enum_variants_with_brackets`] no longer lints reachable enums or enums used
48 as functions within same crate [#12971](https://github.com/rust-lang/rust-clippy/pull/12971)
49* [`needless_lifetimes`] now checks for lifetime uses in closures
50 [#14608](https://github.com/rust-lang/rust-clippy/pull/14608)
51* [`wildcard_imports`] now lints on `pub use` when `warn_on_all_wildcard_imports` is enabled
52 [#14182](https://github.com/rust-lang/rust-clippy/pull/14182)
53* [`collapsible_if`] now recognizes the `let_chains` feature
54 [#14481](https://github.com/rust-lang/rust-clippy/pull/14481)
55* [`match_single_binding`] now allows macros in scrutinee and patterns
56 [#14635](https://github.com/rust-lang/rust-clippy/pull/14635)
57* [`needless_borrow`] does not contradict the compiler's
58 `dangerous_implicit_autorefs` lint even though the references
59 are not mandatory
60 [#14810](https://github.com/rust-lang/rust-clippy/pull/14810)
61
62### False Positive Fixes
63
64* [`double_ended_iterator_last`] and [`needless_collect`] fixed FP when iter has side effects
65 [#14490](https://github.com/rust-lang/rust-clippy/pull/14490)
66* [`mut_from_ref`] fixed FP where lifetimes nested in types were not considered
67 [#14471](https://github.com/rust-lang/rust-clippy/pull/14471)
68* [`redundant_clone`] fixed FP in overlapping lifetime
69 [#14237](https://github.com/rust-lang/rust-clippy/pull/14237)
70* [`map_entry`] fixed FP where lint would trigger without insert calls present
71 [#14568](https://github.com/rust-lang/rust-clippy/pull/14568)
72* [`iter_cloned_collect`] fixed FP with custom `From`/`IntoIterator` impl
73 [#14473](https://github.com/rust-lang/rust-clippy/pull/14473)
74* [`shadow_unrelated`] fixed FP in destructuring assignments
75 [#14381](https://github.com/rust-lang/rust-clippy/pull/14381)
76* [`redundant_clone`] fixed FP on enum cast
77 [#14395](https://github.com/rust-lang/rust-clippy/pull/14395)
78* [`collapsible_if`] fixed FP on block stmt before expr
79 [#14730](https://github.com/rust-lang/rust-clippy/pull/14730)
80
81### ICE Fixes
82
83* [`missing_const_for_fn`] fix ICE with `-Z validate-mir` compilation option
84 [#14776](https://github.com/rust-lang/rust-clippy/pull/14776)
85
86### Documentation Improvements
87
88* [`missing_asserts_for_indexing`] improved documentation and examples
89 [#14108](https://github.com/rust-lang/rust-clippy/pull/14108)
90
91### Others
92
93* We're testing with edition 2024 now
94 [#14602](https://github.com/rust-lang/rust-clippy/pull/14602)
95* Don't warn about unloaded crates in `clippy.toml` disallowed paths
96 [#14733](https://github.com/rust-lang/rust-clippy/pull/14733)
1097
1198## Rust 1.87
1299
......@@ -5729,6 +5816,7 @@ Released 2018-09-13
57295816[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
57305817[`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
57315818[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
5819[`doc_broken_link`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_broken_link
57325820[`doc_comment_double_space_linebreaks`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_comment_double_space_linebreaks
57335821[`doc_include_without_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_include_without_cfg
57345822[`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation
......@@ -5967,6 +6055,7 @@ Released 2018-09-13
59676055[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
59686056[`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite
59696057[`manual_is_infinite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_infinite
6058[`manual_is_multiple_of`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_multiple_of
59706059[`manual_is_power_of_two`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two
59716060[`manual_is_variant_and`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_variant_and
59726061[`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else
src/tools/clippy/Cargo.toml+2-1
......@@ -1,6 +1,6 @@
11[package]
22name = "clippy"
3version = "0.1.89"
3version = "0.1.90"
44description = "A bunch of helpful lints to avoid common pitfalls in Rust"
55repository = "https://github.com/rust-lang/rust-clippy"
66readme = "README.md"
......@@ -24,6 +24,7 @@ path = "src/driver.rs"
2424clippy_config = { path = "clippy_config" }
2525clippy_lints = { path = "clippy_lints" }
2626clippy_utils = { path = "clippy_utils" }
27declare_clippy_lint = { path = "declare_clippy_lint" }
2728rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" }
2829clippy_lints_internal = { path = "clippy_lints_internal", optional = true }
2930tempfile = { version = "3.20", optional = true }
src/tools/clippy/book/src/README.md+4
......@@ -1,5 +1,9 @@
11# Clippy
22
3[### IMPORTANT NOTE FOR CONTRIBUTORS ================](development/feature_freeze.md)
4
5----
6
37[![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](https://github.com/rust-lang/rust-clippy#license)
48
59A collection of lints to catch common mistakes and improve your
src/tools/clippy/book/src/SUMMARY.md+1
......@@ -13,6 +13,7 @@
1313 - [GitLab CI](continuous_integration/gitlab.md)
1414 - [Travis CI](continuous_integration/travis.md)
1515- [Development](development/README.md)
16 - [IMPORTANT: FEATURE FREEZE](development/feature_freeze.md)
1617 - [Basics](development/basics.md)
1718 - [Adding Lints](development/adding_lints.md)
1819 - [Defining Lints](development/defining_lints.md)
src/tools/clippy/book/src/development/adding_lints.md+3
......@@ -1,5 +1,8 @@
11# Adding a new lint
22
3[### IMPORTANT NOTE FOR CONTRIBUTORS ================](feature_freeze.md)
4
5
36You are probably here because you want to add a new lint to Clippy. If this is
47the first time you're contributing to Clippy, this document guides you through
58creating an example lint from scratch.
src/tools/clippy/book/src/development/feature_freeze.md created+55
......@@ -0,0 +1,55 @@
1# IMPORTANT: FEATURE FREEZE
2
3This is a temporary notice.
4
5From the 26th of June until the 18th of September we will perform a feature freeze. Only bugfix PRs will be reviewed
6except already open ones. Every feature-adding PR opened in between those dates will be moved into a
7milestone to be reviewed separately at another time.
8
9We do this because of the long backlog of bugs that need to be addressed
10in order to continue being the state-of-the-art linter that Clippy has become known for being.
11
12## For contributors
13
14If you are a contributor or are planning to become one, **please do not open a lint-adding PR**, we have lots of open
15bugs of all levels of difficulty that you can address instead!
16
17We currently have about 800 lints, each one posing a maintainability challenge that needs to account to every possible
18use case of the whole ecosystem. Bugs are natural in every software, but the Clippy team considers that Clippy needs a
19refinement period.
20
21If you open a PR at this time, we will not review it but push it into a milestone until the refinement period ends,
22adding additional load into our reviewing schedules.
23
24## I want to help, what can I do
25
26Thanks a lot to everyone who wants to help Clippy become better software in this feature freeze period!
27If you'd like to help, making a bugfix, making sure that it works, and opening a PR is a great step!
28
29To find things to fix, go to the [tracking issue][tracking_issue], find an issue that you like, go there and claim that
30issue with `@rustbot claim`.
31
32As a general metric and always taking into account your skill and knowledge level, you can use this guide:
33
34- 🟥 [ICEs][search_ice], these are compiler errors that causes Clippy to panic and crash. Usually involves high-level
35debugging, sometimes interacting directly with the upstream compiler. Difficult to fix but a great challenge that
36improves a lot developer workflows!
37
38- 🟧 [Suggestion causes bug][sugg_causes_bug], Clippy suggested code that changed logic in some silent way.
39Unacceptable, as this may have disastrous consequences. Easier to fix than ICEs
40
41- 🟨 [Suggestion causes error][sugg_causes_error], Clippy suggested code snippet that caused a compiler error
42when applied. We need to make sure that Clippy doesn't suggest using a variable twice at the same time or similar
43easy-to-happen occurrences.
44
45- 🟩 [False positives][false_positive], a lint should not have fired, the easiest of them all, as this is "just"
46identifying the root of a false positive and making an exception for those cases.
47
48Note that false negatives do not have priority unless the case is very clear, as they are a feature-request in a
49trench coat.
50
51[search_ice]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc+state%3Aopen+label%3A%22I-ICE%22
52[sugg_causes_bug]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-bug
53[sugg_causes_error]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-error%20
54[false_positive]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-false-positive
55[tracking_issue]: https://github.com/rust-lang/rust-clippy/issues/15086
src/tools/clippy/book/src/lint_configuration.md+23-1
......@@ -488,6 +488,13 @@ The maximum cognitive complexity a function can have
488488## `disallowed-macros`
489489The list of disallowed macros, written as fully qualified paths.
490490
491**Fields:**
492- `path` (required): the fully qualified path to the macro that should be disallowed
493- `reason` (optional): explanation why this macro is disallowed
494- `replacement` (optional): suggested alternative macro
495- `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry
496 if the path doesn't exist, instead of emitting an error
497
491498**Default Value:** `[]`
492499
493500---
......@@ -498,6 +505,13 @@ The list of disallowed macros, written as fully qualified paths.
498505## `disallowed-methods`
499506The list of disallowed methods, written as fully qualified paths.
500507
508**Fields:**
509- `path` (required): the fully qualified path to the method that should be disallowed
510- `reason` (optional): explanation why this method is disallowed
511- `replacement` (optional): suggested alternative method
512- `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry
513 if the path doesn't exist, instead of emitting an error
514
501515**Default Value:** `[]`
502516
503517---
......@@ -520,6 +534,13 @@ default configuration of Clippy. By default, any configuration will replace the
520534## `disallowed-types`
521535The list of disallowed types, written as fully qualified paths.
522536
537**Fields:**
538- `path` (required): the fully qualified path to the type that should be disallowed
539- `reason` (optional): explanation why this type is disallowed
540- `replacement` (optional): suggested alternative type
541- `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry
542 if the path doesn't exist, instead of emitting an error
543
523544**Default Value:** `[]`
524545
525546---
......@@ -651,13 +672,14 @@ The maximum size of the `Err`-variant in a `Result` returned from a function
651672
652673
653674## `lint-commented-code`
654Whether collapsible `if` chains are linted if they contain comments inside the parts
675Whether collapsible `if` and `else if` chains are linted if they contain comments inside the parts
655676that would be collapsed.
656677
657678**Default Value:** `false`
658679
659680---
660681**Affected lints:**
682* [`collapsible_else_if`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if)
661683* [`collapsible_if`](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if)
662684
663685
src/tools/clippy/clippy_config/Cargo.toml+1-1
......@@ -1,6 +1,6 @@
11[package]
22name = "clippy_config"
3version = "0.1.89"
3version = "0.1.90"
44edition = "2024"
55publish = false
66
src/tools/clippy/clippy_config/src/conf.rs+23-2
......@@ -575,10 +575,24 @@ define_Conf! {
575575 #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)]
576576 cyclomatic_complexity_threshold: u64 = 25,
577577 /// The list of disallowed macros, written as fully qualified paths.
578 ///
579 /// **Fields:**
580 /// - `path` (required): the fully qualified path to the macro that should be disallowed
581 /// - `reason` (optional): explanation why this macro is disallowed
582 /// - `replacement` (optional): suggested alternative macro
583 /// - `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry
584 /// if the path doesn't exist, instead of emitting an error
578585 #[disallowed_paths_allow_replacements = true]
579586 #[lints(disallowed_macros)]
580587 disallowed_macros: Vec<DisallowedPath> = Vec::new(),
581588 /// The list of disallowed methods, written as fully qualified paths.
589 ///
590 /// **Fields:**
591 /// - `path` (required): the fully qualified path to the method that should be disallowed
592 /// - `reason` (optional): explanation why this method is disallowed
593 /// - `replacement` (optional): suggested alternative method
594 /// - `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry
595 /// if the path doesn't exist, instead of emitting an error
582596 #[disallowed_paths_allow_replacements = true]
583597 #[lints(disallowed_methods)]
584598 disallowed_methods: Vec<DisallowedPath> = Vec::new(),
......@@ -588,6 +602,13 @@ define_Conf! {
588602 #[lints(disallowed_names)]
589603 disallowed_names: Vec<String> = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect(),
590604 /// The list of disallowed types, written as fully qualified paths.
605 ///
606 /// **Fields:**
607 /// - `path` (required): the fully qualified path to the type that should be disallowed
608 /// - `reason` (optional): explanation why this type is disallowed
609 /// - `replacement` (optional): suggested alternative type
610 /// - `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry
611 /// if the path doesn't exist, instead of emitting an error
591612 #[disallowed_paths_allow_replacements = true]
592613 #[lints(disallowed_types)]
593614 disallowed_types: Vec<DisallowedPath> = Vec::new(),
......@@ -641,9 +662,9 @@ define_Conf! {
641662 /// The maximum size of the `Err`-variant in a `Result` returned from a function
642663 #[lints(result_large_err)]
643664 large_error_threshold: u64 = 128,
644 /// Whether collapsible `if` chains are linted if they contain comments inside the parts
665 /// Whether collapsible `if` and `else if` chains are linted if they contain comments inside the parts
645666 /// that would be collapsed.
646 #[lints(collapsible_if)]
667 #[lints(collapsible_else_if, collapsible_if)]
647668 lint_commented_code: bool = false,
648669 /// Whether to suggest reordering constructor fields when initializers are present.
649670 /// DEPRECATED CONFIGURATION: lint-inconsistent-struct-field-initializers
src/tools/clippy/clippy_dev/src/lint.rs+2-2
......@@ -13,7 +13,7 @@ pub fn run<'a>(path: &str, edition: &str, args: impl Iterator<Item = &'a String>
1313
1414 if is_file {
1515 exit_if_err(
16 Command::new(env::var("CARGO").unwrap_or("cargo".into()))
16 Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
1717 .args(["run", "--bin", "clippy-driver", "--"])
1818 .args(["-L", "./target/debug"])
1919 .args(["-Z", "no-codegen"])
......@@ -26,7 +26,7 @@ pub fn run<'a>(path: &str, edition: &str, args: impl Iterator<Item = &'a String>
2626 );
2727 } else {
2828 exit_if_err(
29 Command::new(env::var("CARGO").unwrap_or("cargo".into()))
29 Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
3030 .arg("build")
3131 .status(),
3232 );
src/tools/clippy/clippy_dev/src/release.rs+1
......@@ -5,6 +5,7 @@ static CARGO_TOML_FILES: &[&str] = &[
55 "clippy_config/Cargo.toml",
66 "clippy_lints/Cargo.toml",
77 "clippy_utils/Cargo.toml",
8 "declare_clippy_lint/Cargo.toml",
89 "Cargo.toml",
910];
1011
src/tools/clippy/clippy_dev/src/serve.rs+1-1
......@@ -28,7 +28,7 @@ pub fn run(port: u16, lint: Option<String>) -> ! {
2828 .map(mtime);
2929
3030 if times.iter().any(|&time| index_time < time) {
31 Command::new(env::var("CARGO").unwrap_or("cargo".into()))
31 Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
3232 .arg("collect-metadata")
3333 .spawn()
3434 .unwrap()
src/tools/clippy/clippy_dev/src/update_lints.rs+170-121
......@@ -2,11 +2,11 @@ use crate::utils::{
22 ErrAction, File, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, expect_action, update_text_region_fn,
33};
44use itertools::Itertools;
5use rustc_lexer::{LiteralKind, TokenKind, tokenize};
65use std::collections::HashSet;
76use std::fmt::Write;
7use std::fs;
88use std::ops::Range;
9use std::path::{Path, PathBuf};
9use std::path::{self, Path, PathBuf};
1010use walkdir::{DirEntry, WalkDir};
1111
1212const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
......@@ -37,123 +37,164 @@ pub fn generate_lint_files(
3737 deprecated: &[DeprecatedLint],
3838 renamed: &[RenamedLint],
3939) {
40 FileUpdater::default().update_files_checked(
40 let mut updater = FileUpdater::default();
41 updater.update_file_checked(
4142 "cargo dev update_lints",
4243 update_mode,
43 &mut [
44 (
45 "README.md",
46 &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
47 write!(dst, "{}", round_to_fifty(lints.len())).unwrap();
48 }),
49 ),
50 (
51 "book/src/README.md",
52 &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
53 write!(dst, "{}", round_to_fifty(lints.len())).unwrap();
54 }),
55 ),
56 (
57 "CHANGELOG.md",
58 &mut update_text_region_fn(
59 "<!-- begin autogenerated links to lint list -->\n",
60 "<!-- end autogenerated links to lint list -->",
61 |dst| {
62 for lint in lints
63 .iter()
64 .map(|l| &*l.name)
65 .chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::")))
66 .chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::")))
67 .sorted()
68 {
69 writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
70 }
71 },
72 ),
73 ),
74 (
75 "clippy_lints/src/lib.rs",
76 &mut update_text_region_fn(
77 "// begin lints modules, do not remove this comment, it's used in `update_lints`\n",
78 "// end lints modules, do not remove this comment, it's used in `update_lints`",
79 |dst| {
80 for lint_mod in lints.iter().map(|l| &l.module).sorted().dedup() {
81 writeln!(dst, "mod {lint_mod};").unwrap();
82 }
83 },
84 ),
85 ),
86 ("clippy_lints/src/declared_lints.rs", &mut |_, src, dst| {
87 dst.push_str(GENERATED_FILE_COMMENT);
88 dst.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n");
89 for (module_name, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() {
90 writeln!(dst, " crate::{module_name}::{lint_name}_INFO,").unwrap();
91 }
92 dst.push_str("];\n");
93 UpdateStatus::from_changed(src != dst)
94 }),
95 ("clippy_lints/src/deprecated_lints.rs", &mut |_, src, dst| {
96 let mut searcher = RustSearcher::new(src);
97 assert!(
98 searcher.find_token(Token::Ident("declare_with_version"))
99 && searcher.find_token(Token::Ident("declare_with_version")),
100 "error reading deprecated lints"
101 );
102 dst.push_str(&src[..searcher.pos() as usize]);
103 dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n");
104 for lint in deprecated {
105 write!(
106 dst,
107 " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
108 lint.version, lint.name, lint.reason,
109 )
110 .unwrap();
44 "README.md",
45 &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
46 write!(dst, "{}", round_to_fifty(lints.len())).unwrap();
47 }),
48 );
49 updater.update_file_checked(
50 "cargo dev update_lints",
51 update_mode,
52 "book/src/README.md",
53 &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
54 write!(dst, "{}", round_to_fifty(lints.len())).unwrap();
55 }),
56 );
57 updater.update_file_checked(
58 "cargo dev update_lints",
59 update_mode,
60 "CHANGELOG.md",
61 &mut update_text_region_fn(
62 "<!-- begin autogenerated links to lint list -->\n",
63 "<!-- end autogenerated links to lint list -->",
64 |dst| {
65 for lint in lints
66 .iter()
67 .map(|l| &*l.name)
68 .chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::")))
69 .chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::")))
70 .sorted()
71 {
72 writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
11173 }
112 dst.push_str(
113 "]}\n\n\
74 },
75 ),
76 );
77 updater.update_file_checked(
78 "cargo dev update_lints",
79 update_mode,
80 "clippy_lints/src/deprecated_lints.rs",
81 &mut |_, src, dst| {
82 let mut searcher = RustSearcher::new(src);
83 assert!(
84 searcher.find_token(Token::Ident("declare_with_version"))
85 && searcher.find_token(Token::Ident("declare_with_version")),
86 "error reading deprecated lints"
87 );
88 dst.push_str(&src[..searcher.pos() as usize]);
89 dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n");
90 for lint in deprecated {
91 write!(
92 dst,
93 " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
94 lint.version, lint.name, lint.reason,
95 )
96 .unwrap();
97 }
98 dst.push_str(
99 "]}\n\n\
114100 #[rustfmt::skip]\n\
115101 declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\
116102 ",
117 );
118 for lint in renamed {
119 write!(
120 dst,
121 " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
122 lint.version, lint.old_name, lint.new_name,
123 )
124 .unwrap();
103 );
104 for lint in renamed {
105 write!(
106 dst,
107 " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
108 lint.version, lint.old_name, lint.new_name,
109 )
110 .unwrap();
111 }
112 dst.push_str("]}\n");
113 UpdateStatus::from_changed(src != dst)
114 },
115 );
116 updater.update_file_checked(
117 "cargo dev update_lints",
118 update_mode,
119 "tests/ui/deprecated.rs",
120 &mut |_, src, dst| {
121 dst.push_str(GENERATED_FILE_COMMENT);
122 for lint in deprecated {
123 writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap();
124 }
125 dst.push_str("\nfn main() {}\n");
126 UpdateStatus::from_changed(src != dst)
127 },
128 );
129 updater.update_file_checked(
130 "cargo dev update_lints",
131 update_mode,
132 "tests/ui/rename.rs",
133 &mut move |_, src, dst| {
134 let mut seen_lints = HashSet::new();
135 dst.push_str(GENERATED_FILE_COMMENT);
136 dst.push_str("#![allow(clippy::duplicated_attributes)]\n");
137 for lint in renamed {
138 if seen_lints.insert(&lint.new_name) {
139 writeln!(dst, "#![allow({})]", lint.new_name).unwrap();
125140 }
126 dst.push_str("]}\n");
127 UpdateStatus::from_changed(src != dst)
128 }),
129 ("tests/ui/deprecated.rs", &mut |_, src, dst| {
130 dst.push_str(GENERATED_FILE_COMMENT);
131 for lint in deprecated {
132 writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap();
141 }
142 seen_lints.clear();
143 for lint in renamed {
144 if seen_lints.insert(&lint.old_name) {
145 writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap();
133146 }
134 dst.push_str("\nfn main() {}\n");
135 UpdateStatus::from_changed(src != dst)
136 }),
137 ("tests/ui/rename.rs", &mut move |_, src, dst| {
138 let mut seen_lints = HashSet::new();
139 dst.push_str(GENERATED_FILE_COMMENT);
140 dst.push_str("#![allow(clippy::duplicated_attributes)]\n");
141 for lint in renamed {
142 if seen_lints.insert(&lint.new_name) {
143 writeln!(dst, "#![allow({})]", lint.new_name).unwrap();
147 }
148 dst.push_str("\nfn main() {}\n");
149 UpdateStatus::from_changed(src != dst)
150 },
151 );
152 for (crate_name, lints) in lints.iter().into_group_map_by(|&l| {
153 let Some(path::Component::Normal(name)) = l.path.components().next() else {
154 // All paths should start with `{crate_name}/src` when parsed from `find_lint_decls`
155 panic!("internal error: can't read crate name from path `{}`", l.path.display());
156 };
157 name
158 }) {
159 updater.update_file_checked(
160 "cargo dev update_lints",
161 update_mode,
162 Path::new(crate_name).join("src/lib.rs"),
163 &mut update_text_region_fn(
164 "// begin lints modules, do not remove this comment, it's used in `update_lints`\n",
165 "// end lints modules, do not remove this comment, it's used in `update_lints`",
166 |dst| {
167 for lint_mod in lints
168 .iter()
169 .filter(|l| !l.module.is_empty())
170 .map(|l| l.module.split_once("::").map_or(&*l.module, |x| x.0))
171 .sorted()
172 .dedup()
173 {
174 writeln!(dst, "mod {lint_mod};").unwrap();
144175 }
145 }
146 seen_lints.clear();
147 for lint in renamed {
148 if seen_lints.insert(&lint.old_name) {
149 writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap();
176 },
177 ),
178 );
179 updater.update_file_checked(
180 "cargo dev update_lints",
181 update_mode,
182 Path::new(crate_name).join("src/declared_lints.rs"),
183 &mut |_, src, dst| {
184 dst.push_str(GENERATED_FILE_COMMENT);
185 dst.push_str("pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[\n");
186 for (module_path, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() {
187 if module_path.is_empty() {
188 writeln!(dst, " crate::{lint_name}_INFO,").unwrap();
189 } else {
190 writeln!(dst, " crate::{module_path}::{lint_name}_INFO,").unwrap();
150191 }
151192 }
152 dst.push_str("\nfn main() {}\n");
193 dst.push_str("];\n");
153194 UpdateStatus::from_changed(src != dst)
154 }),
155 ],
156 );
195 },
196 );
197 }
157198}
158199
159200fn round_to_fifty(count: usize) -> usize {
......@@ -187,13 +228,25 @@ pub struct RenamedLint {
187228pub fn find_lint_decls() -> Vec<Lint> {
188229 let mut lints = Vec::with_capacity(1000);
189230 let mut contents = String::new();
190 for (file, module) in read_src_with_module("clippy_lints/src".as_ref()) {
191 parse_clippy_lint_decls(
192 file.path(),
193 File::open_read_to_cleared_string(file.path(), &mut contents),
194 &module,
195 &mut lints,
196 );
231 for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") {
232 let e = expect_action(e, ErrAction::Read, ".");
233 if !expect_action(e.file_type(), ErrAction::Read, ".").is_dir() {
234 continue;
235 }
236 let Ok(mut name) = e.file_name().into_string() else {
237 continue;
238 };
239 if name.starts_with("clippy_lints") && name != "clippy_lints_internal" {
240 name.push_str("/src");
241 for (file, module) in read_src_with_module(name.as_ref()) {
242 parse_clippy_lint_decls(
243 file.path(),
244 File::open_read_to_cleared_string(file.path(), &mut contents),
245 &module,
246 &mut lints,
247 );
248 }
249 }
197250 }
198251 lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name));
199252 lints
......@@ -205,7 +258,7 @@ fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator<Item = (DirE
205258 let e = expect_action(e, ErrAction::Read, src_root);
206259 let path = e.path().as_os_str().as_encoded_bytes();
207260 if let Some(path) = path.strip_suffix(b".rs")
208 && let Some(path) = path.get("clippy_lints/src/".len()..)
261 && let Some(path) = path.get(src_root.as_os_str().len() + 1..)
209262 {
210263 if path == b"lib" {
211264 Some((e, String::new()))
......@@ -333,17 +386,13 @@ pub fn read_deprecated_lints() -> (Vec<DeprecatedLint>, Vec<RenamedLint>) {
333386
334387/// Removes the line splices and surrounding quotes from a string literal
335388fn parse_str_lit(s: &str) -> String {
336 let (s, mode) = if let Some(s) = s.strip_prefix("r") {
337 (s.trim_matches('#'), rustc_literal_escaper::Mode::RawStr)
338 } else {
339 (s, rustc_literal_escaper::Mode::Str)
340 };
389 let s = s.strip_prefix("r").unwrap_or(s).trim_matches('#');
341390 let s = s
342391 .strip_prefix('"')
343392 .and_then(|s| s.strip_suffix('"'))
344393 .unwrap_or_else(|| panic!("expected quoted string, found `{s}`"));
345394 let mut res = String::with_capacity(s.len());
346 rustc_literal_escaper::unescape_str(s, |range, ch| {
395 rustc_literal_escaper::unescape_str(s, &mut |_, ch| {
347396 if let Ok(ch) = ch {
348397 res.push(ch);
349398 }
src/tools/clippy/clippy_dev/src/utils.rs-15
......@@ -383,21 +383,6 @@ impl FileUpdater {
383383 self.update_file_checked_inner(tool, mode, path.as_ref(), update);
384384 }
385385
386 #[expect(clippy::type_complexity)]
387 pub fn update_files_checked(
388 &mut self,
389 tool: &str,
390 mode: UpdateMode,
391 files: &mut [(
392 impl AsRef<Path>,
393 &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus,
394 )],
395 ) {
396 for (path, update) in files {
397 self.update_file_checked_inner(tool, mode, path.as_ref(), update);
398 }
399 }
400
401386 pub fn update_file(
402387 &mut self,
403388 path: impl AsRef<Path>,
src/tools/clippy/clippy_lints/Cargo.toml+2-1
......@@ -1,6 +1,6 @@
11[package]
22name = "clippy_lints"
3version = "0.1.89"
3version = "0.1.90"
44description = "A bunch of helpful lints to avoid common pitfalls in Rust"
55repository = "https://github.com/rust-lang/rust-clippy"
66readme = "README.md"
......@@ -13,6 +13,7 @@ arrayvec = { version = "0.7", default-features = false }
1313cargo_metadata = "0.18"
1414clippy_config = { path = "../clippy_config" }
1515clippy_utils = { path = "../clippy_utils" }
16declare_clippy_lint = { path = "../declare_clippy_lint" }
1617itertools = "0.12"
1718quine-mc_cluskey = "0.2"
1819regex-syntax = "0.8"
src/tools/clippy/clippy_lints/src/attrs/inline_always.rs+2-2
......@@ -1,10 +1,10 @@
11use super::INLINE_ALWAYS;
22use clippy_utils::diagnostics::span_lint;
3use rustc_attr_data_structures::{find_attr, AttributeKind, InlineAttr};
3use rustc_attr_data_structures::{AttributeKind, InlineAttr, find_attr};
44use rustc_hir::Attribute;
55use rustc_lint::LateContext;
6use rustc_span::symbol::Symbol;
76use rustc_span::Span;
7use rustc_span::symbol::Symbol;
88
99pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) {
1010 if span.from_expansion() {
src/tools/clippy/clippy_lints/src/attrs/mod.rs+1-1
......@@ -207,7 +207,7 @@ declare_clippy_lint! {
207207declare_clippy_lint! {
208208 /// ### What it does
209209 /// Checks for usage of the `#[allow]` attribute and suggests replacing it with
210 /// the `#[expect]` (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html))
210 /// the `#[expect]` attribute (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html))
211211 ///
212212 /// This lint only warns outer attributes (`#[allow]`), as inner attributes
213213 /// (`#![allow]`) are usually used to enable or disable lints on a global scale.
src/tools/clippy/clippy_lints/src/attrs/utils.rs+7-5
......@@ -46,11 +46,13 @@ pub(super) fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> b
4646}
4747
4848fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, block: &Block<'_>) -> bool {
49 block.stmts.first().map_or(
50 block
51 .expr
52 .as_ref()
53 .is_some_and(|e| is_relevant_expr(cx, typeck_results, e)),
49 block.stmts.first().map_or_else(
50 || {
51 block
52 .expr
53 .as_ref()
54 .is_some_and(|e| is_relevant_expr(cx, typeck_results, e))
55 },
5456 |stmt| match &stmt.kind {
5557 StmtKind::Let(_) => true,
5658 StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs+12-3
......@@ -2,9 +2,9 @@ use crate::reference::DEREF_ADDROF;
22use clippy_utils::diagnostics::span_lint_and_then;
33use clippy_utils::source::SpanRangeExt;
44use clippy_utils::ty::implements_trait;
5use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed, is_mutable};
5use clippy_utils::{get_parent_expr, is_expr_temporary_value, is_from_proc_macro, is_lint_allowed, is_mutable};
66use rustc_errors::Applicability;
7use rustc_hir::{BorrowKind, ExprKind, UnOp};
7use rustc_hir::{BorrowKind, Expr, ExprKind, Node, UnOp};
88use rustc_lint::{LateContext, LateLintPass};
99use rustc_middle::mir::Mutability;
1010use rustc_middle::ty;
......@@ -48,7 +48,7 @@ declare_clippy_lint! {
4848declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]);
4949
5050impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
51 fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) {
51 fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) {
5252 if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, addrof_target) = e.kind
5353 && let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind
5454 && !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..))
......@@ -76,6 +76,9 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
7676 && let e_ty = cx.typeck_results().expr_ty_adjusted(e)
7777 // check if the reference is coercing to a mutable reference
7878 && (!matches!(e_ty.kind(), ty::Ref(_, _, Mutability::Mut)) || is_mutable(cx, deref_target))
79 // If the new borrow might be itself borrowed mutably and the original reference is not a temporary
80 // value, do not propose to use it directly.
81 && (is_expr_temporary_value(cx, deref_target) || !potentially_bound_to_mutable_ref(cx, e))
7982 && let Some(deref_text) = deref_target.span.get_source_text(cx)
8083 {
8184 span_lint_and_then(
......@@ -110,3 +113,9 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
110113 }
111114 }
112115}
116
117/// Checks if `expr` is used as part of a `let` statement containing a `ref mut` binding.
118fn potentially_bound_to_mutable_ref<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
119 matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::LetStmt(let_stmt)
120 if let_stmt.pat.contains_explicit_ref_binding() == Some(Mutability::Mut))
121}
src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs+1-1
......@@ -168,7 +168,7 @@ fn pow_call_result_sign(cx: &LateContext<'_>, base: &Expr<'_>, exponent: &Expr<'
168168
169169 // Rust's integer pow() functions take an unsigned exponent.
170170 let exponent_val = get_const_unsigned_int_eval(cx, exponent, None);
171 let exponent_is_even = exponent_val.map(|val| val % 2 == 0);
171 let exponent_is_even = exponent_val.map(|val| val.is_multiple_of(2));
172172
173173 match (base_sign, exponent_is_even) {
174174 // Non-negative bases always return non-negative results, ignoring overflow.
src/tools/clippy/clippy_lints/src/collapsible_if.rs+93-34
......@@ -1,13 +1,16 @@
11use clippy_config::Conf;
2use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
2use clippy_utils::diagnostics::span_lint_and_then;
33use clippy_utils::msrvs::{self, Msrv};
4use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block, snippet_block_with_applicability};
4use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability};
5use clippy_utils::{span_contains_non_whitespace, tokenize_with_text};
56use rustc_ast::BinOpKind;
67use rustc_errors::Applicability;
78use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind};
9use rustc_lexer::TokenKind;
810use rustc_lint::{LateContext, LateLintPass};
911use rustc_session::impl_lint_pass;
10use rustc_span::Span;
12use rustc_span::source_map::SourceMap;
13use rustc_span::{BytePos, Span};
1114
1215declare_clippy_lint! {
1316 /// ### What it does
......@@ -90,35 +93,74 @@ impl CollapsibleIf {
9093 }
9194 }
9295
93 fn check_collapsible_else_if(cx: &LateContext<'_>, then_span: Span, else_block: &Block<'_>) {
94 if !block_starts_with_comment(cx, else_block)
95 && let Some(else_) = expr_block(else_block)
96 fn check_collapsible_else_if(&self, cx: &LateContext<'_>, then_span: Span, else_block: &Block<'_>) {
97 if let Some(else_) = expr_block(else_block)
9698 && cx.tcx.hir_attrs(else_.hir_id).is_empty()
9799 && !else_.span.from_expansion()
98 && let ExprKind::If(..) = else_.kind
100 && let ExprKind::If(else_if_cond, ..) = else_.kind
101 && !block_starts_with_significant_tokens(cx, else_block, else_, self.lint_commented_code)
99102 {
100 // Prevent "elseif"
101 // Check that the "else" is followed by whitespace
102 let up_to_else = then_span.between(else_block.span);
103 let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() {
104 !c.is_whitespace()
105 } else {
106 false
107 };
108
109 let mut applicability = Applicability::MachineApplicable;
110 span_lint_and_sugg(
103 span_lint_and_then(
111104 cx,
112105 COLLAPSIBLE_ELSE_IF,
113106 else_block.span,
114107 "this `else { if .. }` block can be collapsed",
115 "collapse nested if block",
116 format!(
117 "{}{}",
118 if requires_space { " " } else { "" },
119 snippet_block_with_applicability(cx, else_.span, "..", Some(else_block.span), &mut applicability)
120 ),
121 applicability,
108 |diag| {
109 let up_to_else = then_span.between(else_block.span);
110 let else_before_if = else_.span.shrink_to_lo().with_hi(else_if_cond.span.lo() - BytePos(1));
111 if self.lint_commented_code
112 && let Some(else_keyword_span) =
113 span_extract_keyword(cx.tcx.sess.source_map(), up_to_else, "else")
114 && let Some(else_if_keyword_span) =
115 span_extract_keyword(cx.tcx.sess.source_map(), else_before_if, "if")
116 {
117 let else_keyword_span = else_keyword_span.with_leading_whitespace(cx).into_span();
118 let else_open_bracket = else_block.span.split_at(1).0.with_leading_whitespace(cx).into_span();
119 let else_closing_bracket = {
120 let end = else_block.span.shrink_to_hi();
121 end.with_lo(end.lo() - BytePos(1))
122 .with_leading_whitespace(cx)
123 .into_span()
124 };
125 let sugg = vec![
126 // Remove the outer else block `else`
127 (else_keyword_span, String::new()),
128 // Replace the inner `if` by `else if`
129 (else_if_keyword_span, String::from("else if")),
130 // Remove the outer else block `{`
131 (else_open_bracket, String::new()),
132 // Remove the outer else block '}'
133 (else_closing_bracket, String::new()),
134 ];
135 diag.multipart_suggestion("collapse nested if block", sugg, Applicability::MachineApplicable);
136 return;
137 }
138
139 // Prevent "elseif"
140 // Check that the "else" is followed by whitespace
141 let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() {
142 !c.is_whitespace()
143 } else {
144 false
145 };
146 let mut applicability = Applicability::MachineApplicable;
147 diag.span_suggestion(
148 else_block.span,
149 "collapse nested if block",
150 format!(
151 "{}{}",
152 if requires_space { " " } else { "" },
153 snippet_block_with_applicability(
154 cx,
155 else_.span,
156 "..",
157 Some(else_block.span),
158 &mut applicability
159 )
160 ),
161 applicability,
162 );
163 },
122164 );
123165 }
124166 }
......@@ -130,7 +172,7 @@ impl CollapsibleIf {
130172 && self.eligible_condition(cx, check_inner)
131173 && let ctxt = expr.span.ctxt()
132174 && inner.span.ctxt() == ctxt
133 && (self.lint_commented_code || !block_starts_with_comment(cx, then))
175 && !block_starts_with_significant_tokens(cx, then, inner, self.lint_commented_code)
134176 {
135177 span_lint_and_then(
136178 cx,
......@@ -141,7 +183,7 @@ impl CollapsibleIf {
141183 let then_open_bracket = then.span.split_at(1).0.with_leading_whitespace(cx).into_span();
142184 let then_closing_bracket = {
143185 let end = then.span.shrink_to_hi();
144 end.with_lo(end.lo() - rustc_span::BytePos(1))
186 end.with_lo(end.lo() - BytePos(1))
145187 .with_leading_whitespace(cx)
146188 .into_span()
147189 };
......@@ -179,7 +221,7 @@ impl LateLintPass<'_> for CollapsibleIf {
179221 if let Some(else_) = else_
180222 && let ExprKind::Block(else_, None) = else_.kind
181223 {
182 Self::check_collapsible_else_if(cx, then.span, else_);
224 self.check_collapsible_else_if(cx, then.span, else_);
183225 } else if else_.is_none()
184226 && self.eligible_condition(cx, cond)
185227 && let ExprKind::Block(then, None) = then.kind
......@@ -190,12 +232,16 @@ impl LateLintPass<'_> for CollapsibleIf {
190232 }
191233}
192234
193fn block_starts_with_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool {
194 // We trim all opening braces and whitespaces and then check if the next string is a comment.
195 let trimmed_block_text = snippet_block(cx, block.span, "..", None)
196 .trim_start_matches(|c: char| c.is_whitespace() || c == '{')
197 .to_owned();
198 trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*")
235// Check that nothing significant can be found but whitespaces between the initial `{` of `block`
236// and the beginning of `stop_at`.
237fn block_starts_with_significant_tokens(
238 cx: &LateContext<'_>,
239 block: &Block<'_>,
240 stop_at: &Expr<'_>,
241 lint_commented_code: bool,
242) -> bool {
243 let span = block.span.split_at(1).1.until(stop_at.span);
244 span_contains_non_whitespace(cx, span, lint_commented_code)
199245}
200246
201247/// If `block` is a block with either one expression or a statement containing an expression,
......@@ -226,3 +272,16 @@ fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> {
226272 vec![]
227273 }
228274}
275
276fn span_extract_keyword(sm: &SourceMap, span: Span, keyword: &str) -> Option<Span> {
277 let snippet = sm.span_to_snippet(span).ok()?;
278 tokenize_with_text(&snippet)
279 .filter(|(t, s, _)| matches!(t, TokenKind::Ident if *s == keyword))
280 .map(|(_, _, inner)| {
281 span.split_at(u32::try_from(inner.start).unwrap())
282 .1
283 .split_at(u32::try_from(inner.end - inner.start).unwrap())
284 .0
285 })
286 .next()
287}
src/tools/clippy/clippy_lints/src/copies.rs+16-2
......@@ -11,7 +11,7 @@ use clippy_utils::{
1111use core::iter;
1212use core::ops::ControlFlow;
1313use rustc_errors::Applicability;
14use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind, intravisit};
14use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Node, Stmt, StmtKind, intravisit};
1515use rustc_lint::{LateContext, LateLintPass};
1616use rustc_middle::ty::TyCtxt;
1717use rustc_session::impl_lint_pass;
......@@ -295,7 +295,7 @@ fn lint_branches_sharing_code<'tcx>(
295295 sugg,
296296 Applicability::Unspecified,
297297 );
298 if !cx.typeck_results().expr_ty(expr).is_unit() {
298 if is_expr_parent_assignment(cx, expr) || !cx.typeck_results().expr_ty(expr).is_unit() {
299299 diag.note("the end suggestion probably needs some adjustments to use the expression result correctly");
300300 }
301301 }
......@@ -660,3 +660,17 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
660660 );
661661 }
662662}
663
664fn is_expr_parent_assignment(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
665 let parent = cx.tcx.parent_hir_node(expr.hir_id);
666 if let Node::LetStmt(LetStmt { init: Some(e), .. })
667 | Node::Expr(Expr {
668 kind: ExprKind::Assign(_, e, _),
669 ..
670 }) = parent
671 {
672 return e.hir_id == expr.hir_id;
673 }
674
675 false
676}
src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs deleted-168
......@@ -1,168 +0,0 @@
1#[macro_export]
2#[allow(clippy::crate_in_macro_def)]
3macro_rules! declare_clippy_lint {
4 (@
5 $(#[doc = $lit:literal])*
6 pub $lint_name:ident,
7 $level:ident,
8 $lintcategory:expr,
9 $desc:literal,
10 $version_expr:expr,
11 $version_lit:literal
12 $(, $eval_always: literal)?
13 ) => {
14 rustc_session::declare_tool_lint! {
15 $(#[doc = $lit])*
16 #[clippy::version = $version_lit]
17 pub clippy::$lint_name,
18 $level,
19 $desc,
20 report_in_external_macro:true
21 $(, @eval_always = $eval_always)?
22 }
23
24 pub(crate) static ${concat($lint_name, _INFO)}: &'static crate::LintInfo = &crate::LintInfo {
25 lint: &$lint_name,
26 category: $lintcategory,
27 explanation: concat!($($lit,"\n",)*),
28 location: concat!(file!(), "#L", line!()),
29 version: $version_expr
30 };
31 };
32 (
33 $(#[doc = $lit:literal])*
34 #[clippy::version = $version:literal]
35 pub $lint_name:ident,
36 restriction,
37 $desc:literal
38 $(, @eval_always = $eval_always: literal)?
39 ) => {
40 declare_clippy_lint! {@
41 $(#[doc = $lit])*
42 pub $lint_name, Allow, crate::LintCategory::Restriction, $desc,
43 Some($version), $version
44 $(, $eval_always)?
45 }
46 };
47 (
48 $(#[doc = $lit:literal])*
49 #[clippy::version = $version:literal]
50 pub $lint_name:ident,
51 style,
52 $desc:literal
53 $(, @eval_always = $eval_always: literal)?
54 ) => {
55 declare_clippy_lint! {@
56 $(#[doc = $lit])*
57 pub $lint_name, Warn, crate::LintCategory::Style, $desc,
58 Some($version), $version
59 $(, $eval_always)?
60 }
61 };
62 (
63 $(#[doc = $lit:literal])*
64 #[clippy::version = $version:literal]
65 pub $lint_name:ident,
66 correctness,
67 $desc:literal
68 $(, @eval_always = $eval_always: literal)?
69 ) => {
70 declare_clippy_lint! {@
71 $(#[doc = $lit])*
72 pub $lint_name, Deny, crate::LintCategory::Correctness, $desc,
73 Some($version), $version
74 $(, $eval_always)?
75
76 }
77 };
78 (
79 $(#[doc = $lit:literal])*
80 #[clippy::version = $version:literal]
81 pub $lint_name:ident,
82 perf,
83 $desc:literal
84 $(, @eval_always = $eval_always: literal)?
85 ) => {
86 declare_clippy_lint! {@
87 $(#[doc = $lit])*
88 pub $lint_name, Warn, crate::LintCategory::Perf, $desc,
89 Some($version), $version
90 $(, $eval_always)?
91 }
92 };
93 (
94 $(#[doc = $lit:literal])*
95 #[clippy::version = $version:literal]
96 pub $lint_name:ident,
97 complexity,
98 $desc:literal
99 $(, @eval_always = $eval_always: literal)?
100 ) => {
101 declare_clippy_lint! {@
102 $(#[doc = $lit])*
103 pub $lint_name, Warn, crate::LintCategory::Complexity, $desc,
104 Some($version), $version
105 $(, $eval_always)?
106 }
107 };
108 (
109 $(#[doc = $lit:literal])*
110 #[clippy::version = $version:literal]
111 pub $lint_name:ident,
112 suspicious,
113 $desc:literal
114 $(, @eval_always = $eval_always: literal)?
115 ) => {
116 declare_clippy_lint! {@
117 $(#[doc = $lit])*
118 pub $lint_name, Warn, crate::LintCategory::Suspicious, $desc,
119 Some($version), $version
120 $(, $eval_always)?
121 }
122 };
123 (
124 $(#[doc = $lit:literal])*
125 #[clippy::version = $version:literal]
126 pub $lint_name:ident,
127 nursery,
128 $desc:literal
129 $(, @eval_always = $eval_always: literal)?
130 ) => {
131 declare_clippy_lint! {@
132 $(#[doc = $lit])*
133 pub $lint_name, Allow, crate::LintCategory::Nursery, $desc,
134 Some($version), $version
135 $(, $eval_always)?
136 }
137 };
138 (
139 $(#[doc = $lit:literal])*
140 #[clippy::version = $version:literal]
141 pub $lint_name:ident,
142 pedantic,
143 $desc:literal
144 $(, @eval_always = $eval_always: literal)?
145 ) => {
146 declare_clippy_lint! {@
147 $(#[doc = $lit])*
148 pub $lint_name, Allow, crate::LintCategory::Pedantic, $desc,
149 Some($version), $version
150 $(, $eval_always)?
151 }
152 };
153 (
154 $(#[doc = $lit:literal])*
155 #[clippy::version = $version:literal]
156 pub $lint_name:ident,
157 cargo,
158 $desc:literal
159 $(, @eval_always = $eval_always: literal)?
160 ) => {
161 declare_clippy_lint! {@
162 $(#[doc = $lit])*
163 pub $lint_name, Allow, crate::LintCategory::Cargo, $desc,
164 Some($version), $version
165 $(, $eval_always)?
166 }
167 };
168}
src/tools/clippy/clippy_lints/src/declared_lints.rs+3-1
......@@ -2,7 +2,7 @@
22// Use that command to update this file and do not edit by hand.
33// Manual edits will be overwritten.
44
5pub static LINTS: &[&crate::LintInfo] = &[
5pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
66 crate::absolute_paths::ABSOLUTE_PATHS_INFO,
77 crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO,
88 crate::approx_const::APPROX_CONSTANT_INFO,
......@@ -112,6 +112,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
112112 crate::disallowed_names::DISALLOWED_NAMES_INFO,
113113 crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO,
114114 crate::disallowed_types::DISALLOWED_TYPES_INFO,
115 crate::doc::DOC_BROKEN_LINK_INFO,
115116 crate::doc::DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS_INFO,
116117 crate::doc::DOC_INCLUDE_WITHOUT_CFG_INFO,
117118 crate::doc::DOC_LAZY_CONTINUATION_INFO,
......@@ -590,6 +591,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
590591 crate::operators::IMPOSSIBLE_COMPARISONS_INFO,
591592 crate::operators::INEFFECTIVE_BIT_MASK_INFO,
592593 crate::operators::INTEGER_DIVISION_INFO,
594 crate::operators::MANUAL_IS_MULTIPLE_OF_INFO,
593595 crate::operators::MANUAL_MIDPOINT_INFO,
594596 crate::operators::MISREFACTORED_ASSIGN_OP_INFO,
595597 crate::operators::MODULO_ARITHMETIC_INFO,
src/tools/clippy/clippy_lints/src/disallowed_macros.rs+3
......@@ -40,6 +40,9 @@ declare_clippy_lint! {
4040 /// # When using an inline table, can add a `reason` for why the macro
4141 /// # is disallowed.
4242 /// { path = "serde::Serialize", reason = "no serializing" },
43 /// # This would normally error if the path is incorrect, but with `allow-invalid` = `true`,
44 /// # it will be silently ignored
45 /// { path = "std::invalid_macro", reason = "use alternative instead", allow-invalid = true }
4346 /// ]
4447 /// ```
4548 /// ```no_run
src/tools/clippy/clippy_lints/src/disallowed_methods.rs+3
......@@ -34,6 +34,9 @@ declare_clippy_lint! {
3434 /// { path = "std::vec::Vec::leak", reason = "no leaking memory" },
3535 /// # Can also add a `replacement` that will be offered as a suggestion.
3636 /// { path = "std::sync::Mutex::new", reason = "prefer faster & simpler non-poisonable mutex", replacement = "parking_lot::Mutex::new" },
37 /// # This would normally error if the path is incorrect, but with `allow-invalid` = `true`,
38 /// # it will be silently ignored
39 /// { path = "std::fs::InvalidPath", reason = "use alternative instead", allow-invalid = true },
3740 /// ]
3841 /// ```
3942 ///
src/tools/clippy/clippy_lints/src/disallowed_types.rs+3
......@@ -35,6 +35,9 @@ declare_clippy_lint! {
3535 /// { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
3636 /// # Can also add a `replacement` that will be offered as a suggestion.
3737 /// { path = "std::sync::Mutex", reason = "prefer faster & simpler non-poisonable mutex", replacement = "parking_lot::Mutex" },
38 /// # This would normally error if the path is incorrect, but with `allow-invalid` = `true`,
39 /// # it will be silently ignored
40 /// { path = "std::invalid::Type", reason = "use alternative instead", allow-invalid = true }
3841 /// ]
3942 /// ```
4043 ///
src/tools/clippy/clippy_lints/src/doc/broken_link.rs created+83
......@@ -0,0 +1,83 @@
1use clippy_utils::diagnostics::span_lint;
2use pulldown_cmark::BrokenLink as PullDownBrokenLink;
3use rustc_lint::LateContext;
4use rustc_resolve::rustdoc::{DocFragment, source_span_for_markdown_range};
5use rustc_span::{BytePos, Pos, Span};
6
7use super::DOC_BROKEN_LINK;
8
9/// Scan and report broken link on documents.
10/// It ignores false positives detected by `pulldown_cmark`, and only
11/// warns users when the broken link is consider a URL.
12// NOTE: We don't check these other cases because
13// rustdoc itself will check and warn about it:
14// - When a link url is broken across multiple lines in the URL path part
15// - When a link tag is missing the close parenthesis character at the end.
16// - When a link has whitespace within the url link.
17pub fn check(cx: &LateContext<'_>, bl: &PullDownBrokenLink<'_>, doc: &str, fragments: &[DocFragment]) {
18 warn_if_broken_link(cx, bl, doc, fragments);
19}
20
21fn warn_if_broken_link(cx: &LateContext<'_>, bl: &PullDownBrokenLink<'_>, doc: &str, fragments: &[DocFragment]) {
22 if let Some((span, _)) = source_span_for_markdown_range(cx.tcx, doc, &bl.span, fragments) {
23 let mut len = 0;
24
25 // grab raw link data
26 let (_, raw_link) = doc.split_at(bl.span.start);
27
28 // strip off link text part
29 let raw_link = match raw_link.split_once(']') {
30 None => return,
31 Some((prefix, suffix)) => {
32 len += prefix.len() + 1;
33 suffix
34 },
35 };
36
37 let raw_link = match raw_link.split_once('(') {
38 None => return,
39 Some((prefix, suffix)) => {
40 if !prefix.is_empty() {
41 // there is text between ']' and '(' chars, so it is not a valid link
42 return;
43 }
44 len += prefix.len() + 1;
45 suffix
46 },
47 };
48
49 if raw_link.starts_with("(http") {
50 // reduce chances of false positive reports
51 // by limiting this checking only to http/https links.
52 return;
53 }
54
55 for c in raw_link.chars() {
56 if c == ')' {
57 // it is a valid link
58 return;
59 }
60
61 if c == '\n' {
62 report_broken_link(cx, span, len);
63 break;
64 }
65
66 len += 1;
67 }
68 }
69}
70
71fn report_broken_link(cx: &LateContext<'_>, frag_span: Span, offset: usize) {
72 let start = frag_span.lo();
73 let end = start + BytePos::from_usize(offset);
74
75 let span = Span::new(start, end, frag_span.ctxt(), frag_span.parent());
76
77 span_lint(
78 cx,
79 DOC_BROKEN_LINK,
80 span,
81 "possible broken doc link: broken across multiple lines",
82 );
83}
src/tools/clippy/clippy_lints/src/doc/doc_suspicious_footnotes.rs+1-5
......@@ -49,11 +49,7 @@ pub fn check(cx: &LateContext<'_>, doc: &str, range: Range<usize>, fragments: &F
4949 .filter(|attr| attr.span().overlaps(this_fragment.span))
5050 .rev()
5151 .find_map(|attr| {
52 Some((
53 attr,
54 attr.doc_str_and_comment_kind()?,
55 attr.doc_resolution_scope()?,
56 ))
52 Some((attr, attr.doc_str_and_comment_kind()?, attr.doc_resolution_scope()?))
5753 })
5854 .unwrap();
5955 let (to_add, terminator) = match (doc_attr_comment_kind, attr_style) {
src/tools/clippy/clippy_lints/src/doc/mod.rs+43-7
......@@ -24,6 +24,7 @@ use rustc_span::edition::Edition;
2424use std::ops::Range;
2525use url::Url;
2626
27mod broken_link;
2728mod doc_comment_double_space_linebreaks;
2829mod doc_suspicious_footnotes;
2930mod include_in_doc_without_cfg;
......@@ -292,6 +293,34 @@ declare_clippy_lint! {
292293 "possible typo for an intra-doc link"
293294}
294295
296declare_clippy_lint! {
297 /// ### What it does
298 /// Checks the doc comments have unbroken links, mostly caused
299 /// by bad formatted links such as broken across multiple lines.
300 ///
301 /// ### Why is this bad?
302 /// Because documentation generated by rustdoc will be broken
303 /// since expected links won't be links and just text.
304 ///
305 /// ### Examples
306 /// This link is broken:
307 /// ```no_run
308 /// /// [example of a bad link](https://
309 /// /// github.com/rust-lang/rust-clippy/)
310 /// pub fn do_something() {}
311 /// ```
312 ///
313 /// It shouldn't be broken across multiple lines to work:
314 /// ```no_run
315 /// /// [example of a good link](https://github.com/rust-lang/rust-clippy/)
316 /// pub fn do_something() {}
317 /// ```
318 #[clippy::version = "1.84.0"]
319 pub DOC_BROKEN_LINK,
320 pedantic,
321 "broken document link"
322}
323
295324declare_clippy_lint! {
296325 /// ### What it does
297326 /// Checks for the doc comments of publicly visible
......@@ -656,6 +685,7 @@ impl Documentation {
656685impl_lint_pass!(Documentation => [
657686 DOC_LINK_CODE,
658687 DOC_LINK_WITH_QUOTES,
688 DOC_BROKEN_LINK,
659689 DOC_MARKDOWN,
660690 DOC_NESTED_REFDEFS,
661691 MISSING_SAFETY_DOC,
......@@ -786,9 +816,9 @@ struct DocHeaders {
786816/// back in the various late lint pass methods if they need the final doc headers, like "Safety" or
787817/// "Panics" sections.
788818fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[Attribute]) -> Option<DocHeaders> {
789 /// We don't want the parser to choke on intra doc links. Since we don't
790 /// actually care about rendering them, just pretend that all broken links
791 /// point to a fake address.
819 // We don't want the parser to choke on intra doc links. Since we don't
820 // actually care about rendering them, just pretend that all broken links
821 // point to a fake address.
792822 #[expect(clippy::unnecessary_wraps)] // we're following a type signature
793823 fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> {
794824 Some(("fake".into(), "fake".into()))
......@@ -828,14 +858,12 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
828858 return Some(DocHeaders::default());
829859 }
830860
831 let mut cb = fake_broken_link_callback;
832
833861 check_for_code_clusters(
834862 cx,
835863 pulldown_cmark::Parser::new_with_broken_link_callback(
836864 &doc,
837865 main_body_opts() - Options::ENABLE_SMART_PUNCTUATION,
838 Some(&mut cb),
866 Some(&mut fake_broken_link_callback),
839867 )
840868 .into_offset_iter(),
841869 &doc,
......@@ -845,9 +873,17 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
845873 },
846874 );
847875
876 // NOTE: check_doc uses it own cb function,
877 // to avoid causing duplicated diagnostics for the broken link checker.
878 let mut full_fake_broken_link_callback = |bl: BrokenLink<'_>| -> Option<(CowStr<'_>, CowStr<'_>)> {
879 broken_link::check(cx, &bl, &doc, &fragments);
880 Some(("fake".into(), "fake".into()))
881 };
882
848883 // disable smart punctuation to pick up ['link'] more easily
849884 let opts = main_body_opts() - Options::ENABLE_SMART_PUNCTUATION;
850 let parser = pulldown_cmark::Parser::new_with_broken_link_callback(&doc, opts, Some(&mut cb));
885 let parser =
886 pulldown_cmark::Parser::new_with_broken_link_callback(&doc, opts, Some(&mut full_fake_broken_link_callback));
851887
852888 Some(check_doc(
853889 cx,
src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs+24-3
......@@ -71,6 +71,7 @@ pub fn check(
7171 if !ignore {
7272 get_test_spans(&item, *ident, &mut test_attr_spans);
7373 }
74
7475 let is_async = matches!(sig.header.coroutine_kind, Some(CoroutineKind::Async { .. }));
7576 let returns_nothing = match &sig.decl.output {
7677 FnRetTy::Default(..) => true,
......@@ -89,9 +90,14 @@ pub fn check(
8990 // Another function was found; this case is ignored for needless_doctest_main
9091 ItemKind::Fn(fn_) => {
9192 eligible = false;
92 if !ignore {
93 get_test_spans(&item, fn_.ident, &mut test_attr_spans);
93 if ignore {
94 // If ignore is active invalidating one lint,
95 // and we already found another function thus
96 // invalidating the other one, we have no
97 // business continuing.
98 return (false, test_attr_spans);
9499 }
100 get_test_spans(&item, fn_.ident, &mut test_attr_spans);
95101 },
96102 // Tests with one of these items are ignored
97103 ItemKind::Static(..)
......@@ -104,7 +110,10 @@ pub fn check(
104110 },
105111 Ok(None) => break,
106112 Err(e) => {
107 e.cancel();
113 // See issue #15041. When calling `.cancel()` on the `Diag`, Clippy will unexpectedly panic
114 // when the `Diag` is unwinded. Meanwhile, we can just call `.emit()`, since the `DiagCtxt`
115 // is just a sink, nothing will be printed.
116 e.emit();
108117 return (false, test_attr_spans);
109118 },
110119 }
......@@ -119,6 +128,18 @@ pub fn check(
119128
120129 let trailing_whitespace = text.len() - text.trim_end().len();
121130
131 // We currently only test for "fn main". Checking for the real
132 // entrypoint (with tcx.entry_fn(())) in each block would be unnecessarily
133 // expensive, as those are probably intended and relevant. Same goes for
134 // macros and other weird ways of declaring a main function.
135 //
136 // Also, as we only check for attribute names and don't do macro expansion,
137 // we can check only for #[test]
138
139 if !((text.contains("main") && text.contains("fn")) || text.contains("#[test]")) {
140 return;
141 }
142
122143 // Because of the global session, we need to create a new session in a different thread with
123144 // the edition we need.
124145 let text = text.to_owned();
src/tools/clippy/clippy_lints/src/empty_line_after.rs+57-7
......@@ -10,7 +10,7 @@ use rustc_errors::{Applicability, Diag, SuggestionStyle};
1010use rustc_lexer::TokenKind;
1111use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
1212use rustc_session::impl_lint_pass;
13use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, kw};
13use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, kw, sym};
1414
1515declare_clippy_lint! {
1616 /// ### What it does
......@@ -129,10 +129,55 @@ struct Stop {
129129 kind: StopKind,
130130 first: usize,
131131 last: usize,
132 name: Option<Symbol>,
132133}
133134
134135impl Stop {
135 fn convert_to_inner(&self) -> (Span, String) {
136 fn is_outer_attr_only(&self) -> bool {
137 let Some(name) = self.name else {
138 return false;
139 };
140 // Check if the attribute only has effect when as an outer attribute
141 // The below attributes are collected from the builtin attributes of The Rust Reference
142 // https://doc.rust-lang.org/reference/attributes.html#r-attributes.builtin
143 // And the comments below are from compiler errors and warnings
144 matches!(
145 name,
146 // Cannot be used at crate level
147 sym::repr | sym::test | sym::derive | sym::automatically_derived | sym::path | sym::global_allocator |
148 // Only has an effect on macro definitions
149 sym::macro_export |
150 // Only be applied to trait definitions
151 sym::on_unimplemented |
152 // Only be placed on trait implementations
153 sym::do_not_recommend |
154 // Only has an effect on items
155 sym::ignore | sym::should_panic | sym::proc_macro | sym::proc_macro_derive | sym::proc_macro_attribute |
156 // Has no effect when applied to a module
157 sym::must_use |
158 // Should be applied to a foreign function or static
159 sym::link_name | sym::link_ordinal | sym::link_section |
160 // Should be applied to an `extern crate` item
161 sym::no_link |
162 // Should be applied to a free function, impl method or static
163 sym::export_name | sym::no_mangle |
164 // Should be applied to a `static` variable
165 sym::used |
166 // Should be applied to function or closure
167 sym::inline |
168 // Should be applied to a function definition
169 sym::cold | sym::target_feature | sym::track_caller | sym::instruction_set |
170 // Should be applied to a struct or enum
171 sym::non_exhaustive |
172 // Note: No any warning when it as an inner attribute, but it has no effect
173 sym::panic_handler
174 )
175 }
176
177 fn convert_to_inner(&self) -> Option<(Span, String)> {
178 if self.is_outer_attr_only() {
179 return None;
180 }
136181 let inner = match self.kind {
137182 // #![...]
138183 StopKind::Attr => InnerSpan::new(1, 1),
......@@ -140,7 +185,7 @@ impl Stop {
140185 // ^ ^
141186 StopKind::Doc(_) => InnerSpan::new(2, 3),
142187 };
143 (self.span.from_inner(inner), "!".into())
188 Some((self.span.from_inner(inner), "!".into()))
144189 }
145190
146191 fn comment_out(&self, cx: &EarlyContext<'_>, suggestions: &mut Vec<(Span, String)>) {
......@@ -177,6 +222,7 @@ impl Stop {
177222 },
178223 first: file.lookup_line(file.relative_position(lo))?,
179224 last: file.lookup_line(file.relative_position(hi))?,
225 name: attr.name(),
180226 })
181227 }
182228}
......@@ -356,6 +402,12 @@ impl EmptyLineAfter {
356402 if let Some(parent) = self.items.iter().rev().nth(1)
357403 && (parent.kind == "module" || parent.kind == "crate")
358404 && parent.mod_items == Some(id)
405 && let suggestions = gaps
406 .iter()
407 .flat_map(|gap| gap.prev_chunk)
408 .filter_map(Stop::convert_to_inner)
409 .collect::<Vec<_>>()
410 && !suggestions.is_empty()
359411 {
360412 let desc = if parent.kind == "module" {
361413 "parent module"
......@@ -367,10 +419,7 @@ impl EmptyLineAfter {
367419 StopKind::Attr => format!("if the attribute should apply to the {desc} use an inner attribute"),
368420 StopKind::Doc(_) => format!("if the comment should document the {desc} use an inner doc comment"),
369421 },
370 gaps.iter()
371 .flat_map(|gap| gap.prev_chunk)
372 .map(Stop::convert_to_inner)
373 .collect(),
422 suggestions,
374423 Applicability::MaybeIncorrect,
375424 );
376425 }
......@@ -425,6 +474,7 @@ impl EmptyLineAfter {
425474 first: line.line,
426475 // last doesn't need to be accurate here, we don't compare it with anything
427476 last: line.line,
477 name: None,
428478 });
429479 }
430480
src/tools/clippy/clippy_lints/src/eta_reduction.rs+53-39
......@@ -1,4 +1,4 @@
1use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
1use clippy_utils::diagnostics::span_lint_hir_and_then;
22use clippy_utils::higher::VecArgs;
33use clippy_utils::source::{snippet_opt, snippet_with_applicability};
44use clippy_utils::ty::get_type_diagnostic_name;
......@@ -109,14 +109,20 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
109109 {
110110 let vec_crate = if is_no_std_crate(cx) { "alloc" } else { "std" };
111111 // replace `|| vec![]` with `Vec::new`
112 span_lint_and_sugg(
112 span_lint_hir_and_then(
113113 cx,
114114 REDUNDANT_CLOSURE,
115 expr.hir_id,
115116 expr.span,
116117 "redundant closure",
117 "replace the closure with `Vec::new`",
118 format!("{vec_crate}::vec::Vec::new"),
119 Applicability::MachineApplicable,
118 |diag| {
119 diag.span_suggestion(
120 expr.span,
121 "replace the closure with `Vec::new`",
122 format!("{vec_crate}::vec::Vec::new"),
123 Applicability::MachineApplicable,
124 );
125 },
120126 );
121127 }
122128 // skip `foo(|| macro!())`
......@@ -198,41 +204,48 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
198204 // For now ignore all callee types which reference a type parameter.
199205 && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_)))
200206 {
201 span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
202 if let Some(mut snippet) = snippet_opt(cx, callee.span) {
203 if path_to_local(callee).is_some_and(|l| {
204 // FIXME: Do we really need this `local_used_in` check?
205 // Isn't it checking something like... `callee(callee)`?
206 // If somehow this check is needed, add some test for it,
207 // 'cuz currently nothing changes after deleting this check.
208 local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
209 }) {
210 match cx
211 .tcx
212 .infer_ctxt()
213 .build(cx.typing_mode())
214 .err_ctxt()
215 .type_implements_fn_trait(
216 cx.param_env,
217 Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
218 ty::PredicatePolarity::Positive,
219 ) {
220 // Mutable closure is used after current expr; we cannot consume it.
221 Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"),
222 Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => {
223 snippet = format!("&{snippet}");
224 },
225 _ => (),
207 span_lint_hir_and_then(
208 cx,
209 REDUNDANT_CLOSURE,
210 expr.hir_id,
211 expr.span,
212 "redundant closure",
213 |diag| {
214 if let Some(mut snippet) = snippet_opt(cx, callee.span) {
215 if path_to_local(callee).is_some_and(|l| {
216 // FIXME: Do we really need this `local_used_in` check?
217 // Isn't it checking something like... `callee(callee)`?
218 // If somehow this check is needed, add some test for it,
219 // 'cuz currently nothing changes after deleting this check.
220 local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
221 }) {
222 match cx
223 .tcx
224 .infer_ctxt()
225 .build(cx.typing_mode())
226 .err_ctxt()
227 .type_implements_fn_trait(
228 cx.param_env,
229 Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
230 ty::PredicatePolarity::Positive,
231 ) {
232 // Mutable closure is used after current expr; we cannot consume it.
233 Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"),
234 Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => {
235 snippet = format!("&{snippet}");
236 },
237 _ => (),
238 }
226239 }
240 diag.span_suggestion(
241 expr.span,
242 "replace the closure with the function itself",
243 snippet,
244 Applicability::MachineApplicable,
245 );
227246 }
228 diag.span_suggestion(
229 expr.span,
230 "replace the closure with the function itself",
231 snippet,
232 Applicability::MachineApplicable,
233 );
234 }
235 });
247 },
248 );
236249 }
237250 },
238251 ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => {
......@@ -245,9 +258,10 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
245258 Some(span) => format!("::{}", snippet_with_applicability(cx, span, "<..>", &mut app)),
246259 None => String::new(),
247260 };
248 span_lint_and_then(
261 span_lint_hir_and_then(
249262 cx,
250263 REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
264 expr.hir_id,
251265 expr.span,
252266 "redundant closure",
253267 |diag| {
src/tools/clippy/clippy_lints/src/exhaustive_items.rs+1-1
......@@ -76,7 +76,7 @@ impl LateLintPass<'_> for ExhaustiveItems {
7676 "exported enums should not be exhaustive",
7777 [].as_slice(),
7878 ),
79 ItemKind::Struct(_, _, v) => (
79 ItemKind::Struct(_, _, v) if v.fields().iter().all(|f| f.default.is_none()) => (
8080 EXHAUSTIVE_STRUCTS,
8181 "exported structs should not be exhaustive",
8282 v.fields(),
src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs+1-2
......@@ -5,14 +5,13 @@ use clippy_utils::{
55 eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate,
66 numeric_literal, peel_blocks, sugg, sym,
77};
8use rustc_ast::ast;
89use rustc_errors::Applicability;
910use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
1011use rustc_lint::{LateContext, LateLintPass};
1112use rustc_middle::ty;
1213use rustc_session::declare_lint_pass;
1314use rustc_span::source_map::Spanned;
14
15use rustc_ast::ast;
1615use std::f32::consts as f32_consts;
1716use std::f64::consts as f64_consts;
1817use sugg::Sugg;
src/tools/clippy/clippy_lints/src/functions/must_use.rs+40-13
......@@ -14,9 +14,9 @@ use clippy_utils::source::SpanRangeExt;
1414use clippy_utils::ty::is_must_use_ty;
1515use clippy_utils::visitors::for_each_expr_without_closures;
1616use clippy_utils::{return_ty, trait_ref_of_method};
17use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
18use rustc_span::Symbol;
1917use rustc_attr_data_structures::{AttributeKind, find_attr};
18use rustc_span::Symbol;
19use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
2020
2121use core::ops::ControlFlow;
2222
......@@ -34,7 +34,17 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>
3434 let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
3535 let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
3636 if let Some((attr_span, reason)) = attr {
37 check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, *attr_span, *reason, attrs, sig);
37 check_needless_must_use(
38 cx,
39 sig.decl,
40 item.owner_id,
41 item.span,
42 fn_header_span,
43 *attr_span,
44 *reason,
45 attrs,
46 sig,
47 );
3848 } else if is_public && !is_proc_macro(attrs) && !find_attr!(attrs, AttributeKind::NoMangle(..)) {
3949 check_must_use_candidate(
4050 cx,
......@@ -54,9 +64,20 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp
5464 let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
5565 let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
5666 let attrs = cx.tcx.hir_attrs(item.hir_id());
57 let attr = find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason));
67 let attr =
68 find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason));
5869 if let Some((attr_span, reason)) = attr {
59 check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, *attr_span, *reason, attrs, sig);
70 check_needless_must_use(
71 cx,
72 sig.decl,
73 item.owner_id,
74 item.span,
75 fn_header_span,
76 *attr_span,
77 *reason,
78 attrs,
79 sig,
80 );
6081 } else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id).is_none() {
6182 check_must_use_candidate(
6283 cx,
......@@ -77,9 +98,20 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
7798 let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
7899
79100 let attrs = cx.tcx.hir_attrs(item.hir_id());
80 let attr = find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason));
101 let attr =
102 find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason));
81103 if let Some((attr_span, reason)) = attr {
82 check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, *attr_span, *reason, attrs, sig);
104 check_needless_must_use(
105 cx,
106 sig.decl,
107 item.owner_id,
108 item.span,
109 fn_header_span,
110 *attr_span,
111 *reason,
112 attrs,
113 sig,
114 );
83115 } else if let hir::TraitFn::Provided(eid) = *eid {
84116 let body = cx.tcx.hir_body(eid);
85117 if attr.is_none() && is_public && !is_proc_macro(attrs) {
......@@ -121,12 +153,7 @@ fn check_needless_must_use(
121153 fn_header_span,
122154 "this unit-returning function has a `#[must_use]` attribute",
123155 |diag| {
124 diag.span_suggestion(
125 attr_span,
126 "remove the attribute",
127 "",
128 Applicability::MachineApplicable,
129 );
156 diag.span_suggestion(attr_span, "remove the attribute", "", Applicability::MachineApplicable);
130157 },
131158 );
132159 } else {
src/tools/clippy/clippy_lints/src/if_not_else.rs+2-9
......@@ -1,4 +1,4 @@
1use clippy_utils::consts::{ConstEvalCtxt, Constant};
1use clippy_utils::consts::is_zero_integer_const;
22use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
33use clippy_utils::is_else_clause;
44use clippy_utils::source::{HasSession, indent_of, reindent_multiline, snippet};
......@@ -48,13 +48,6 @@ declare_clippy_lint! {
4848
4949declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]);
5050
51fn is_zero_const(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool {
52 if let Some(value) = ConstEvalCtxt::new(cx).eval_simple(expr) {
53 return Constant::Int(0) == value;
54 }
55 false
56}
57
5851impl LateLintPass<'_> for IfNotElse {
5952 fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
6053 if let ExprKind::If(cond, cond_inner, Some(els)) = e.kind
......@@ -68,7 +61,7 @@ impl LateLintPass<'_> for IfNotElse {
6861 ),
6962 // Don't lint on `… != 0`, as these are likely to be bit tests.
7063 // For example, `if foo & 0x0F00 != 0 { … } else { … }` is already in the "proper" order.
71 ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_const(rhs, cx) => (
64 ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_integer_const(cx, rhs) => (
7265 "unnecessary `!=` operation",
7366 "change to `==` and swap the blocks of the `if`/`else`",
7467 ),
src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs+5-5
......@@ -1,6 +1,6 @@
11use clippy_utils::diagnostics::span_lint_and_then;
22use clippy_utils::sugg::DiagExt;
3use rustc_attr_data_structures::{find_attr, AttributeKind};
3use rustc_attr_data_structures::{AttributeKind, find_attr};
44use rustc_errors::Applicability;
55use rustc_hir::{TraitFn, TraitItem, TraitItemKind};
66use rustc_lint::{LateContext, LateLintPass};
......@@ -33,10 +33,10 @@ impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody {
3333 fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
3434 if let TraitItemKind::Fn(_, TraitFn::Required(_)) = item.kind
3535 && let Some(attr_span) = find_attr!(cx
36 .tcx
37 .hir_attrs(item.hir_id()),
38 AttributeKind::Inline(_, span) => *span
39 )
36 .tcx
37 .hir_attrs(item.hir_id()),
38 AttributeKind::Inline(_, span) => *span
39 )
4040 {
4141 span_lint_and_then(
4242 cx,
src/tools/clippy/clippy_lints/src/lib.rs+4-122
......@@ -59,10 +59,10 @@ extern crate smallvec;
5959extern crate thin_vec;
6060
6161#[macro_use]
62mod declare_clippy_lint;
62extern crate clippy_utils;
6363
6464#[macro_use]
65extern crate clippy_utils;
65extern crate declare_clippy_lint;
6666
6767mod utils;
6868
......@@ -411,108 +411,9 @@ mod zombie_processes;
411411use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation};
412412use clippy_utils::macros::FormatArgsStorage;
413413use rustc_data_structures::fx::FxHashSet;
414use rustc_lint::{Lint, LintId};
414use rustc_lint::Lint;
415415use utils::attr_collector::{AttrCollector, AttrStorage};
416416
417#[derive(Default)]
418struct RegistrationGroups {
419 all: Vec<LintId>,
420 cargo: Vec<LintId>,
421 complexity: Vec<LintId>,
422 correctness: Vec<LintId>,
423 nursery: Vec<LintId>,
424 pedantic: Vec<LintId>,
425 perf: Vec<LintId>,
426 restriction: Vec<LintId>,
427 style: Vec<LintId>,
428 suspicious: Vec<LintId>,
429}
430
431impl RegistrationGroups {
432 #[rustfmt::skip]
433 fn register(self, store: &mut rustc_lint::LintStore) {
434 store.register_group(true, "clippy::all", Some("clippy_all"), self.all);
435 store.register_group(true, "clippy::cargo", Some("clippy_cargo"), self.cargo);
436 store.register_group(true, "clippy::complexity", Some("clippy_complexity"), self.complexity);
437 store.register_group(true, "clippy::correctness", Some("clippy_correctness"), self.correctness);
438 store.register_group(true, "clippy::nursery", Some("clippy_nursery"), self.nursery);
439 store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), self.pedantic);
440 store.register_group(true, "clippy::perf", Some("clippy_perf"), self.perf);
441 store.register_group(true, "clippy::restriction", Some("clippy_restriction"), self.restriction);
442 store.register_group(true, "clippy::style", Some("clippy_style"), self.style);
443 store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), self.suspicious);
444 }
445}
446
447#[derive(Copy, Clone, Debug)]
448pub(crate) enum LintCategory {
449 Cargo,
450 Complexity,
451 Correctness,
452 Nursery,
453 Pedantic,
454 Perf,
455 Restriction,
456 Style,
457 Suspicious,
458}
459
460#[allow(clippy::enum_glob_use)]
461use LintCategory::*;
462
463impl LintCategory {
464 fn is_all(self) -> bool {
465 matches!(self, Correctness | Suspicious | Style | Complexity | Perf)
466 }
467
468 fn group(self, groups: &mut RegistrationGroups) -> &mut Vec<LintId> {
469 match self {
470 Cargo => &mut groups.cargo,
471 Complexity => &mut groups.complexity,
472 Correctness => &mut groups.correctness,
473 Nursery => &mut groups.nursery,
474 Pedantic => &mut groups.pedantic,
475 Perf => &mut groups.perf,
476 Restriction => &mut groups.restriction,
477 Style => &mut groups.style,
478 Suspicious => &mut groups.suspicious,
479 }
480 }
481}
482
483pub struct LintInfo {
484 /// Double reference to maintain pointer equality
485 pub lint: &'static &'static Lint,
486 category: LintCategory,
487 pub explanation: &'static str,
488 /// e.g. `clippy_lints/src/absolute_paths.rs#43`
489 pub location: &'static str,
490 pub version: Option<&'static str>,
491}
492
493impl LintInfo {
494 /// Returns the lint name in lowercase without the `clippy::` prefix
495 #[allow(clippy::missing_panics_doc)]
496 pub fn name_lower(&self) -> String {
497 self.lint.name.strip_prefix("clippy::").unwrap().to_ascii_lowercase()
498 }
499
500 /// Returns the name of the lint's category in lowercase (`style`, `pedantic`)
501 pub fn category_str(&self) -> &'static str {
502 match self.category {
503 Cargo => "cargo",
504 Complexity => "complexity",
505 Correctness => "correctness",
506 Nursery => "nursery",
507 Pedantic => "pedantic",
508 Perf => "perf",
509 Restriction => "restriction",
510 Style => "style",
511 Suspicious => "suspicious",
512 }
513 }
514}
515
516417pub fn explain(name: &str) -> i32 {
517418 let target = format!("clippy::{}", name.to_ascii_uppercase());
518419
......@@ -535,30 +436,11 @@ pub fn explain(name: &str) -> i32 {
535436 }
536437}
537438
538fn register_categories(store: &mut rustc_lint::LintStore) {
539 let mut groups = RegistrationGroups::default();
540
541 for LintInfo { lint, category, .. } in declared_lints::LINTS {
542 if category.is_all() {
543 groups.all.push(LintId::of(lint));
544 }
545
546 category.group(&mut groups).push(LintId::of(lint));
547 }
548
549 let lints: Vec<&'static Lint> = declared_lints::LINTS.iter().map(|info| *info.lint).collect();
550
551 store.register_lints(&lints);
552 groups.register(store);
553}
554
555439/// Register all lints and lint groups with the rustc lint store
556440///
557441/// Used in `./src/driver.rs`.
558442#[expect(clippy::too_many_lines)]
559pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
560 register_categories(store);
561
443pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
562444 for (old_name, new_name) in deprecated_lints::RENAMED {
563445 store.register_renamed(old_name, new_name);
564446 }
src/tools/clippy/clippy_lints/src/loops/same_item_push.rs+7-8
......@@ -163,15 +163,14 @@ impl<'tcx> Visitor<'tcx> for SameItemPushVisitor<'_, 'tcx> {
163163 StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(expr),
164164 _ => {},
165165 }
166 }
167 // Current statement is a push ...check whether another
168 // push had been previously done
169 else if self.vec_push.is_none() {
170 self.vec_push = vec_push_option;
166171 } else {
167 // Current statement is a push ...check whether another
168 // push had been previously done
169 if self.vec_push.is_none() {
170 self.vec_push = vec_push_option;
171 } else {
172 // There are multiple pushes ... don't lint
173 self.multiple_pushes = true;
174 }
172 // There are multiple pushes ... don't lint
173 self.multiple_pushes = true;
175174 }
176175 }
177176}
src/tools/clippy/clippy_lints/src/manual_let_else.rs-1
......@@ -13,7 +13,6 @@ use rustc_errors::Applicability;
1313use rustc_hir::def::{CtorOf, DefKind, Res};
1414use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind};
1515use rustc_lint::{LateContext, LintContext};
16
1716use rustc_span::Span;
1817use rustc_span::symbol::{Symbol, sym};
1918use std::slice;
src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs+17-3
......@@ -1,9 +1,9 @@
11use clippy_utils::diagnostics::span_lint_and_sugg;
22use clippy_utils::source::{indent_of, reindent_multiline};
33use clippy_utils::sugg::Sugg;
4use clippy_utils::ty::option_arg_ty;
4use clippy_utils::ty::{option_arg_ty, peel_mid_ty_refs_is_mutable};
55use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res, peel_blocks, span_contains_comment};
6use rustc_ast::BindingMode;
6use rustc_ast::{BindingMode, Mutability};
77use rustc_errors::Applicability;
88use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr};
99use rustc_hir::def::{DefKind, Res};
......@@ -133,7 +133,21 @@ fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok
133133 Applicability::MachineApplicable
134134 };
135135 let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_paren();
136 let sugg = format!("{scrut}.{method}()");
136
137 let scrutinee_ty = cx.typeck_results().expr_ty(scrutinee);
138 let (_, n_ref, mutability) = peel_mid_ty_refs_is_mutable(scrutinee_ty);
139 let prefix = if n_ref > 0 {
140 if mutability == Mutability::Mut {
141 ".as_mut()"
142 } else {
143 ".as_ref()"
144 }
145 } else {
146 ""
147 };
148
149 let sugg = format!("{scrut}{prefix}.{method}()");
150
137151 // If the expression being expanded is the `if …` part of an `else if …`, it must be blockified.
138152 let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr)
139153 && let ExprKind::If(_, _, Some(else_part)) = parent_expr.kind
src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs+8-6
......@@ -1,4 +1,5 @@
11use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
2use clippy_utils::source::SpanRangeExt;
23use clippy_utils::ty::is_type_diagnostic_item;
34use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns};
45use rustc_errors::Applicability;
......@@ -116,11 +117,12 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
116117 let format_suggestion = |variant: &VariantDef| {
117118 format!(
118119 "{}{}{}{}",
119 if let Some(ident) = wildcard_ident {
120 format!("{} @ ", ident.name)
121 } else {
122 String::new()
123 },
120 wildcard_ident.map_or(String::new(), |ident| {
121 ident
122 .span
123 .get_source_text(cx)
124 .map_or_else(|| format!("{} @ ", ident.name), |s| format!("{s} @ "))
125 }),
124126 if let CommonPrefixSearcher::Path(path_prefix) = path_prefix {
125127 let mut s = String::new();
126128 for seg in path_prefix {
......@@ -138,7 +140,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
138140 Some(CtorKind::Fn) if variant.fields.len() == 1 => "(_)",
139141 Some(CtorKind::Fn) => "(..)",
140142 Some(CtorKind::Const) => "",
141 None => "{ .. }",
143 None => " { .. }",
142144 }
143145 )
144146 };
src/tools/clippy/clippy_lints/src/methods/mod.rs+1-1
......@@ -4426,7 +4426,7 @@ declare_clippy_lint! {
44264426 /// ```no_run
44274427 /// use std::io::{BufReader, Read};
44284428 /// use std::fs::File;
4429 /// let file = BufReader::new(std::fs::File::open("./bytes.txt").unwrap());
4429 /// let file = BufReader::new(File::open("./bytes.txt").unwrap());
44304430 /// file.bytes();
44314431 /// ```
44324432 #[clippy::version = "1.87.0"]
src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs+6-5
......@@ -136,7 +136,7 @@ pub(super) fn check<'tcx>(
136136 fun_span: Option<Span>,
137137 ) -> bool {
138138 // (path, fn_has_argument, methods, suffix)
139 const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 4] = [
139 const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 5] = [
140140 (sym::BTreeEntry, false, &[sym::or_insert], "with"),
141141 (sym::HashMapEntry, false, &[sym::or_insert], "with"),
142142 (
......@@ -145,16 +145,17 @@ pub(super) fn check<'tcx>(
145145 &[sym::map_or, sym::ok_or, sym::or, sym::unwrap_or],
146146 "else",
147147 ),
148 (sym::Result, true, &[sym::or, sym::unwrap_or], "else"),
148 (sym::Option, false, &[sym::get_or_insert], "with"),
149 (sym::Result, true, &[sym::map_or, sym::or, sym::unwrap_or], "else"),
149150 ];
150151
151152 if KNOW_TYPES.iter().any(|k| k.2.contains(&name))
152153 && switch_to_lazy_eval(cx, arg)
153154 && !contains_return(arg)
154155 && let self_ty = cx.typeck_results().expr_ty(self_expr)
155 && let Some(&(_, fn_has_arguments, poss, suffix)) =
156 KNOW_TYPES.iter().find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0))
157 && poss.contains(&name)
156 && let Some(&(_, fn_has_arguments, _, suffix)) = KNOW_TYPES
157 .iter()
158 .find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0) && i.2.contains(&name))
158159 {
159160 let ctxt = span.ctxt();
160161 let mut app = Applicability::HasPlaceholders;
src/tools/clippy/clippy_lints/src/missing_inline.rs+1-1
......@@ -1,5 +1,5 @@
11use clippy_utils::diagnostics::span_lint;
2use rustc_attr_data_structures::{find_attr, AttributeKind};
2use rustc_attr_data_structures::{AttributeKind, find_attr};
33use rustc_hir as hir;
44use rustc_hir::Attribute;
55use rustc_lint::{LateContext, LateLintPass, LintContext};
src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs+6-6
......@@ -161,7 +161,7 @@ fn path_has_args(p: &QPath<'_>) -> bool {
161161/// - `Copy` itself, or
162162/// - the only use of a mutable reference, or
163163/// - not a variable (created by a function call)
164#[expect(clippy::too_many_arguments)]
164#[expect(clippy::too_many_arguments, clippy::too_many_lines)]
165165fn needless_borrow_count<'tcx>(
166166 cx: &LateContext<'tcx>,
167167 possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
......@@ -232,11 +232,11 @@ fn needless_borrow_count<'tcx>(
232232 let mut args_with_referent_ty = callee_args.to_vec();
233233
234234 let mut check_reference_and_referent = |reference: &Expr<'tcx>, referent: &Expr<'tcx>| {
235 if let ExprKind::Field(base, _) = &referent.kind {
236 let base_ty = cx.typeck_results().expr_ty(base);
237 if drop_trait_def_id.is_some_and(|id| implements_trait(cx, base_ty, id, &[])) {
238 return false;
239 }
235 if let ExprKind::Field(base, _) = &referent.kind
236 && let base_ty = cx.typeck_results().expr_ty(base)
237 && drop_trait_def_id.is_some_and(|id| implements_trait(cx, base_ty, id, &[]))
238 {
239 return false;
240240 }
241241
242242 let referent_ty = cx.typeck_results().expr_ty(referent);
src/tools/clippy/clippy_lints/src/needless_parens_on_range_literals.rs-1
......@@ -5,7 +5,6 @@ use clippy_utils::source::{snippet, snippet_with_applicability};
55use rustc_ast::ast;
66use rustc_errors::Applicability;
77use rustc_hir::{Expr, ExprKind};
8
98use rustc_lint::{LateContext, LateLintPass};
109use rustc_session::declare_lint_pass;
1110
src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs+4-2
......@@ -124,8 +124,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
124124 // Note that we do not want to deal with qualified predicates here.
125125 match pred.kind().no_bound_vars() {
126126 Some(ty::ClauseKind::Trait(pred))
127 if pred.def_id() != sized_trait && pred.def_id() != meta_sized_trait
128 => Some(pred),
127 if pred.def_id() != sized_trait && pred.def_id() != meta_sized_trait =>
128 {
129 Some(pred)
130 },
129131 _ => None,
130132 }
131133 })
src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs+2-3
......@@ -1,13 +1,12 @@
11use clippy_utils::diagnostics::span_lint_and_then;
22use clippy_utils::source::{snippet, snippet_with_applicability};
33use rustc_abi::ExternAbi;
4use rustc_attr_data_structures::AttributeKind;
45use rustc_errors::Applicability;
5use rustc_hir::{Item, ItemKind};
6use rustc_hir::{Attribute, Item, ItemKind};
67use rustc_lint::{LateContext, LateLintPass};
78use rustc_session::declare_lint_pass;
89use rustc_span::{BytePos, Pos};
9use rustc_attr_data_structures::AttributeKind;
10use rustc_hir::Attribute;
1110
1211declare_clippy_lint! {
1312 /// ### What it does
src/tools/clippy/clippy_lints/src/non_copy_const.rs+1-1
......@@ -617,7 +617,7 @@ impl<'tcx> NonCopyConst<'tcx> {
617617
618618 // Then a type check. Note we only check the type here as the result
619619 // gets cached.
620 let ty = EarlyBinder::bind(typeck.expr_ty(src_expr)).instantiate(tcx, init_args);
620 let ty = typeck.expr_ty(src_expr);
621621 // Normalized as we need to check if this is an array later.
622622 let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
623623 if self.is_ty_freeze(tcx, typing_env, ty).is_freeze() {
src/tools/clippy/clippy_lints/src/operators/identity_op.rs+67-9
......@@ -1,12 +1,13 @@
1use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt};
1use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt, integer_const, is_zero_integer_const};
22use clippy_utils::diagnostics::span_lint_and_sugg;
33use clippy_utils::source::snippet_with_applicability;
4use clippy_utils::{clip, peel_hir_expr_refs, unsext};
4use clippy_utils::{ExprUseNode, clip, expr_use_ctxt, peel_hir_expr_refs, unsext};
55use rustc_errors::Applicability;
6use rustc_hir::{BinOpKind, Expr, ExprKind, Node};
6use rustc_hir::def::{DefKind, Res};
7use rustc_hir::{BinOpKind, Expr, ExprKind, Node, Path, QPath};
78use rustc_lint::LateContext;
89use rustc_middle::ty;
9use rustc_span::Span;
10use rustc_span::{Span, kw};
1011
1112use super::IDENTITY_OP;
1213
......@@ -17,7 +18,7 @@ pub(crate) fn check<'tcx>(
1718 left: &'tcx Expr<'_>,
1819 right: &'tcx Expr<'_>,
1920) {
20 if !is_allowed(cx, op, left, right) {
21 if !is_allowed(cx, expr, op, left, right) {
2122 return;
2223 }
2324
......@@ -165,14 +166,27 @@ fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, child: &Expr<'_>)
165166 Parens::Needed
166167}
167168
168fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> bool {
169fn is_allowed<'tcx>(
170 cx: &LateContext<'tcx>,
171 expr: &'tcx Expr<'tcx>,
172 cmp: BinOpKind,
173 left: &Expr<'tcx>,
174 right: &Expr<'tcx>,
175) -> bool {
176 // Exclude case where the left or right side is associated function call returns a type which is
177 // `Self` that is not given explicitly, and the expression is not a let binding's init
178 // expression and the let binding has a type annotation, or a function's return value.
179 if (is_assoc_fn_without_type_instance(cx, left) || is_assoc_fn_without_type_instance(cx, right))
180 && !is_expr_used_with_type_annotation(cx, expr)
181 {
182 return false;
183 }
184
169185 // This lint applies to integers and their references
170186 cx.typeck_results().expr_ty(left).peel_refs().is_integral()
171187 && cx.typeck_results().expr_ty(right).peel_refs().is_integral()
172188 // `1 << 0` is a common pattern in bit manipulation code
173 && !(cmp == BinOpKind::Shl
174 && ConstEvalCtxt::new(cx).eval_simple(right) == Some(Constant::Int(0))
175 && ConstEvalCtxt::new(cx).eval_simple(left) == Some(Constant::Int(1)))
189 && !(cmp == BinOpKind::Shl && is_zero_integer_const(cx, right) && integer_const(cx, left) == Some(1))
176190}
177191
178192fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) {
......@@ -234,3 +248,47 @@ fn span_ineffective_operation(
234248 applicability,
235249 );
236250}
251
252fn is_expr_used_with_type_annotation<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
253 match expr_use_ctxt(cx, expr).use_node(cx) {
254 ExprUseNode::LetStmt(letstmt) => letstmt.ty.is_some(),
255 ExprUseNode::Return(_) => true,
256 _ => false,
257 }
258}
259
260/// Check if the expression is an associated function without a type instance.
261/// Example:
262/// ```
263/// trait Def {
264/// fn def() -> Self;
265/// }
266/// impl Def for usize {
267/// fn def() -> Self {
268/// 0
269/// }
270/// }
271/// fn test() {
272/// let _ = 0usize + &Default::default();
273/// let _ = 0usize + &Def::def();
274/// }
275/// ```
276fn is_assoc_fn_without_type_instance<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
277 if let ExprKind::Call(func, _) = peel_hir_expr_refs(expr).0.kind
278 && let ExprKind::Path(QPath::Resolved(
279 // If it's not None, don't need to go further.
280 None,
281 Path {
282 res: Res::Def(DefKind::AssocFn, def_id),
283 ..
284 },
285 )) = func.kind
286 && let output_ty = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder().output()
287 && let ty::Param(ty::ParamTy {
288 name: kw::SelfUpper, ..
289 }) = output_ty.kind()
290 {
291 return true;
292 }
293 false
294}
src/tools/clippy/clippy_lints/src/operators/manual_is_multiple_of.rs created+66
......@@ -0,0 +1,66 @@
1use clippy_utils::consts::is_zero_integer_const;
2use clippy_utils::diagnostics::span_lint_and_sugg;
3use clippy_utils::msrvs::{self, Msrv};
4use clippy_utils::sugg::Sugg;
5use rustc_ast::BinOpKind;
6use rustc_errors::Applicability;
7use rustc_hir::{Expr, ExprKind};
8use rustc_lint::LateContext;
9use rustc_middle::ty;
10
11use super::MANUAL_IS_MULTIPLE_OF;
12
13pub(super) fn check<'tcx>(
14 cx: &LateContext<'tcx>,
15 expr: &Expr<'_>,
16 op: BinOpKind,
17 lhs: &'tcx Expr<'tcx>,
18 rhs: &'tcx Expr<'tcx>,
19 msrv: Msrv,
20) {
21 if msrv.meets(cx, msrvs::UNSIGNED_IS_MULTIPLE_OF)
22 && let Some(operand) = uint_compare_to_zero(cx, op, lhs, rhs)
23 && let ExprKind::Binary(operand_op, operand_left, operand_right) = operand.kind
24 && operand_op.node == BinOpKind::Rem
25 {
26 let mut app = Applicability::MachineApplicable;
27 let divisor = Sugg::hir_with_applicability(cx, operand_right, "_", &mut app);
28 span_lint_and_sugg(
29 cx,
30 MANUAL_IS_MULTIPLE_OF,
31 expr.span,
32 "manual implementation of `.is_multiple_of()`",
33 "replace with",
34 format!(
35 "{}{}.is_multiple_of({divisor})",
36 if op == BinOpKind::Eq { "" } else { "!" },
37 Sugg::hir_with_applicability(cx, operand_left, "_", &mut app).maybe_paren()
38 ),
39 app,
40 );
41 }
42}
43
44// If we have a `x == 0`, `x != 0` or `x > 0` (or the reverted ones), return the non-zero operand
45fn uint_compare_to_zero<'tcx>(
46 cx: &LateContext<'tcx>,
47 op: BinOpKind,
48 lhs: &'tcx Expr<'tcx>,
49 rhs: &'tcx Expr<'tcx>,
50) -> Option<&'tcx Expr<'tcx>> {
51 let operand = if matches!(lhs.kind, ExprKind::Binary(..))
52 && matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Gt)
53 && is_zero_integer_const(cx, rhs)
54 {
55 lhs
56 } else if matches!(rhs.kind, ExprKind::Binary(..))
57 && matches!(op, BinOpKind::Eq | BinOpKind::Ne | BinOpKind::Lt)
58 && is_zero_integer_const(cx, lhs)
59 {
60 rhs
61 } else {
62 return None;
63 };
64
65 matches!(cx.typeck_results().expr_ty_adjusted(operand).kind(), ty::Uint(_)).then_some(operand)
66}
src/tools/clippy/clippy_lints/src/operators/mod.rs+33
......@@ -11,6 +11,7 @@ mod float_cmp;
1111mod float_equality_without_abs;
1212mod identity_op;
1313mod integer_division;
14mod manual_is_multiple_of;
1415mod manual_midpoint;
1516mod misrefactored_assign_op;
1617mod modulo_arithmetic;
......@@ -830,12 +831,42 @@ declare_clippy_lint! {
830831 "manual implementation of `midpoint` which can overflow"
831832}
832833
834declare_clippy_lint! {
835 /// ### What it does
836 /// Checks for manual implementation of `.is_multiple_of()` on
837 /// unsigned integer types.
838 ///
839 /// ### Why is this bad?
840 /// `a.is_multiple_of(b)` is a clearer way to check for divisibility
841 /// of `a` by `b`. This expression can never panic.
842 ///
843 /// ### Example
844 /// ```no_run
845 /// # let (a, b) = (3u64, 4u64);
846 /// if a % b == 0 {
847 /// println!("{a} is divisible by {b}");
848 /// }
849 /// ```
850 /// Use instead:
851 /// ```no_run
852 /// # let (a, b) = (3u64, 4u64);
853 /// if a.is_multiple_of(b) {
854 /// println!("{a} is divisible by {b}");
855 /// }
856 /// ```
857 #[clippy::version = "1.89.0"]
858 pub MANUAL_IS_MULTIPLE_OF,
859 complexity,
860 "manual implementation of `.is_multiple_of()`"
861}
862
833863pub struct Operators {
834864 arithmetic_context: numeric_arithmetic::Context,
835865 verbose_bit_mask_threshold: u64,
836866 modulo_arithmetic_allow_comparison_to_zero: bool,
837867 msrv: Msrv,
838868}
869
839870impl Operators {
840871 pub fn new(conf: &'static Conf) -> Self {
841872 Self {
......@@ -874,6 +905,7 @@ impl_lint_pass!(Operators => [
874905 NEEDLESS_BITWISE_BOOL,
875906 SELF_ASSIGNMENT,
876907 MANUAL_MIDPOINT,
908 MANUAL_IS_MULTIPLE_OF,
877909]);
878910
879911impl<'tcx> LateLintPass<'tcx> for Operators {
......@@ -891,6 +923,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
891923 identity_op::check(cx, e, op.node, lhs, rhs);
892924 needless_bitwise_bool::check(cx, e, op.node, lhs, rhs);
893925 manual_midpoint::check(cx, e, op.node, lhs, rhs, self.msrv);
926 manual_is_multiple_of::check(cx, e, op.node, lhs, rhs, self.msrv);
894927 }
895928 self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
896929 bit_mask::check(cx, e, op.node, lhs, rhs);
src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs+2-2
......@@ -3,10 +3,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
33use clippy_utils::source::snippet;
44use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy};
55use clippy_utils::{is_self, is_self_ty};
6use rustc_attr_data_structures::{find_attr, AttributeKind, InlineAttr};
7use rustc_data_structures::fx::FxHashSet;
86use core::ops::ControlFlow;
97use rustc_abi::ExternAbi;
8use rustc_attr_data_structures::{AttributeKind, InlineAttr, find_attr};
9use rustc_data_structures::fx::FxHashSet;
1010use rustc_errors::Applicability;
1111use rustc_hir as hir;
1212use rustc_hir::intravisit::FnKind;
src/tools/clippy/clippy_lints/src/question_mark.rs+1
......@@ -142,6 +142,7 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
142142 && let Some(ret) = find_let_else_ret_expression(els)
143143 && let Some(inner_pat) = pat_and_expr_can_be_question_mark(cx, pat, ret)
144144 && !span_contains_comment(cx.tcx.sess.source_map(), els.span)
145 && !span_contains_cfg(cx, els.span)
145146 {
146147 let mut applicability = Applicability::MaybeIncorrect;
147148 let init_expr_str = Sugg::hir_with_applicability(cx, init_expr, "..", &mut applicability).maybe_paren();
src/tools/clippy/clippy_lints/src/question_mark_used.rs-1
......@@ -1,5 +1,4 @@
11use clippy_utils::diagnostics::span_lint_and_then;
2
32use clippy_utils::macros::span_is_local;
43use rustc_hir::{Expr, ExprKind, MatchSource};
54use rustc_lint::{LateContext, LateLintPass};
src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs+1-2
......@@ -3,11 +3,10 @@ use clippy_utils::higher::{VecInitKind, get_vec_init_kind};
33use clippy_utils::source::snippet;
44use clippy_utils::{get_enclosing_block, sym};
55
6use hir::{Expr, ExprKind, HirId, LetStmt, PatKind, PathSegment, QPath, StmtKind};
76use rustc_errors::Applicability;
8use rustc_hir as hir;
97use rustc_hir::def::Res;
108use rustc_hir::intravisit::{Visitor, walk_expr};
9use rustc_hir::{self as hir, Expr, ExprKind, HirId, LetStmt, PatKind, PathSegment, QPath, StmtKind};
1110use rustc_lint::{LateContext, LateLintPass};
1211use rustc_session::declare_lint_pass;
1312
src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs+2-3
......@@ -1,14 +1,13 @@
11use clippy_utils::diagnostics::span_lint_and_help;
22use clippy_utils::ty::is_must_use_ty;
33use clippy_utils::{nth_arg, return_ty};
4use rustc_attr_data_structures::{AttributeKind, find_attr};
45use rustc_hir::def_id::LocalDefId;
56use rustc_hir::intravisit::FnKind;
67use rustc_hir::{Body, FnDecl, OwnerId, TraitItem, TraitItemKind};
78use rustc_lint::{LateContext, LateLintPass, LintContext};
89use rustc_session::declare_lint_pass;
9use rustc_span::{Span};
10use rustc_attr_data_structures::AttributeKind;
11use rustc_attr_data_structures::find_attr;
10use rustc_span::Span;
1211
1312declare_clippy_lint! {
1413 /// ### What it does
src/tools/clippy/clippy_lints/src/single_component_path_imports.rs+14-15
......@@ -219,22 +219,21 @@ impl SingleComponentPathImports {
219219 }
220220 }
221221 }
222 } else {
223 // keep track of `use self::some_module` usages
224 if segments[0].ident.name == kw::SelfLower {
225 // simple case such as `use self::module::SomeStruct`
226 if segments.len() > 1 {
227 imports_reused_with_self.push(segments[1].ident.name);
228 return;
229 }
222 }
223 // keep track of `use self::some_module` usages
224 else if segments[0].ident.name == kw::SelfLower {
225 // simple case such as `use self::module::SomeStruct`
226 if segments.len() > 1 {
227 imports_reused_with_self.push(segments[1].ident.name);
228 return;
229 }
230230
231 // nested case such as `use self::{module1::Struct1, module2::Struct2}`
232 if let UseTreeKind::Nested { items, .. } = &use_tree.kind {
233 for tree in items {
234 let segments = &tree.0.prefix.segments;
235 if !segments.is_empty() {
236 imports_reused_with_self.push(segments[0].ident.name);
237 }
231 // nested case such as `use self::{module1::Struct1, module2::Struct2}`
232 if let UseTreeKind::Nested { items, .. } = &use_tree.kind {
233 for tree in items {
234 let segments = &tree.0.prefix.segments;
235 if !segments.is_empty() {
236 imports_reused_with_self.push(segments[0].ident.name);
238237 }
239238 }
240239 }
src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs+22-23
......@@ -606,32 +606,31 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span
606606 let ctxt = span.ctxt();
607607 if ctxt == SyntaxContext::root() {
608608 HasSafetyComment::Maybe
609 } else {
610 // From a macro expansion. Get the text from the start of the macro declaration to start of the
611 // unsafe block.
612 // macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; }
613 // ^--------------------------------------------^
614 if let Ok(unsafe_line) = source_map.lookup_line(span.lo())
615 && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo())
616 && Arc::ptr_eq(&unsafe_line.sf, &macro_line.sf)
617 && let Some(src) = unsafe_line.sf.src.as_deref()
618 {
619 if macro_line.line < unsafe_line.line {
620 match text_has_safety_comment(
621 src,
622 &unsafe_line.sf.lines()[macro_line.line + 1..=unsafe_line.line],
623 unsafe_line.sf.start_pos,
624 ) {
625 Some(b) => HasSafetyComment::Yes(b),
626 None => HasSafetyComment::No,
627 }
628 } else {
629 HasSafetyComment::No
609 }
610 // From a macro expansion. Get the text from the start of the macro declaration to start of the
611 // unsafe block.
612 // macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; }
613 // ^--------------------------------------------^
614 else if let Ok(unsafe_line) = source_map.lookup_line(span.lo())
615 && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo())
616 && Arc::ptr_eq(&unsafe_line.sf, &macro_line.sf)
617 && let Some(src) = unsafe_line.sf.src.as_deref()
618 {
619 if macro_line.line < unsafe_line.line {
620 match text_has_safety_comment(
621 src,
622 &unsafe_line.sf.lines()[macro_line.line + 1..=unsafe_line.line],
623 unsafe_line.sf.start_pos,
624 ) {
625 Some(b) => HasSafetyComment::Yes(b),
626 None => HasSafetyComment::No,
630627 }
631628 } else {
632 // Problem getting source text. Pretend a comment was found.
633 HasSafetyComment::Maybe
629 HasSafetyComment::No
634630 }
631 } else {
632 // Problem getting source text. Pretend a comment was found.
633 HasSafetyComment::Maybe
635634 }
636635}
637636
src/tools/clippy/clippy_utils/Cargo.toml+1-1
......@@ -1,6 +1,6 @@
11[package]
22name = "clippy_utils"
3version = "0.1.89"
3version = "0.1.90"
44edition = "2024"
55description = "Helpful tools for writing lints, provided as they are used in Clippy"
66repository = "https://github.com/rust-lang/rust-clippy"
src/tools/clippy/clippy_utils/README.md+1-1
......@@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain:
88
99<!-- begin autogenerated nightly -->
1010```
11nightly-2025-06-12
11nightly-2025-06-26
1212```
1313<!-- end autogenerated nightly -->
1414
src/tools/clippy/clippy_utils/src/consts.rs+15
......@@ -958,3 +958,18 @@ fn field_of_struct<'tcx>(
958958 None
959959 }
960960}
961
962/// If `expr` evaluates to an integer constant, return its value.
963pub fn integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
964 if let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_simple(expr) {
965 Some(value)
966 } else {
967 None
968 }
969}
970
971/// Check if `expr` evaluates to an integer constant of 0.
972#[inline]
973pub fn is_zero_integer_const(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
974 integer_const(cx, expr) == Some(0)
975}
src/tools/clippy/clippy_utils/src/diagnostics.rs+3-3
......@@ -109,7 +109,7 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult
109109 });
110110}
111111
112/// Same as `span_lint` but with an extra `help` message.
112/// Same as [`span_lint`] but with an extra `help` message.
113113///
114114/// Use this if you want to provide some general help but
115115/// can't provide a specific machine applicable suggestion.
......@@ -166,7 +166,7 @@ pub fn span_lint_and_help<T: LintContext>(
166166 });
167167}
168168
169/// Like `span_lint` but with a `note` section instead of a `help` message.
169/// Like [`span_lint`] but with a `note` section instead of a `help` message.
170170///
171171/// The `note` message is presented separately from the main lint message
172172/// and is attached to a specific span:
......@@ -226,7 +226,7 @@ pub fn span_lint_and_note<T: LintContext>(
226226 });
227227}
228228
229/// Like `span_lint` but allows to add notes, help and suggestions using a closure.
229/// Like [`span_lint`] but allows to add notes, help and suggestions using a closure.
230230///
231231/// If you need to customize your lint output a lot, use this function.
232232/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
src/tools/clippy/clippy_utils/src/lib.rs+16-6
......@@ -122,7 +122,7 @@ use rustc_span::hygiene::{ExpnKind, MacroKind};
122122use rustc_span::source_map::SourceMap;
123123use rustc_span::symbol::{Ident, Symbol, kw};
124124use rustc_span::{InnerSpan, Span};
125use source::walk_span_to_context;
125use source::{SpanRangeExt, walk_span_to_context};
126126use visitors::{Visitable, for_each_unconsumed_temporary};
127127
128128use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
......@@ -1886,10 +1886,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
18861886 _ => None,
18871887 };
18881888
1889 did.is_some_and(|did| find_attr!(
1890 cx.tcx.get_all_attrs(did),
1891 AttributeKind::MustUse { ..}
1892 ))
1889 did.is_some_and(|did| find_attr!(cx.tcx.get_all_attrs(did), AttributeKind::MustUse { .. }))
18931890}
18941891
18951892/// Checks if a function's body represents the identity function. Looks for bodies of the form:
......@@ -2713,7 +2710,7 @@ impl<'tcx> ExprUseNode<'tcx> {
27132710}
27142711
27152712/// Gets the context an expression's value is used in.
2716pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprUseCtxt<'tcx> {
2713pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtxt<'tcx> {
27172714 let mut adjustments = [].as_slice();
27182715 let mut is_ty_unified = false;
27192716 let mut moved_before_use = false;
......@@ -2790,6 +2787,19 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
27902787 });
27912788}
27922789
2790/// Checks whether a given span has any significant token. A significant token is a non-whitespace
2791/// token, including comments unless `skip_comments` is set.
2792/// This is useful to determine if there are any actual code tokens in the span that are omitted in
2793/// the late pass, such as platform-specific code.
2794pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool {
2795 matches!(span.get_source_text(cx), Some(snippet) if tokenize_with_text(&snippet).any(|(token, _, _)|
2796 match token {
2797 TokenKind::Whitespace => false,
2798 TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments,
2799 _ => true,
2800 }
2801 ))
2802}
27932803/// Returns all the comments a given span contains
27942804///
27952805/// Comments are returned wrapped with their relevant delimiters
src/tools/clippy/clippy_utils/src/msrvs.rs+2-1
......@@ -24,7 +24,7 @@ macro_rules! msrv_aliases {
2424// names may refer to stabilized feature flags or library items
2525msrv_aliases! {
2626 1,88,0 { LET_CHAINS }
27 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT }
27 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF }
2828 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL }
2929 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR }
3030 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP }
......@@ -42,6 +42,7 @@ msrv_aliases! {
4242 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
4343 1,63,0 { CLONE_INTO, CONST_SLICE_FROM_REF }
4444 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_C_FN }
45 1,61,0 { CONST_FN_TRAIT_BOUND }
4546 1,60,0 { ABS_DIFF }
4647 1,59,0 { THREAD_LOCAL_CONST_INIT }
4748 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF }
src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs+15
......@@ -32,6 +32,21 @@ pub fn is_min_const_fn<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, msrv: Ms
3232 for local in &body.local_decls {
3333 check_ty(cx, local.ty, local.source_info.span, msrv)?;
3434 }
35 if !msrv.meets(cx, msrvs::CONST_FN_TRAIT_BOUND)
36 && let Some(sized_did) = cx.tcx.lang_items().sized_trait()
37 && let Some(meta_sized_did) = cx.tcx.lang_items().meta_sized_trait()
38 && cx.tcx.param_env(def_id).caller_bounds().iter().any(|bound| {
39 bound.as_trait_clause().is_some_and(|clause| {
40 let did = clause.def_id();
41 did != sized_did && did != meta_sized_did
42 })
43 })
44 {
45 return Err((
46 body.span,
47 "non-`Sized` trait clause before `const_fn_trait_bound` is stabilized".into(),
48 ));
49 }
3550 // impl trait is gone in MIR, so check the return type manually
3651 check_ty(
3752 cx,
src/tools/clippy/clippy_utils/src/sugg.rs+21-1
......@@ -494,7 +494,17 @@ impl<T: Display> Display for ParenHelper<T> {
494494/// operators have the same
495495/// precedence.
496496pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> {
497 Sugg::MaybeParen(format!("{op}{}", expr.maybe_paren()).into())
497 // If the `expr` starts with `op` already, do not add wrap it in
498 // parentheses.
499 let expr = if let Sugg::MaybeParen(ref sugg) = expr
500 && !has_enclosing_paren(sugg)
501 && sugg.starts_with(op)
502 {
503 expr
504 } else {
505 expr.maybe_paren()
506 };
507 Sugg::MaybeParen(format!("{op}{expr}").into())
498508}
499509
500510/// Builds the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
......@@ -1016,6 +1026,16 @@ mod test {
10161026 let sugg = Sugg::BinOp(AssocOp::Binary(ast::BinOpKind::Add), "(1 + 1)".into(), "(1 + 1)".into());
10171027 assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_paren().to_string());
10181028 }
1029
1030 #[test]
1031 fn unop_parenthesize() {
1032 let sugg = Sugg::NonParen("x".into()).mut_addr();
1033 assert_eq!("&mut x", sugg.to_string());
1034 let sugg = sugg.mut_addr();
1035 assert_eq!("&mut &mut x", sugg.to_string());
1036 assert_eq!("(&mut &mut x)", sugg.maybe_paren().to_string());
1037 }
1038
10191039 #[test]
10201040 fn not_op() {
10211041 use ast::BinOpKind::{Add, And, Eq, Ge, Gt, Le, Lt, Ne, Or};
src/tools/clippy/clippy_utils/src/sym.rs+1-4
......@@ -46,7 +46,6 @@ generate! {
4646 DOUBLE_QUOTE: "\"",
4747 Deserialize,
4848 EarlyLintPass,
49 ErrorKind,
5049 IntoIter,
5150 Itertools,
5251 LF: "\n",
......@@ -65,7 +64,6 @@ generate! {
6564 RegexBuilder,
6665 RegexSet,
6766 Start,
68 Step,
6967 Symbol,
7068 SyntaxContext,
7169 TBD,
......@@ -158,7 +156,6 @@ generate! {
158156 from_ne_bytes,
159157 from_ptr,
160158 from_raw,
161 from_ref,
162159 from_str,
163160 from_str_radix,
164161 fs,
......@@ -166,6 +163,7 @@ generate! {
166163 futures_util,
167164 get,
168165 get_mut,
166 get_or_insert,
169167 get_or_insert_with,
170168 get_unchecked,
171169 get_unchecked_mut,
......@@ -216,7 +214,6 @@ generate! {
216214 max_by_key,
217215 max_value,
218216 maximum,
219 mem,
220217 min,
221218 min_by,
222219 min_by_key,
src/tools/clippy/clippy_utils/src/ty/mod.rs+10-14
......@@ -6,6 +6,7 @@ use core::ops::ControlFlow;
66use itertools::Itertools;
77use rustc_abi::VariantIdx;
88use rustc_ast::ast::Mutability;
9use rustc_attr_data_structures::{AttributeKind, find_attr};
910use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1011use rustc_hir as hir;
1112use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
......@@ -20,8 +21,8 @@ use rustc_middle::traits::EvaluationResult;
2021use rustc_middle::ty::layout::ValidityRequirement;
2122use rustc_middle::ty::{
2223 self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
23 GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable,
24 TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
24 GenericParamDefKind, IntTy, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
25 TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
2526};
2627use rustc_span::symbol::Ident;
2728use rustc_span::{DUMMY_SP, Span, Symbol, sym};
......@@ -31,8 +32,6 @@ use rustc_trait_selection::traits::{Obligation, ObligationCause};
3132use std::assert_matches::debug_assert_matches;
3233use std::collections::hash_map::Entry;
3334use std::iter;
34use rustc_attr_data_structures::find_attr;
35use rustc_attr_data_structures::AttributeKind;
3635
3736use crate::path_res;
3837use crate::paths::{PathNS, lookup_path_str};
......@@ -328,14 +327,8 @@ pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
328327// Returns whether the type has #[must_use] attribute
329328pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
330329 match ty.kind() {
331 ty::Adt(adt, _) => find_attr!(
332 cx.tcx.get_all_attrs(adt.did()),
333 AttributeKind::MustUse { ..}
334 ),
335 ty::Foreign(did) => find_attr!(
336 cx.tcx.get_all_attrs(*did),
337 AttributeKind::MustUse { ..}
338 ),
330 ty::Adt(adt, _) => find_attr!(cx.tcx.get_all_attrs(adt.did()), AttributeKind::MustUse { .. }),
331 ty::Foreign(did) => find_attr!(cx.tcx.get_all_attrs(*did), AttributeKind::MustUse { .. }),
339332 ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => {
340333 // for the Array case we don't need to care for the len == 0 case
341334 // because we don't want to lint functions returning empty arrays
......@@ -345,7 +338,10 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
345338 ty::Alias(ty::Opaque, AliasTy { def_id, .. }) => {
346339 for (predicate, _) in cx.tcx.explicit_item_self_bounds(def_id).skip_binder() {
347340 if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
348 && find_attr!(cx.tcx.get_all_attrs(trait_predicate.trait_ref.def_id), AttributeKind::MustUse { ..})
341 && find_attr!(
342 cx.tcx.get_all_attrs(trait_predicate.trait_ref.def_id),
343 AttributeKind::MustUse { .. }
344 )
349345 {
350346 return true;
351347 }
......@@ -355,7 +351,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
355351 ty::Dynamic(binder, _, _) => {
356352 for predicate in *binder {
357353 if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
358 && find_attr!(cx.tcx.get_all_attrs(trait_ref.def_id), AttributeKind::MustUse { ..})
354 && find_attr!(cx.tcx.get_all_attrs(trait_ref.def_id), AttributeKind::MustUse { .. })
359355 {
360356 return true;
361357 }
src/tools/clippy/declare_clippy_lint/Cargo.toml created+10
......@@ -0,0 +1,10 @@
1[package]
2name = "declare_clippy_lint"
3version = "0.1.90"
4edition = "2024"
5repository = "https://github.com/rust-lang/rust-clippy"
6license = "MIT OR Apache-2.0"
7
8[package.metadata.rust-analyzer]
9# This crate uses #[feature(rustc_private)]
10rustc_private = true
src/tools/clippy/declare_clippy_lint/src/lib.rs created+280
......@@ -0,0 +1,280 @@
1#![feature(macro_metavar_expr_concat, rustc_private)]
2
3extern crate rustc_lint;
4
5use rustc_lint::{Lint, LintId, LintStore};
6
7// Needed by `declare_clippy_lint!`.
8pub extern crate rustc_session;
9
10#[derive(Default)]
11pub struct LintListBuilder {
12 lints: Vec<&'static Lint>,
13 all: Vec<LintId>,
14 cargo: Vec<LintId>,
15 complexity: Vec<LintId>,
16 correctness: Vec<LintId>,
17 nursery: Vec<LintId>,
18 pedantic: Vec<LintId>,
19 perf: Vec<LintId>,
20 restriction: Vec<LintId>,
21 style: Vec<LintId>,
22 suspicious: Vec<LintId>,
23}
24impl LintListBuilder {
25 pub fn insert(&mut self, lints: &[&LintInfo]) {
26 #[allow(clippy::enum_glob_use)]
27 use LintCategory::*;
28
29 self.lints.extend(lints.iter().map(|&x| x.lint));
30 for &&LintInfo { lint, category, .. } in lints {
31 let (all, cat) = match category {
32 Complexity => (Some(&mut self.all), &mut self.complexity),
33 Correctness => (Some(&mut self.all), &mut self.correctness),
34 Perf => (Some(&mut self.all), &mut self.perf),
35 Style => (Some(&mut self.all), &mut self.style),
36 Suspicious => (Some(&mut self.all), &mut self.suspicious),
37 Cargo => (None, &mut self.cargo),
38 Nursery => (None, &mut self.nursery),
39 Pedantic => (None, &mut self.pedantic),
40 Restriction => (None, &mut self.restriction),
41 };
42 if let Some(all) = all {
43 all.push(LintId::of(lint));
44 }
45 cat.push(LintId::of(lint));
46 }
47 }
48
49 pub fn register(self, store: &mut LintStore) {
50 store.register_lints(&self.lints);
51 store.register_group(true, "clippy::all", Some("clippy_all"), self.all);
52 store.register_group(true, "clippy::cargo", Some("clippy_cargo"), self.cargo);
53 store.register_group(true, "clippy::complexity", Some("clippy_complexity"), self.complexity);
54 store.register_group(
55 true,
56 "clippy::correctness",
57 Some("clippy_correctness"),
58 self.correctness,
59 );
60 store.register_group(true, "clippy::nursery", Some("clippy_nursery"), self.nursery);
61 store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), self.pedantic);
62 store.register_group(true, "clippy::perf", Some("clippy_perf"), self.perf);
63 store.register_group(
64 true,
65 "clippy::restriction",
66 Some("clippy_restriction"),
67 self.restriction,
68 );
69 store.register_group(true, "clippy::style", Some("clippy_style"), self.style);
70 store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), self.suspicious);
71 }
72}
73
74#[derive(Copy, Clone, Debug)]
75pub enum LintCategory {
76 Cargo,
77 Complexity,
78 Correctness,
79 Nursery,
80 Pedantic,
81 Perf,
82 Restriction,
83 Style,
84 Suspicious,
85}
86impl LintCategory {
87 #[must_use]
88 pub fn name(self) -> &'static str {
89 match self {
90 Self::Cargo => "cargo",
91 Self::Complexity => "complexity",
92 Self::Correctness => "correctness",
93 Self::Nursery => "nursery",
94 Self::Pedantic => "pedantic",
95 Self::Perf => "perf",
96 Self::Restriction => "restriction",
97 Self::Style => "style",
98 Self::Suspicious => "suspicious",
99 }
100 }
101}
102
103pub struct LintInfo {
104 pub lint: &'static Lint,
105 pub category: LintCategory,
106 pub explanation: &'static str,
107 /// e.g. `clippy_lints/src/absolute_paths.rs#43`
108 pub location: &'static str,
109 pub version: &'static str,
110}
111
112impl LintInfo {
113 /// Returns the lint name in lowercase without the `clippy::` prefix
114 #[must_use]
115 #[expect(clippy::missing_panics_doc)]
116 pub fn name_lower(&self) -> String {
117 self.lint.name.strip_prefix("clippy::").unwrap().to_ascii_lowercase()
118 }
119}
120
121#[macro_export]
122macro_rules! declare_clippy_lint_inner {
123 (
124 $(#[doc = $docs:literal])*
125 #[clippy::version = $version:literal]
126 $vis:vis $lint_name:ident,
127 $level:ident,
128 $category:ident,
129 $desc:literal
130 $(, @eval_always = $eval_always:literal)?
131 ) => {
132 $crate::rustc_session::declare_tool_lint! {
133 $(#[doc = $docs])*
134 #[clippy::version = $version]
135 $vis clippy::$lint_name,
136 $level,
137 $desc,
138 report_in_external_macro:true
139 $(, @eval_always = $eval_always)?
140 }
141
142 pub(crate) static ${concat($lint_name, _INFO)}: &'static $crate::LintInfo = &$crate::LintInfo {
143 lint: $lint_name,
144 category: $crate::LintCategory::$category,
145 explanation: concat!($($docs,"\n",)*),
146 location: concat!(file!(), "#L", line!()),
147 version: $version,
148 };
149 };
150}
151
152#[macro_export]
153macro_rules! declare_clippy_lint {
154 (
155 $(#[$($meta:tt)*])*
156 $vis:vis $lint_name:ident,
157 correctness,
158 $($rest:tt)*
159 ) => {
160 $crate::declare_clippy_lint_inner! {
161 $(#[$($meta)*])*
162 $vis $lint_name,
163 Deny,
164 Correctness,
165 $($rest)*
166 }
167 };
168 (
169 $(#[$($meta:tt)*])*
170 $vis:vis $lint_name:ident,
171 complexity,
172 $($rest:tt)*
173 ) => {
174 $crate::declare_clippy_lint_inner! {
175 $(#[$($meta)*])*
176 $vis $lint_name,
177 Warn,
178 Complexity,
179 $($rest)*
180 }
181 };
182 (
183 $(#[$($meta:tt)*])*
184 $vis:vis $lint_name:ident,
185 perf,
186 $($rest:tt)*
187 ) => {
188 $crate::declare_clippy_lint_inner! {
189 $(#[$($meta)*])*
190 $vis $lint_name,
191 Warn,
192 Perf,
193 $($rest)*
194 }
195 };
196 (
197 $(#[$($meta:tt)*])*
198 $vis:vis $lint_name:ident,
199 style,
200 $($rest:tt)*
201 ) => {
202 $crate::declare_clippy_lint_inner! {
203 $(#[$($meta)*])*
204 $vis $lint_name,
205 Warn,
206 Style,
207 $($rest)*
208 }
209 };
210 (
211 $(#[$($meta:tt)*])*
212 $vis:vis $lint_name:ident,
213 suspicious,
214 $($rest:tt)*
215 ) => {
216 $crate::declare_clippy_lint_inner! {
217 $(#[$($meta)*])*
218 $vis $lint_name,
219 Warn,
220 Suspicious,
221 $($rest)*
222 }
223 };
224 (
225 $(#[$($meta:tt)*])*
226 $vis:vis $lint_name:ident,
227 cargo,
228 $($rest:tt)*
229 ) => {
230 $crate::declare_clippy_lint_inner! {
231 $(#[$($meta)*])*
232 $vis $lint_name,
233 Allow,
234 Cargo,
235 $($rest)*
236 }
237 };
238 (
239 $(#[$($meta:tt)*])*
240 $vis:vis $lint_name:ident,
241 nursery,
242 $($rest:tt)*
243 ) => {
244 $crate::declare_clippy_lint_inner! {
245 $(#[$($meta)*])*
246 $vis $lint_name,
247 Allow,
248 Nursery,
249 $($rest)*
250 }
251 };
252 (
253 $(#[$($meta:tt)*])*
254 $vis:vis $lint_name:ident,
255 pedantic,
256 $($rest:tt)*
257 ) => {
258 $crate::declare_clippy_lint_inner! {
259 $(#[$($meta)*])*
260 $vis $lint_name,
261 Allow,
262 Pedantic,
263 $($rest)*
264 }
265 };
266 (
267 $(#[$($meta:tt)*])*
268 $vis:vis $lint_name:ident,
269 restriction,
270 $($rest:tt)*
271 ) => {
272 $crate::declare_clippy_lint_inner! {
273 $(#[$($meta)*])*
274 $vis $lint_name,
275 Allow,
276 Restriction,
277 $($rest)*
278 }
279 };
280}
src/tools/clippy/lintcheck/src/main.rs+1-1
......@@ -45,7 +45,7 @@ use rayon::prelude::*;
4545
4646#[must_use]
4747pub fn target_dir() -> String {
48 env::var("CARGO_TARGET_DIR").unwrap_or("target".to_owned())
48 env::var("CARGO_TARGET_DIR").unwrap_or_else(|_| "target".to_owned())
4949}
5050
5151fn lintcheck_sources() -> String {
src/tools/clippy/rust-toolchain.toml+1-1
......@@ -1,6 +1,6 @@
11[toolchain]
22# begin autogenerated nightly
3channel = "nightly-2025-06-12"
3channel = "nightly-2025-06-26"
44# end autogenerated nightly
55components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
66profile = "minimal"
src/tools/clippy/src/driver.rs+7-1
......@@ -19,6 +19,7 @@ extern crate rustc_span;
1919extern crate tikv_jemalloc_sys as jemalloc_sys;
2020
2121use clippy_utils::sym;
22use declare_clippy_lint::LintListBuilder;
2223use rustc_interface::interface;
2324use rustc_session::EarlyDiagCtxt;
2425use rustc_session::config::ErrorOutputType;
......@@ -156,8 +157,13 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
156157 (previous)(sess, lint_store);
157158 }
158159
160 let mut list_builder = LintListBuilder::default();
161 list_builder.insert(clippy_lints::declared_lints::LINTS);
162 list_builder.register(lint_store);
163
159164 let conf = clippy_config::Conf::read(sess, &conf_path);
160 clippy_lints::register_lints(lint_store, conf);
165 clippy_lints::register_lint_passes(lint_store, conf);
166
161167 #[cfg(feature = "internal")]
162168 clippy_lints_internal::register_lints(lint_store);
163169 }));
src/tools/clippy/src/main.rs+1-1
......@@ -107,7 +107,7 @@ impl ClippyCmd {
107107 }
108108
109109 fn into_std_cmd(self) -> Command {
110 let mut cmd = Command::new(env::var("CARGO").unwrap_or("cargo".into()));
110 let mut cmd = Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into()));
111111 let clippy_args: String = self
112112 .clippy_args
113113 .iter()
src/tools/clippy/tests/compile-test.rs+3-3
......@@ -7,9 +7,9 @@ use askama::filters::Safe;
77use cargo_metadata::Message;
88use cargo_metadata::diagnostic::{Applicability, Diagnostic};
99use clippy_config::ClippyConfiguration;
10use clippy_lints::LintInfo;
1110use clippy_lints::declared_lints::LINTS;
1211use clippy_lints::deprecated_lints::{DEPRECATED, DEPRECATED_VERSION, RENAMED};
12use declare_clippy_lint::LintInfo;
1313use pulldown_cmark::{Options, Parser, html};
1414use serde::Deserialize;
1515use test_utils::IS_RUSTC_TEST_SUITE;
......@@ -568,10 +568,10 @@ impl LintMetadata {
568568 Self {
569569 id: name,
570570 id_location: Some(lint.location),
571 group: lint.category_str(),
571 group: lint.category.name(),
572572 level: lint.lint.default_level.as_str(),
573573 docs,
574 version: lint.version.unwrap(),
574 version: lint.version,
575575 applicability,
576576 }
577577 }
src/tools/clippy/tests/dogfood.rs+1
......@@ -40,6 +40,7 @@ fn dogfood() {
4040 "clippy_lints",
4141 "clippy_utils",
4242 "clippy_config",
43 "declare_clippy_lint",
4344 "lintcheck",
4445 "rustc_tools_util",
4546 ] {
src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_else_if.fixed created+50
......@@ -0,0 +1,50 @@
1#![allow(clippy::eq_op, clippy::nonminimal_bool)]
2
3#[rustfmt::skip]
4#[warn(clippy::collapsible_if)]
5fn main() {
6 let (x, y) = ("hello", "world");
7
8 if x == "hello" {
9 todo!()
10 }
11 // Comment must be kept
12 else if y == "world" {
13 println!("Hello world!");
14 }
15 //~^^^^^^ collapsible_else_if
16
17 if x == "hello" {
18 todo!()
19 } // Inner comment
20 else if y == "world" {
21 println!("Hello world!");
22 }
23 //~^^^^^ collapsible_else_if
24
25 if x == "hello" {
26 todo!()
27 }
28 /* Inner comment */
29 else if y == "world" {
30 println!("Hello world!");
31 }
32 //~^^^^^^ collapsible_else_if
33
34 if x == "hello" {
35 todo!()
36 } /* Inner comment */
37 else if y == "world" {
38 println!("Hello world!");
39 }
40 //~^^^^^ collapsible_else_if
41
42 if x == "hello" {
43 todo!()
44 } /* This should not be removed */ /* So does this */
45 // Comment must be kept
46 else if y == "world" {
47 println!("Hello world!");
48 }
49 //~^^^^^^ collapsible_else_if
50}
src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_else_if.rs created+55
......@@ -0,0 +1,55 @@
1#![allow(clippy::eq_op, clippy::nonminimal_bool)]
2
3#[rustfmt::skip]
4#[warn(clippy::collapsible_if)]
5fn main() {
6 let (x, y) = ("hello", "world");
7
8 if x == "hello" {
9 todo!()
10 } else {
11 // Comment must be kept
12 if y == "world" {
13 println!("Hello world!");
14 }
15 }
16 //~^^^^^^ collapsible_else_if
17
18 if x == "hello" {
19 todo!()
20 } else { // Inner comment
21 if y == "world" {
22 println!("Hello world!");
23 }
24 }
25 //~^^^^^ collapsible_else_if
26
27 if x == "hello" {
28 todo!()
29 } else {
30 /* Inner comment */
31 if y == "world" {
32 println!("Hello world!");
33 }
34 }
35 //~^^^^^^ collapsible_else_if
36
37 if x == "hello" {
38 todo!()
39 } else { /* Inner comment */
40 if y == "world" {
41 println!("Hello world!");
42 }
43 }
44 //~^^^^^ collapsible_else_if
45
46 if x == "hello" {
47 todo!()
48 } /* This should not be removed */ else /* So does this */ {
49 // Comment must be kept
50 if y == "world" {
51 println!("Hello world!");
52 }
53 }
54 //~^^^^^^ collapsible_else_if
55}
src/tools/clippy/tests/ui-toml/collapsible_if/collapsible_else_if.stderr created+105
......@@ -0,0 +1,105 @@
1error: this `else { if .. }` block can be collapsed
2 --> tests/ui-toml/collapsible_if/collapsible_else_if.rs:10:12
3 |
4LL | } else {
5 | ____________^
6LL | | // Comment must be kept
7LL | | if y == "world" {
8LL | | println!("Hello world!");
9LL | | }
10LL | | }
11 | |_____^
12 |
13 = note: `-D clippy::collapsible-else-if` implied by `-D warnings`
14 = help: to override `-D warnings` add `#[allow(clippy::collapsible_else_if)]`
15help: collapse nested if block
16 |
17LL ~ }
18LL | // Comment must be kept
19LL ~ else if y == "world" {
20LL | println!("Hello world!");
21LL ~ }
22 |
23
24error: this `else { if .. }` block can be collapsed
25 --> tests/ui-toml/collapsible_if/collapsible_else_if.rs:20:12
26 |
27LL | } else { // Inner comment
28 | ____________^
29LL | | if y == "world" {
30LL | | println!("Hello world!");
31LL | | }
32LL | | }
33 | |_____^
34 |
35help: collapse nested if block
36 |
37LL ~ } // Inner comment
38LL ~ else if y == "world" {
39LL | println!("Hello world!");
40LL ~ }
41 |
42
43error: this `else { if .. }` block can be collapsed
44 --> tests/ui-toml/collapsible_if/collapsible_else_if.rs:29:12
45 |
46LL | } else {
47 | ____________^
48LL | | /* Inner comment */
49LL | | if y == "world" {
50LL | | println!("Hello world!");
51LL | | }
52LL | | }
53 | |_____^
54 |
55help: collapse nested if block
56 |
57LL ~ }
58LL | /* Inner comment */
59LL ~ else if y == "world" {
60LL | println!("Hello world!");
61LL ~ }
62 |
63
64error: this `else { if .. }` block can be collapsed
65 --> tests/ui-toml/collapsible_if/collapsible_else_if.rs:39:12
66 |
67LL | } else { /* Inner comment */
68 | ____________^
69LL | | if y == "world" {
70LL | | println!("Hello world!");
71LL | | }
72LL | | }
73 | |_____^
74 |
75help: collapse nested if block
76 |
77LL ~ } /* Inner comment */
78LL ~ else if y == "world" {
79LL | println!("Hello world!");
80LL ~ }
81 |
82
83error: this `else { if .. }` block can be collapsed
84 --> tests/ui-toml/collapsible_if/collapsible_else_if.rs:48:64
85 |
86LL | } /* This should not be removed */ else /* So does this */ {
87 | ________________________________________________________________^
88LL | | // Comment must be kept
89LL | | if y == "world" {
90LL | | println!("Hello world!");
91LL | | }
92LL | | }
93 | |_____^
94 |
95help: collapse nested if block
96 |
97LL ~ } /* This should not be removed */ /* So does this */
98LL | // Comment must be kept
99LL ~ else if y == "world" {
100LL | println!("Hello world!");
101LL ~ }
102 |
103
104error: aborting due to 5 previous errors
105
src/tools/clippy/tests/ui/borrow_deref_ref.fixed+47
......@@ -124,3 +124,50 @@ mod issue_11346 {
124124 //~^ borrow_deref_ref
125125 }
126126}
127
128fn issue_14934() {
129 let x: &'static str = "x";
130 let y = "y".to_string();
131 {
132 #[expect(clippy::toplevel_ref_arg)]
133 let ref mut x = &*x; // Do not lint
134 *x = &*y;
135 }
136 {
137 let mut x = x;
138 //~^ borrow_deref_ref
139 x = &*y;
140 }
141 {
142 #[expect(clippy::toplevel_ref_arg, clippy::needless_borrow)]
143 let ref x = x;
144 //~^ borrow_deref_ref
145 }
146 {
147 #[expect(clippy::toplevel_ref_arg)]
148 let ref mut x = std::convert::identity(x);
149 //~^ borrow_deref_ref
150 *x = &*y;
151 }
152 {
153 #[derive(Clone)]
154 struct S(&'static str);
155 let s = S("foo");
156 #[expect(clippy::toplevel_ref_arg)]
157 let ref mut x = &*s.0; // Do not lint
158 *x = "bar";
159 #[expect(clippy::toplevel_ref_arg)]
160 let ref mut x = s.clone().0;
161 //~^ borrow_deref_ref
162 *x = "bar";
163 #[expect(clippy::toplevel_ref_arg)]
164 let ref mut x = &*std::convert::identity(&s).0;
165 *x = "bar";
166 }
167 {
168 let y = &1;
169 #[expect(clippy::toplevel_ref_arg)]
170 let ref mut x = { y };
171 //~^ borrow_deref_ref
172 }
173}
src/tools/clippy/tests/ui/borrow_deref_ref.rs+47
......@@ -124,3 +124,50 @@ mod issue_11346 {
124124 //~^ borrow_deref_ref
125125 }
126126}
127
128fn issue_14934() {
129 let x: &'static str = "x";
130 let y = "y".to_string();
131 {
132 #[expect(clippy::toplevel_ref_arg)]
133 let ref mut x = &*x; // Do not lint
134 *x = &*y;
135 }
136 {
137 let mut x = &*x;
138 //~^ borrow_deref_ref
139 x = &*y;
140 }
141 {
142 #[expect(clippy::toplevel_ref_arg, clippy::needless_borrow)]
143 let ref x = &*x;
144 //~^ borrow_deref_ref
145 }
146 {
147 #[expect(clippy::toplevel_ref_arg)]
148 let ref mut x = &*std::convert::identity(x);
149 //~^ borrow_deref_ref
150 *x = &*y;
151 }
152 {
153 #[derive(Clone)]
154 struct S(&'static str);
155 let s = S("foo");
156 #[expect(clippy::toplevel_ref_arg)]
157 let ref mut x = &*s.0; // Do not lint
158 *x = "bar";
159 #[expect(clippy::toplevel_ref_arg)]
160 let ref mut x = &*s.clone().0;
161 //~^ borrow_deref_ref
162 *x = "bar";
163 #[expect(clippy::toplevel_ref_arg)]
164 let ref mut x = &*std::convert::identity(&s).0;
165 *x = "bar";
166 }
167 {
168 let y = &1;
169 #[expect(clippy::toplevel_ref_arg)]
170 let ref mut x = { &*y };
171 //~^ borrow_deref_ref
172 }
173}
src/tools/clippy/tests/ui/borrow_deref_ref.stderr+31-1
......@@ -25,5 +25,35 @@ error: deref on an immutable reference
2525LL | (&*s).foo();
2626 | ^^^^^ help: if you would like to reborrow, try removing `&*`: `s`
2727
28error: aborting due to 4 previous errors
28error: deref on an immutable reference
29 --> tests/ui/borrow_deref_ref.rs:137:21
30 |
31LL | let mut x = &*x;
32 | ^^^ help: if you would like to reborrow, try removing `&*`: `x`
33
34error: deref on an immutable reference
35 --> tests/ui/borrow_deref_ref.rs:143:21
36 |
37LL | let ref x = &*x;
38 | ^^^ help: if you would like to reborrow, try removing `&*`: `x`
39
40error: deref on an immutable reference
41 --> tests/ui/borrow_deref_ref.rs:148:25
42 |
43LL | let ref mut x = &*std::convert::identity(x);
44 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `std::convert::identity(x)`
45
46error: deref on an immutable reference
47 --> tests/ui/borrow_deref_ref.rs:160:25
48 |
49LL | let ref mut x = &*s.clone().0;
50 | ^^^^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `s.clone().0`
51
52error: deref on an immutable reference
53 --> tests/ui/borrow_deref_ref.rs:170:27
54 |
55LL | let ref mut x = { &*y };
56 | ^^^ help: if you would like to reborrow, try removing `&*`: `y`
57
58error: aborting due to 9 previous errors
2959
src/tools/clippy/tests/ui/borrow_interior_mutable_const.rs+16
......@@ -218,4 +218,20 @@ fn main() {
218218 let _ = &S::VALUE.1; //~ borrow_interior_mutable_const
219219 let _ = &S::VALUE.2;
220220 }
221 {
222 pub struct Foo<T, const N: usize>(pub Entry<N>, pub T);
223
224 pub struct Entry<const N: usize>(pub Cell<[u32; N]>);
225
226 impl<const N: usize> Entry<N> {
227 const INIT: Self = Self(Cell::new([42; N]));
228 }
229
230 impl<T, const N: usize> Foo<T, N> {
231 pub fn make_foo(v: T) -> Self {
232 // Used to ICE due to incorrect instantiation.
233 Foo(Entry::INIT, v)
234 }
235 }
236 }
221237}
src/tools/clippy/tests/ui/box_default.fixed+1-1
......@@ -126,7 +126,7 @@ fn issue_10381() {
126126 impl Bar for Foo {}
127127
128128 fn maybe_get_bar(i: u32) -> Option<Box<dyn Bar>> {
129 if i % 2 == 0 {
129 if i.is_multiple_of(2) {
130130 Some(Box::new(Foo::default()))
131131 } else {
132132 None
src/tools/clippy/tests/ui/box_default.rs+1-1
......@@ -126,7 +126,7 @@ fn issue_10381() {
126126 impl Bar for Foo {}
127127
128128 fn maybe_get_bar(i: u32) -> Option<Box<dyn Bar>> {
129 if i % 2 == 0 {
129 if i.is_multiple_of(2) {
130130 Some(Box::new(Foo::default()))
131131 } else {
132132 None
src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.rs+24
......@@ -276,3 +276,27 @@ mod issue14873 {
276276 }
277277 }
278278}
279
280fn issue15004() {
281 let a = 12u32;
282 let b = 13u32;
283 let mut c = 8u32;
284
285 let mut result = if b > a {
286 c += 1;
287 0
288 } else {
289 c += 2;
290 0
291 //~^ branches_sharing_code
292 };
293
294 result = if b > a {
295 c += 1;
296 1
297 } else {
298 c += 2;
299 1
300 //~^ branches_sharing_code
301 };
302}
src/tools/clippy/tests/ui/branches_sharing_code/shared_at_bottom.stderr+31-1
......@@ -172,5 +172,35 @@ LL ~ }
172172LL + let y = 1;
173173 |
174174
175error: aborting due to 10 previous errors
175error: all if blocks contain the same code at the end
176 --> tests/ui/branches_sharing_code/shared_at_bottom.rs:290:5
177 |
178LL | / 0
179LL | |
180LL | | };
181 | |_____^
182 |
183 = note: the end suggestion probably needs some adjustments to use the expression result correctly
184help: consider moving these statements after the if
185 |
186LL ~ }
187LL ~ 0;
188 |
189
190error: all if blocks contain the same code at the end
191 --> tests/ui/branches_sharing_code/shared_at_bottom.rs:299:5
192 |
193LL | / 1
194LL | |
195LL | | };
196 | |_____^
197 |
198 = note: the end suggestion probably needs some adjustments to use the expression result correctly
199help: consider moving these statements after the if
200 |
201LL ~ }
202LL ~ 1;
203 |
204
205error: aborting due to 12 previous errors
176206
src/tools/clippy/tests/ui/collapsible_else_if.fixed+18
......@@ -86,3 +86,21 @@ fn issue_7318() {
8686 }else if false {}
8787 //~^^^ collapsible_else_if
8888}
89
90fn issue14799() {
91 use std::ops::ControlFlow;
92
93 let c: ControlFlow<_, ()> = ControlFlow::Break(Some(42));
94 if let ControlFlow::Break(Some(_)) = c {
95 todo!();
96 } else {
97 #[cfg(target_os = "freebsd")]
98 todo!();
99
100 if let ControlFlow::Break(None) = c {
101 todo!();
102 } else {
103 todo!();
104 }
105 }
106}
src/tools/clippy/tests/ui/collapsible_else_if.rs+18
......@@ -102,3 +102,21 @@ fn issue_7318() {
102102 }
103103 //~^^^ collapsible_else_if
104104}
105
106fn issue14799() {
107 use std::ops::ControlFlow;
108
109 let c: ControlFlow<_, ()> = ControlFlow::Break(Some(42));
110 if let ControlFlow::Break(Some(_)) = c {
111 todo!();
112 } else {
113 #[cfg(target_os = "freebsd")]
114 todo!();
115
116 if let ControlFlow::Break(None) = c {
117 todo!();
118 } else {
119 todo!();
120 }
121 }
122}
src/tools/clippy/tests/ui/collapsible_if.fixed+9
......@@ -154,3 +154,12 @@ fn issue14722() {
154154 None
155155 };
156156}
157
158fn issue14799() {
159 if true {
160 #[cfg(target_os = "freebsd")]
161 todo!();
162
163 if true {}
164 };
165}
src/tools/clippy/tests/ui/collapsible_if.rs+9
......@@ -164,3 +164,12 @@ fn issue14722() {
164164 None
165165 };
166166}
167
168fn issue14799() {
169 if true {
170 #[cfg(target_os = "freebsd")]
171 todo!();
172
173 if true {}
174 };
175}
src/tools/clippy/tests/ui/doc/needless_doctest_main.rs+110-2
......@@ -1,5 +1,3 @@
1//@ check-pass
2
31#![warn(clippy::needless_doctest_main)]
42//! issue 10491:
53//! ```rust,no_test
......@@ -19,4 +17,114 @@
1917/// ```
2018fn foo() {}
2119
20#[rustfmt::skip]
21/// Description
22/// ```rust
23/// fn main() {
24//~^ error: needless `fn main` in doctest
25/// let a = 0;
26/// }
27/// ```
28fn mulpipulpi() {}
29
30#[rustfmt::skip]
31/// With a `#[no_main]`
32/// ```rust
33/// #[no_main]
34/// fn a() {
35/// let _ = 0;
36/// }
37/// ```
38fn pulpimulpi() {}
39
40// Without a `#[no_main]` attribute
41/// ```rust
42/// fn a() {
43/// let _ = 0;
44/// }
45/// ```
46fn plumilupi() {}
47
48#[rustfmt::skip]
49/// Additional function, shouldn't trigger
50/// ```rust
51/// fn additional_function() {
52/// let _ = 0;
53/// // Thus `fn main` is actually relevant!
54/// }
55/// fn main() {
56/// let _ = 0;
57/// }
58/// ```
59fn mlupipupi() {}
60
61#[rustfmt::skip]
62/// Additional function AFTER main, shouldn't trigger
63/// ```rust
64/// fn main() {
65/// let _ = 0;
66/// }
67/// fn additional_function() {
68/// let _ = 0;
69/// // Thus `fn main` is actually relevant!
70/// }
71/// ```
72fn lumpimupli() {}
73
74#[rustfmt::skip]
75/// Ignore code block, should not lint at all
76/// ```rust, ignore
77/// fn main() {
78//~^ error: needless `fn main` in doctest
79/// // Hi!
80/// let _ = 0;
81/// }
82/// ```
83fn mpulpilumi() {}
84
85#[rustfmt::skip]
86/// Spaces in weird positions (including an \u{A0} after `main`)
87/// ```rust
88/// fn main (){
89//~^ error: needless `fn main` in doctest
90/// let _ = 0;
91/// }
92/// ```
93fn plumpiplupi() {}
94
95/// 4 Functions, this should not lint because there are several function
96///
97/// ```rust
98/// fn a() {let _ = 0; }
99/// fn b() {let _ = 0; }
100/// fn main() { let _ = 0; }
101/// fn d() { let _ = 0; }
102/// ```
103fn pulmipulmip() {}
104
105/// 3 Functions but main is first, should also not lint
106///
107///```rust
108/// fn main() { let _ = 0; }
109/// fn b() { let _ = 0; }
110/// fn c() { let _ = 0; }
111/// ```
112fn pmuplimulip() {}
113
22114fn main() {}
115
116fn issue8244() -> Result<(), ()> {
117 //! ```compile_fail
118 //! fn test() -> Result< {}
119 //! ```
120 Ok(())
121}
122
123/// # Examples
124///
125/// ```
126/// use std::error::Error;
127/// fn main() -> Result<(), Box<dyn Error>/* > */ {
128/// }
129/// ```
130fn issue15041() {}
src/tools/clippy/tests/ui/doc/needless_doctest_main.stderr created+36
......@@ -0,0 +1,36 @@
1error: needless `fn main` in doctest
2 --> tests/ui/doc/needless_doctest_main.rs:23:5
3 |
4LL | /// fn main() {
5 | _____^
6LL | |
7LL | | /// let a = 0;
8LL | | /// }
9 | |_____^
10 |
11 = note: `-D clippy::needless-doctest-main` implied by `-D warnings`
12 = help: to override `-D warnings` add `#[allow(clippy::needless_doctest_main)]`
13
14error: needless `fn main` in doctest
15 --> tests/ui/doc/needless_doctest_main.rs:77:5
16 |
17LL | /// fn main() {
18 | _____^
19LL | |
20LL | | /// // Hi!
21LL | | /// let _ = 0;
22LL | | /// }
23 | |_____^
24
25error: needless `fn main` in doctest
26 --> tests/ui/doc/needless_doctest_main.rs:88:5
27 |
28LL | /// fn main (){
29 | _____^
30LL | |
31LL | | /// let _ = 0;
32LL | | /// }
33 | |_____^
34
35error: aborting due to 3 previous errors
36
src/tools/clippy/tests/ui/doc_broken_link.rs created+72
......@@ -0,0 +1,72 @@
1#![warn(clippy::doc_broken_link)]
2
3fn main() {}
4
5pub struct FakeType {}
6
7/// This might be considered a link false positive
8/// and should be ignored by this lint rule:
9/// Example of referencing some code with brackets [FakeType].
10pub fn doc_ignore_link_false_positive_1() {}
11
12/// This might be considered a link false positive
13/// and should be ignored by this lint rule:
14/// [`FakeType`]. Continue text after brackets,
15/// then (something in
16/// parenthesis).
17pub fn doc_ignore_link_false_positive_2() {}
18
19/// Test valid link, whole link single line.
20/// [doc valid link](https://test.fake/doc_valid_link)
21pub fn doc_valid_link() {}
22
23/// Test valid link, whole link single line but it has special chars such as brackets and
24/// parenthesis. [doc invalid link url invalid char](https://test.fake/doc_valid_link_url_invalid_char?foo[bar]=1&bar(foo)=2)
25pub fn doc_valid_link_url_invalid_char() {}
26
27/// Test valid link, text tag broken across multiple lines.
28/// [doc valid link broken
29/// text](https://test.fake/doc_valid_link_broken_text)
30pub fn doc_valid_link_broken_text() {}
31
32/// Test valid link, url tag broken across multiple lines, but
33/// the whole url part in a single line.
34/// [doc valid link broken url tag two lines first](https://test.fake/doc_valid_link_broken_url_tag_two_lines_first
35/// )
36pub fn doc_valid_link_broken_url_tag_two_lines_first() {}
37
38/// Test valid link, url tag broken across multiple lines, but
39/// the whole url part in a single line.
40/// [doc valid link broken url tag two lines second](
41/// https://test.fake/doc_valid_link_broken_url_tag_two_lines_second)
42pub fn doc_valid_link_broken_url_tag_two_lines_second() {}
43
44/// Test valid link, url tag broken across multiple lines, but
45/// the whole url part in a single line, but the closing pharentesis
46/// in a third line.
47/// [doc valid link broken url tag three lines](
48/// https://test.fake/doc_valid_link_broken_url_tag_three_lines
49/// )
50pub fn doc_valid_link_broken_url_tag_three_lines() {}
51
52/// Test invalid link, url part broken across multiple lines.
53/// [doc invalid link broken url scheme part](https://
54/// test.fake/doc_invalid_link_broken_url_scheme_part)
55//~^^ ERROR: possible broken doc link: broken across multiple lines
56pub fn doc_invalid_link_broken_url_scheme_part() {}
57
58/// Test invalid link, url part broken across multiple lines.
59/// [doc invalid link broken url host part](https://test
60/// .fake/doc_invalid_link_broken_url_host_part)
61//~^^ ERROR: possible broken doc link: broken across multiple lines
62pub fn doc_invalid_link_broken_url_host_part() {}
63
64/// Test invalid link, for multiple urls in the same block of comment.
65/// There is a [fist link - invalid](https://test
66/// .fake) then it continues
67//~^^ ERROR: possible broken doc link: broken across multiple lines
68/// with a [second link - valid](https://test.fake/doc_valid_link) and another [third link - invalid](https://test
69/// .fake). It ends with another
70//~^^ ERROR: possible broken doc link: broken across multiple lines
71/// line of comment.
72pub fn doc_multiple_invalid_link_broken_url() {}
src/tools/clippy/tests/ui/doc_broken_link.stderr created+29
......@@ -0,0 +1,29 @@
1error: possible broken doc link: broken across multiple lines
2 --> tests/ui/doc_broken_link.rs:53:5
3 |
4LL | /// [doc invalid link broken url scheme part](https://
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6 |
7 = note: `-D clippy::doc-broken-link` implied by `-D warnings`
8 = help: to override `-D warnings` add `#[allow(clippy::doc_broken_link)]`
9
10error: possible broken doc link: broken across multiple lines
11 --> tests/ui/doc_broken_link.rs:59:5
12 |
13LL | /// [doc invalid link broken url host part](https://test
14 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15
16error: possible broken doc link: broken across multiple lines
17 --> tests/ui/doc_broken_link.rs:65:16
18 |
19LL | /// There is a [fist link - invalid](https://test
20 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21
22error: possible broken doc link: broken across multiple lines
23 --> tests/ui/doc_broken_link.rs:68:80
24 |
25LL | /// with a [second link - valid](https://test.fake/doc_valid_link) and another [third link - invalid](https://test
26 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
27
28error: aborting due to 4 previous errors
29
src/tools/clippy/tests/ui/empty_line_after/outer_attribute.1.fixed+9
......@@ -105,4 +105,13 @@ second line
105105")]
106106pub struct Args;
107107
108mod issue_14980 {
109 //~v empty_line_after_outer_attr
110 #[repr(align(536870912))]
111 enum Aligned {
112 Zero = 0,
113 One = 1,
114 }
115}
116
108117fn main() {}
src/tools/clippy/tests/ui/empty_line_after/outer_attribute.2.fixed+9
......@@ -108,4 +108,13 @@ second line
108108")]
109109pub struct Args;
110110
111mod issue_14980 {
112 //~v empty_line_after_outer_attr
113 #[repr(align(536870912))]
114 enum Aligned {
115 Zero = 0,
116 One = 1,
117 }
118}
119
111120fn main() {}
src/tools/clippy/tests/ui/empty_line_after/outer_attribute.rs+10
......@@ -116,4 +116,14 @@ second line
116116")]
117117pub struct Args;
118118
119mod issue_14980 {
120 //~v empty_line_after_outer_attr
121 #[repr(align(536870912))]
122
123 enum Aligned {
124 Zero = 0,
125 One = 1,
126 }
127}
128
119129fn main() {}
src/tools/clippy/tests/ui/empty_line_after/outer_attribute.stderr+12-1
......@@ -111,5 +111,16 @@ LL | pub fn isolated_comment() {}
111111 |
112112 = help: if the empty lines are unintentional, remove them
113113
114error: aborting due to 9 previous errors
114error: empty line after outer attribute
115 --> tests/ui/empty_line_after/outer_attribute.rs:121:5
116 |
117LL | / #[repr(align(536870912))]
118LL | |
119 | |_^
120LL | enum Aligned {
121 | ------------ the attribute applies to this enum
122 |
123 = help: if the empty line is unintentional, remove it
124
125error: aborting due to 10 previous errors
115126
src/tools/clippy/tests/ui/eta.fixed+18
......@@ -543,3 +543,21 @@ mod issue_13073 {
543543 //~^ redundant_closure
544544 }
545545}
546
547fn issue_14789() {
548 _ = Some(1u8).map(
549 #[expect(clippy::redundant_closure)]
550 |a| foo(a),
551 );
552
553 _ = Some("foo").map(
554 #[expect(clippy::redundant_closure_for_method_calls)]
555 |s| s.to_owned(),
556 );
557
558 let _: Vec<u8> = None.map_or_else(
559 #[expect(clippy::redundant_closure)]
560 || vec![],
561 std::convert::identity,
562 );
563}
src/tools/clippy/tests/ui/eta.rs+18
......@@ -543,3 +543,21 @@ mod issue_13073 {
543543 //~^ redundant_closure
544544 }
545545}
546
547fn issue_14789() {
548 _ = Some(1u8).map(
549 #[expect(clippy::redundant_closure)]
550 |a| foo(a),
551 );
552
553 _ = Some("foo").map(
554 #[expect(clippy::redundant_closure_for_method_calls)]
555 |s| s.to_owned(),
556 );
557
558 let _: Vec<u8> = None.map_or_else(
559 #[expect(clippy::redundant_closure)]
560 || vec![],
561 std::convert::identity,
562 );
563}
src/tools/clippy/tests/ui/exhaustive_items.fixed+7
......@@ -1,3 +1,4 @@
1#![feature(default_field_values)]
12#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
23#![allow(unused)]
34
......@@ -90,3 +91,9 @@ pub mod structs {
9091 pub bar: String,
9192 }
9293}
94
95pub mod issue14992 {
96 pub struct A {
97 pub a: isize = 42,
98 }
99}
src/tools/clippy/tests/ui/exhaustive_items.rs+7
......@@ -1,3 +1,4 @@
1#![feature(default_field_values)]
12#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
23#![allow(unused)]
34
......@@ -87,3 +88,9 @@ pub mod structs {
8788 pub bar: String,
8889 }
8990}
91
92pub mod issue14992 {
93 pub struct A {
94 pub a: isize = 42,
95 }
96}
src/tools/clippy/tests/ui/exhaustive_items.stderr+5-5
......@@ -1,5 +1,5 @@
11error: exported enums should not be exhaustive
2 --> tests/ui/exhaustive_items.rs:9:5
2 --> tests/ui/exhaustive_items.rs:10:5
33 |
44LL | / pub enum Exhaustive {
55LL | |
......@@ -11,7 +11,7 @@ LL | | }
1111 | |_____^
1212 |
1313note: the lint level is defined here
14 --> tests/ui/exhaustive_items.rs:1:9
14 --> tests/ui/exhaustive_items.rs:2:9
1515 |
1616LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
1717 | ^^^^^^^^^^^^^^^^^^^^^^^^
......@@ -22,7 +22,7 @@ LL ~ pub enum Exhaustive {
2222 |
2323
2424error: exported enums should not be exhaustive
25 --> tests/ui/exhaustive_items.rs:19:5
25 --> tests/ui/exhaustive_items.rs:20:5
2626 |
2727LL | / pub enum ExhaustiveWithAttrs {
2828LL | |
......@@ -40,7 +40,7 @@ LL ~ pub enum ExhaustiveWithAttrs {
4040 |
4141
4242error: exported structs should not be exhaustive
43 --> tests/ui/exhaustive_items.rs:55:5
43 --> tests/ui/exhaustive_items.rs:56:5
4444 |
4545LL | / pub struct Exhaustive {
4646LL | |
......@@ -50,7 +50,7 @@ LL | | }
5050 | |_____^
5151 |
5252note: the lint level is defined here
53 --> tests/ui/exhaustive_items.rs:1:35
53 --> tests/ui/exhaustive_items.rs:2:35
5454 |
5555LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)]
5656 | ^^^^^^^^^^^^^^^^^^^^^^^^^^
src/tools/clippy/tests/ui/identity_op.fixed+46
......@@ -312,3 +312,49 @@ fn issue_13470() {
312312 let _: u64 = 1u64 + ((x as i32 + y as i32) as u64);
313313 //~^ identity_op
314314}
315
316fn issue_14932() {
317 let _ = 0usize + &Default::default(); // no error
318
319 0usize + &Default::default(); // no error
320
321 <usize as Default>::default();
322 //~^ identity_op
323
324 let _ = usize::default();
325 //~^ identity_op
326
327 let _n: usize = Default::default();
328 //~^ identity_op
329}
330
331// Expr's type can be inferred by the function's return type
332fn issue_14932_2() -> usize {
333 Default::default()
334 //~^ identity_op
335}
336
337trait Def {
338 fn def() -> Self;
339}
340
341impl Def for usize {
342 fn def() -> Self {
343 0
344 }
345}
346
347fn issue_14932_3() {
348 let _ = 0usize + &Def::def(); // no error
349
350 0usize + &Def::def(); // no error
351
352 <usize as Def>::def();
353 //~^ identity_op
354
355 let _ = usize::def();
356 //~^ identity_op
357
358 let _n: usize = Def::def();
359 //~^ identity_op
360}
src/tools/clippy/tests/ui/identity_op.rs+46
......@@ -312,3 +312,49 @@ fn issue_13470() {
312312 let _: u64 = 1u64 + ((x as i32 + y as i32) as u64 + 0u64);
313313 //~^ identity_op
314314}
315
316fn issue_14932() {
317 let _ = 0usize + &Default::default(); // no error
318
319 0usize + &Default::default(); // no error
320
321 0usize + &<usize as Default>::default();
322 //~^ identity_op
323
324 let _ = 0usize + &usize::default();
325 //~^ identity_op
326
327 let _n: usize = 0usize + &Default::default();
328 //~^ identity_op
329}
330
331// Expr's type can be inferred by the function's return type
332fn issue_14932_2() -> usize {
333 0usize + &Default::default()
334 //~^ identity_op
335}
336
337trait Def {
338 fn def() -> Self;
339}
340
341impl Def for usize {
342 fn def() -> Self {
343 0
344 }
345}
346
347fn issue_14932_3() {
348 let _ = 0usize + &Def::def(); // no error
349
350 0usize + &Def::def(); // no error
351
352 0usize + &<usize as Def>::def();
353 //~^ identity_op
354
355 let _ = 0usize + &usize::def();
356 //~^ identity_op
357
358 let _n: usize = 0usize + &Def::def();
359 //~^ identity_op
360}
src/tools/clippy/tests/ui/identity_op.stderr+43-1
......@@ -379,5 +379,47 @@ error: this operation has no effect
379379LL | let _: u64 = 1u64 + ((x as i32 + y as i32) as u64 + 0u64);
380380 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `((x as i32 + y as i32) as u64)`
381381
382error: aborting due to 63 previous errors
382error: this operation has no effect
383 --> tests/ui/identity_op.rs:321:5
384 |
385LL | 0usize + &<usize as Default>::default();
386 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `<usize as Default>::default()`
387
388error: this operation has no effect
389 --> tests/ui/identity_op.rs:324:13
390 |
391LL | let _ = 0usize + &usize::default();
392 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `usize::default()`
393
394error: this operation has no effect
395 --> tests/ui/identity_op.rs:327:21
396 |
397LL | let _n: usize = 0usize + &Default::default();
398 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `Default::default()`
399
400error: this operation has no effect
401 --> tests/ui/identity_op.rs:333:5
402 |
403LL | 0usize + &Default::default()
404 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `Default::default()`
405
406error: this operation has no effect
407 --> tests/ui/identity_op.rs:352:5
408 |
409LL | 0usize + &<usize as Def>::def();
410 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `<usize as Def>::def()`
411
412error: this operation has no effect
413 --> tests/ui/identity_op.rs:355:13
414 |
415LL | let _ = 0usize + &usize::def();
416 | ^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `usize::def()`
417
418error: this operation has no effect
419 --> tests/ui/identity_op.rs:358:21
420 |
421LL | let _n: usize = 0usize + &Def::def();
422 | ^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `Def::def()`
423
424error: aborting due to 70 previous errors
383425
src/tools/clippy/tests/ui/infinite_iter.rs+1-1
......@@ -38,7 +38,7 @@ fn infinite_iters() {
3838 //~^ infinite_iter
3939
4040 // infinite iter
41 (0_u64..).filter(|x| x % 2 == 0).last();
41 (0_u64..).filter(|x| x.is_multiple_of(2)).last();
4242 //~^ infinite_iter
4343
4444 // not an infinite, because ranges are double-ended
src/tools/clippy/tests/ui/infinite_iter.stderr+2-2
......@@ -42,8 +42,8 @@ LL | (0_usize..).flat_map(|x| 0..x).product::<usize>();
4242error: infinite iteration detected
4343 --> tests/ui/infinite_iter.rs:41:5
4444 |
45LL | (0_u64..).filter(|x| x % 2 == 0).last();
46 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
45LL | (0_u64..).filter(|x| x.is_multiple_of(2)).last();
46 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4747
4848error: possible infinite iteration detected
4949 --> tests/ui/infinite_iter.rs:53:5
src/tools/clippy/tests/ui/iter_kv_map.fixed+14-6
......@@ -30,15 +30,19 @@ fn main() {
3030
3131 let _ = map.clone().values().collect::<Vec<_>>();
3232 //~^ iter_kv_map
33 let _ = map.keys().filter(|x| *x % 2 == 0).count();
33 let _ = map.keys().filter(|x| x.is_multiple_of(2)).count();
3434 //~^ iter_kv_map
3535
3636 // Don't lint
37 let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count();
37 let _ = map
38 .iter()
39 .filter(|(_, val)| val.is_multiple_of(2))
40 .map(|(key, _)| key)
41 .count();
3842 let _ = map.iter().map(get_key).collect::<Vec<_>>();
3943
4044 // Linting the following could be an improvement to the lint
41 // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count();
45 // map.iter().filter_map(|(_, val)| (val.is_multiple_of(2)).then(val * 17)).count();
4246
4347 // Lint
4448 let _ = map.keys().map(|key| key * 9).count();
......@@ -84,15 +88,19 @@ fn main() {
8488
8589 let _ = map.clone().values().collect::<Vec<_>>();
8690 //~^ iter_kv_map
87 let _ = map.keys().filter(|x| *x % 2 == 0).count();
91 let _ = map.keys().filter(|x| x.is_multiple_of(2)).count();
8892 //~^ iter_kv_map
8993
9094 // Don't lint
91 let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count();
95 let _ = map
96 .iter()
97 .filter(|(_, val)| val.is_multiple_of(2))
98 .map(|(key, _)| key)
99 .count();
92100 let _ = map.iter().map(get_key).collect::<Vec<_>>();
93101
94102 // Linting the following could be an improvement to the lint
95 // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count();
103 // map.iter().filter_map(|(_, val)| (val.is_multiple_of(2)).then(val * 17)).count();
96104
97105 // Lint
98106 let _ = map.keys().map(|key| key * 9).count();
src/tools/clippy/tests/ui/iter_kv_map.rs+14-6
......@@ -30,15 +30,19 @@ fn main() {
3030
3131 let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
3232 //~^ iter_kv_map
33 let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
33 let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count();
3434 //~^ iter_kv_map
3535
3636 // Don't lint
37 let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count();
37 let _ = map
38 .iter()
39 .filter(|(_, val)| val.is_multiple_of(2))
40 .map(|(key, _)| key)
41 .count();
3842 let _ = map.iter().map(get_key).collect::<Vec<_>>();
3943
4044 // Linting the following could be an improvement to the lint
41 // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count();
45 // map.iter().filter_map(|(_, val)| (val.is_multiple_of(2)).then(val * 17)).count();
4246
4347 // Lint
4448 let _ = map.iter().map(|(key, _value)| key * 9).count();
......@@ -86,15 +90,19 @@ fn main() {
8690
8791 let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
8892 //~^ iter_kv_map
89 let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
93 let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count();
9094 //~^ iter_kv_map
9195
9296 // Don't lint
93 let _ = map.iter().filter(|(_, val)| *val % 2 == 0).map(|(key, _)| key).count();
97 let _ = map
98 .iter()
99 .filter(|(_, val)| val.is_multiple_of(2))
100 .map(|(key, _)| key)
101 .count();
94102 let _ = map.iter().map(get_key).collect::<Vec<_>>();
95103
96104 // Linting the following could be an improvement to the lint
97 // map.iter().filter_map(|(_, val)| (val % 2 == 0).then(val * 17)).count();
105 // map.iter().filter_map(|(_, val)| (val.is_multiple_of(2)).then(val * 17)).count();
98106
99107 // Lint
100108 let _ = map.iter().map(|(key, _value)| key * 9).count();
src/tools/clippy/tests/ui/iter_kv_map.stderr+32-32
......@@ -52,29 +52,29 @@ LL | let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
5252error: iterating on a map's keys
5353 --> tests/ui/iter_kv_map.rs:33:13
5454 |
55LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
55LL | let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count();
5656 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
5757
5858error: iterating on a map's keys
59 --> tests/ui/iter_kv_map.rs:44:13
59 --> tests/ui/iter_kv_map.rs:48:13
6060 |
6161LL | let _ = map.iter().map(|(key, _value)| key * 9).count();
6262 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)`
6363
6464error: iterating on a map's values
65 --> tests/ui/iter_kv_map.rs:46:13
65 --> tests/ui/iter_kv_map.rs:50:13
6666 |
6767LL | let _ = map.iter().map(|(_key, value)| value * 17).count();
6868 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)`
6969
7070error: iterating on a map's values
71 --> tests/ui/iter_kv_map.rs:50:13
71 --> tests/ui/iter_kv_map.rs:54:13
7272 |
7373LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count();
7474 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))`
7575
7676error: iterating on a map's values
77 --> tests/ui/iter_kv_map.rs:54:13
77 --> tests/ui/iter_kv_map.rs:58:13
7878 |
7979LL | let _ = map
8080 | _____________^
......@@ -97,85 +97,85 @@ LL + })
9797 |
9898
9999error: iterating on a map's values
100 --> tests/ui/iter_kv_map.rs:65:13
100 --> tests/ui/iter_kv_map.rs:69:13
101101 |
102102LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count();
103103 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
104104
105105error: iterating on a map's keys
106 --> tests/ui/iter_kv_map.rs:70:13
106 --> tests/ui/iter_kv_map.rs:74:13
107107 |
108108LL | let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
109109 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
110110
111111error: iterating on a map's values
112 --> tests/ui/iter_kv_map.rs:72:13
112 --> tests/ui/iter_kv_map.rs:76:13
113113 |
114114LL | let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
115115 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()`
116116
117117error: iterating on a map's values
118 --> tests/ui/iter_kv_map.rs:74:13
118 --> tests/ui/iter_kv_map.rs:78:13
119119 |
120120LL | let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
121121 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)`
122122
123123error: iterating on a map's keys
124 --> tests/ui/iter_kv_map.rs:77:13
124 --> tests/ui/iter_kv_map.rs:81:13
125125 |
126126LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::<Vec<_>>();
127127 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()`
128128
129129error: iterating on a map's keys
130 --> tests/ui/iter_kv_map.rs:79:13
130 --> tests/ui/iter_kv_map.rs:83:13
131131 |
132132LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::<Vec<_>>();
133133 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)`
134134
135135error: iterating on a map's values
136 --> tests/ui/iter_kv_map.rs:82:13
136 --> tests/ui/iter_kv_map.rs:86:13
137137 |
138138LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::<Vec<_>>();
139139 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
140140
141141error: iterating on a map's values
142 --> tests/ui/iter_kv_map.rs:84:13
142 --> tests/ui/iter_kv_map.rs:88:13
143143 |
144144LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::<Vec<_>>();
145145 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)`
146146
147147error: iterating on a map's values
148 --> tests/ui/iter_kv_map.rs:87:13
148 --> tests/ui/iter_kv_map.rs:91:13
149149 |
150150LL | let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
151151 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()`
152152
153153error: iterating on a map's keys
154 --> tests/ui/iter_kv_map.rs:89:13
154 --> tests/ui/iter_kv_map.rs:93:13
155155 |
156LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
156LL | let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count();
157157 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
158158
159159error: iterating on a map's keys
160 --> tests/ui/iter_kv_map.rs:100:13
160 --> tests/ui/iter_kv_map.rs:108:13
161161 |
162162LL | let _ = map.iter().map(|(key, _value)| key * 9).count();
163163 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)`
164164
165165error: iterating on a map's values
166 --> tests/ui/iter_kv_map.rs:102:13
166 --> tests/ui/iter_kv_map.rs:110:13
167167 |
168168LL | let _ = map.iter().map(|(_key, value)| value * 17).count();
169169 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)`
170170
171171error: iterating on a map's values
172 --> tests/ui/iter_kv_map.rs:106:13
172 --> tests/ui/iter_kv_map.rs:114:13
173173 |
174174LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count();
175175 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))`
176176
177177error: iterating on a map's values
178 --> tests/ui/iter_kv_map.rs:110:13
178 --> tests/ui/iter_kv_map.rs:118:13
179179 |
180180LL | let _ = map
181181 | _____________^
......@@ -198,73 +198,73 @@ LL + })
198198 |
199199
200200error: iterating on a map's values
201 --> tests/ui/iter_kv_map.rs:121:13
201 --> tests/ui/iter_kv_map.rs:129:13
202202 |
203203LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count();
204204 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
205205
206206error: iterating on a map's keys
207 --> tests/ui/iter_kv_map.rs:137:13
207 --> tests/ui/iter_kv_map.rs:145:13
208208 |
209209LL | let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
210210 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
211211
212212error: iterating on a map's values
213 --> tests/ui/iter_kv_map.rs:140:13
213 --> tests/ui/iter_kv_map.rs:148:13
214214 |
215215LL | let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
216216 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()`
217217
218218error: iterating on a map's values
219 --> tests/ui/iter_kv_map.rs:143:13
219 --> tests/ui/iter_kv_map.rs:151:13
220220 |
221221LL | let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
222222 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)`
223223
224224error: iterating on a map's keys
225 --> tests/ui/iter_kv_map.rs:152:13
225 --> tests/ui/iter_kv_map.rs:160:13
226226 |
227227LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::<Vec<_>>();
228228 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()`
229229
230230error: iterating on a map's keys
231 --> tests/ui/iter_kv_map.rs:155:13
231 --> tests/ui/iter_kv_map.rs:163:13
232232 |
233233LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::<Vec<_>>();
234234 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)`
235235
236236error: iterating on a map's values
237 --> tests/ui/iter_kv_map.rs:158:13
237 --> tests/ui/iter_kv_map.rs:166:13
238238 |
239239LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::<Vec<_>>();
240240 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
241241
242242error: iterating on a map's values
243 --> tests/ui/iter_kv_map.rs:161:13
243 --> tests/ui/iter_kv_map.rs:169:13
244244 |
245245LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::<Vec<_>>();
246246 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)`
247247
248248error: iterating on a map's keys
249 --> tests/ui/iter_kv_map.rs:164:13
249 --> tests/ui/iter_kv_map.rs:172:13
250250 |
251251LL | let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
252252 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
253253
254254error: iterating on a map's values
255 --> tests/ui/iter_kv_map.rs:167:13
255 --> tests/ui/iter_kv_map.rs:175:13
256256 |
257257LL | let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
258258 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()`
259259
260260error: iterating on a map's values
261 --> tests/ui/iter_kv_map.rs:170:13
261 --> tests/ui/iter_kv_map.rs:178:13
262262 |
263263LL | let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
264264 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)`
265265
266266error: iterating on a map's values
267 --> tests/ui/iter_kv_map.rs:185:13
267 --> tests/ui/iter_kv_map.rs:193:13
268268 |
269269LL | let _ = map.as_ref().iter().map(|(_, v)| v).copied().collect::<Vec<_>>();
270270 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.as_ref().values()`
src/tools/clippy/tests/ui/let_unit.fixed+1-1
......@@ -61,7 +61,7 @@ fn multiline_sugg() {
6161 //~^ let_unit_value
6262 .into_iter()
6363 .map(|i| i * 2)
64 .filter(|i| i % 2 == 0)
64 .filter(|i| i.is_multiple_of(2))
6565 .map(|_| ())
6666 .next()
6767 .unwrap();
src/tools/clippy/tests/ui/let_unit.rs+1-1
......@@ -61,7 +61,7 @@ fn multiline_sugg() {
6161 //~^ let_unit_value
6262 .into_iter()
6363 .map(|i| i * 2)
64 .filter(|i| i % 2 == 0)
64 .filter(|i| i.is_multiple_of(2))
6565 .map(|_| ())
6666 .next()
6767 .unwrap();
src/tools/clippy/tests/ui/let_unit.stderr+1-1
......@@ -25,7 +25,7 @@ LL ~ v
2525LL +
2626LL + .into_iter()
2727LL + .map(|i| i * 2)
28LL + .filter(|i| i % 2 == 0)
28LL + .filter(|i| i.is_multiple_of(2))
2929LL + .map(|_| ())
3030LL + .next()
3131LL + .unwrap();
src/tools/clippy/tests/ui/manual_contains.fixed+1-1
......@@ -58,7 +58,7 @@ fn should_not_lint() {
5858
5959 let vec: Vec<u32> = vec![1, 2, 3, 4, 5, 6];
6060 let values = &vec[..];
61 let _ = values.iter().any(|&v| v % 2 == 0);
61 let _ = values.iter().any(|&v| v.is_multiple_of(2));
6262 let _ = values.iter().any(|&v| v * 2 == 6);
6363 let _ = values.iter().any(|&v| v == v);
6464 let _ = values.iter().any(|&v| 4 == 4);
src/tools/clippy/tests/ui/manual_contains.rs+1-1
......@@ -58,7 +58,7 @@ fn should_not_lint() {
5858
5959 let vec: Vec<u32> = vec![1, 2, 3, 4, 5, 6];
6060 let values = &vec[..];
61 let _ = values.iter().any(|&v| v % 2 == 0);
61 let _ = values.iter().any(|&v| v.is_multiple_of(2));
6262 let _ = values.iter().any(|&v| v * 2 == 6);
6363 let _ = values.iter().any(|&v| v == v);
6464 let _ = values.iter().any(|&v| 4 == 4);
src/tools/clippy/tests/ui/manual_find_fixable.fixed+2-2
......@@ -11,7 +11,7 @@ fn lookup(n: u32) -> Option<u32> {
1111}
1212
1313fn with_pat(arr: Vec<(u32, u32)>) -> Option<u32> {
14 arr.into_iter().map(|(a, _)| a).find(|&a| a % 2 == 0)
14 arr.into_iter().map(|(a, _)| a).find(|&a| a.is_multiple_of(2))
1515}
1616
1717struct Data {
......@@ -63,7 +63,7 @@ fn with_side_effects(arr: Vec<u32>) -> Option<u32> {
6363
6464fn with_else(arr: Vec<u32>) -> Option<u32> {
6565 for el in arr {
66 if el % 2 == 0 {
66 if el.is_multiple_of(2) {
6767 return Some(el);
6868 } else {
6969 println!("{}", el);
src/tools/clippy/tests/ui/manual_find_fixable.rs+2-2
......@@ -19,7 +19,7 @@ fn lookup(n: u32) -> Option<u32> {
1919fn with_pat(arr: Vec<(u32, u32)>) -> Option<u32> {
2020 for (a, _) in arr {
2121 //~^ manual_find
22 if a % 2 == 0 {
22 if a.is_multiple_of(2) {
2323 return Some(a);
2424 }
2525 }
......@@ -111,7 +111,7 @@ fn with_side_effects(arr: Vec<u32>) -> Option<u32> {
111111
112112fn with_else(arr: Vec<u32>) -> Option<u32> {
113113 for el in arr {
114 if el % 2 == 0 {
114 if el.is_multiple_of(2) {
115115 return Some(el);
116116 } else {
117117 println!("{}", el);
src/tools/clippy/tests/ui/manual_find_fixable.stderr+2-2
......@@ -17,11 +17,11 @@ error: manual implementation of `Iterator::find`
1717 |
1818LL | / for (a, _) in arr {
1919LL | |
20LL | | if a % 2 == 0 {
20LL | | if a.is_multiple_of(2) {
2121LL | | return Some(a);
2222... |
2323LL | | None
24 | |________^ help: replace with an iterator: `arr.into_iter().map(|(a, _)| a).find(|&a| a % 2 == 0)`
24 | |________^ help: replace with an iterator: `arr.into_iter().map(|(a, _)| a).find(|&a| a.is_multiple_of(2))`
2525
2626error: manual implementation of `Iterator::find`
2727 --> tests/ui/manual_find_fixable.rs:34:5
src/tools/clippy/tests/ui/manual_is_multiple_of.fixed created+25
......@@ -0,0 +1,25 @@
1//@aux-build: proc_macros.rs
2#![warn(clippy::manual_is_multiple_of)]
3
4fn main() {}
5
6#[clippy::msrv = "1.87"]
7fn f(a: u64, b: u64) {
8 let _ = a.is_multiple_of(b); //~ manual_is_multiple_of
9 let _ = (a + 1).is_multiple_of(b + 1); //~ manual_is_multiple_of
10 let _ = !a.is_multiple_of(b); //~ manual_is_multiple_of
11 let _ = !(a + 1).is_multiple_of(b + 1); //~ manual_is_multiple_of
12
13 let _ = !a.is_multiple_of(b); //~ manual_is_multiple_of
14 let _ = !a.is_multiple_of(b); //~ manual_is_multiple_of
15
16 proc_macros::external! {
17 let a: u64 = 23424;
18 let _ = a % 4096 == 0;
19 }
20}
21
22#[clippy::msrv = "1.86"]
23fn g(a: u64, b: u64) {
24 let _ = a % b == 0;
25}
src/tools/clippy/tests/ui/manual_is_multiple_of.rs created+25
......@@ -0,0 +1,25 @@
1//@aux-build: proc_macros.rs
2#![warn(clippy::manual_is_multiple_of)]
3
4fn main() {}
5
6#[clippy::msrv = "1.87"]
7fn f(a: u64, b: u64) {
8 let _ = a % b == 0; //~ manual_is_multiple_of
9 let _ = (a + 1) % (b + 1) == 0; //~ manual_is_multiple_of
10 let _ = a % b != 0; //~ manual_is_multiple_of
11 let _ = (a + 1) % (b + 1) != 0; //~ manual_is_multiple_of
12
13 let _ = a % b > 0; //~ manual_is_multiple_of
14 let _ = 0 < a % b; //~ manual_is_multiple_of
15
16 proc_macros::external! {
17 let a: u64 = 23424;
18 let _ = a % 4096 == 0;
19 }
20}
21
22#[clippy::msrv = "1.86"]
23fn g(a: u64, b: u64) {
24 let _ = a % b == 0;
25}
src/tools/clippy/tests/ui/manual_is_multiple_of.stderr created+41
......@@ -0,0 +1,41 @@
1error: manual implementation of `.is_multiple_of()`
2 --> tests/ui/manual_is_multiple_of.rs:8:13
3 |
4LL | let _ = a % b == 0;
5 | ^^^^^^^^^^ help: replace with: `a.is_multiple_of(b)`
6 |
7 = note: `-D clippy::manual-is-multiple-of` implied by `-D warnings`
8 = help: to override `-D warnings` add `#[allow(clippy::manual_is_multiple_of)]`
9
10error: manual implementation of `.is_multiple_of()`
11 --> tests/ui/manual_is_multiple_of.rs:9:13
12 |
13LL | let _ = (a + 1) % (b + 1) == 0;
14 | ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `(a + 1).is_multiple_of(b + 1)`
15
16error: manual implementation of `.is_multiple_of()`
17 --> tests/ui/manual_is_multiple_of.rs:10:13
18 |
19LL | let _ = a % b != 0;
20 | ^^^^^^^^^^ help: replace with: `!a.is_multiple_of(b)`
21
22error: manual implementation of `.is_multiple_of()`
23 --> tests/ui/manual_is_multiple_of.rs:11:13
24 |
25LL | let _ = (a + 1) % (b + 1) != 0;
26 | ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `!(a + 1).is_multiple_of(b + 1)`
27
28error: manual implementation of `.is_multiple_of()`
29 --> tests/ui/manual_is_multiple_of.rs:13:13
30 |
31LL | let _ = a % b > 0;
32 | ^^^^^^^^^ help: replace with: `!a.is_multiple_of(b)`
33
34error: manual implementation of `.is_multiple_of()`
35 --> tests/ui/manual_is_multiple_of.rs:14:13
36 |
37LL | let _ = 0 < a % b;
38 | ^^^^^^^^^ help: replace with: `!a.is_multiple_of(b)`
39
40error: aborting due to 6 previous errors
41
src/tools/clippy/tests/ui/manual_is_variant_and.fixed+4-4
......@@ -77,7 +77,7 @@ fn option_methods() {
7777 let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint
7878
7979 // Should not lint.
80 let _ = Foo::<u32>(0).map(|x| x % 2 == 0) == Some(true);
80 let _ = Foo::<u32>(0).map(|x| x.is_multiple_of(2)) == Some(true);
8181 let _ = Some(2).map(|x| x % 2 == 0) != foo();
8282 let _ = mac!(eq Some(2).map(|x| x % 2 == 0), Some(true));
8383 let _ = mac!(some 2).map(|x| x % 2 == 0) == Some(true);
......@@ -96,11 +96,11 @@ fn result_methods() {
9696 });
9797 let _ = res.is_ok_and(|x| x > 1);
9898
99 let _ = Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0);
99 let _ = Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2));
100100 //~^ manual_is_variant_and
101 let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0);
101 let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2));
102102 //~^ manual_is_variant_and
103 let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0);
103 let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2));
104104 //~^ manual_is_variant_and
105105
106106 // won't fix because the return type of the closure is not `bool`
src/tools/clippy/tests/ui/manual_is_variant_and.rs+4-4
......@@ -83,7 +83,7 @@ fn option_methods() {
8383 let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint
8484
8585 // Should not lint.
86 let _ = Foo::<u32>(0).map(|x| x % 2 == 0) == Some(true);
86 let _ = Foo::<u32>(0).map(|x| x.is_multiple_of(2)) == Some(true);
8787 let _ = Some(2).map(|x| x % 2 == 0) != foo();
8888 let _ = mac!(eq Some(2).map(|x| x % 2 == 0), Some(true));
8989 let _ = mac!(some 2).map(|x| x % 2 == 0) == Some(true);
......@@ -105,11 +105,11 @@ fn result_methods() {
105105 //~^ manual_is_variant_and
106106 .unwrap_or_default();
107107
108 let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) == Ok(true);
108 let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) == Ok(true);
109109 //~^ manual_is_variant_and
110 let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
110 let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) != Ok(true);
111111 //~^ manual_is_variant_and
112 let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
112 let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) != Ok(true);
113113 //~^ manual_is_variant_and
114114
115115 // won't fix because the return type of the closure is not `bool`
src/tools/clippy/tests/ui/manual_is_variant_and.stderr+6-6
......@@ -105,20 +105,20 @@ LL | | .unwrap_or_default();
105105error: called `.map() == Ok()`
106106 --> tests/ui/manual_is_variant_and.rs:108:13
107107 |
108LL | let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) == Ok(true);
109 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)`
108LL | let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) == Ok(true);
109 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2))`
110110
111111error: called `.map() != Ok()`
112112 --> tests/ui/manual_is_variant_and.rs:110:13
113113 |
114LL | let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
115 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)`
114LL | let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) != Ok(true);
115 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2))`
116116
117117error: called `.map() != Ok()`
118118 --> tests/ui/manual_is_variant_and.rs:112:13
119119 |
120LL | let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true);
121 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)`
120LL | let _ = Ok::<usize, ()>(2).map(|x| x.is_multiple_of(2)) != Ok(true);
121 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x.is_multiple_of(2))`
122122
123123error: called `map(<f>).unwrap_or_default()` on a `Result` value
124124 --> tests/ui/manual_is_variant_and.rs:119:18
src/tools/clippy/tests/ui/manual_ok_err.fixed+24
......@@ -103,3 +103,27 @@ fn issue14239() {
103103 };
104104 //~^^^^^ manual_ok_err
105105}
106
107mod issue15051 {
108 struct Container {
109 field: Result<bool, ()>,
110 }
111
112 #[allow(clippy::needless_borrow)]
113 fn with_addr_of(x: &Container) -> Option<&bool> {
114 (&x.field).as_ref().ok()
115 }
116
117 fn from_fn(x: &Container) -> Option<&bool> {
118 let result_with_ref = || &x.field;
119 result_with_ref().as_ref().ok()
120 }
121
122 fn result_with_ref_mut(x: &mut Container) -> &mut Result<bool, ()> {
123 &mut x.field
124 }
125
126 fn from_fn_mut(x: &mut Container) -> Option<&mut bool> {
127 result_with_ref_mut(x).as_mut().ok()
128 }
129}
src/tools/clippy/tests/ui/manual_ok_err.rs+36
......@@ -141,3 +141,39 @@ fn issue14239() {
141141 };
142142 //~^^^^^ manual_ok_err
143143}
144
145mod issue15051 {
146 struct Container {
147 field: Result<bool, ()>,
148 }
149
150 #[allow(clippy::needless_borrow)]
151 fn with_addr_of(x: &Container) -> Option<&bool> {
152 match &x.field {
153 //~^ manual_ok_err
154 Ok(panel) => Some(panel),
155 Err(_) => None,
156 }
157 }
158
159 fn from_fn(x: &Container) -> Option<&bool> {
160 let result_with_ref = || &x.field;
161 match result_with_ref() {
162 //~^ manual_ok_err
163 Ok(panel) => Some(panel),
164 Err(_) => None,
165 }
166 }
167
168 fn result_with_ref_mut(x: &mut Container) -> &mut Result<bool, ()> {
169 &mut x.field
170 }
171
172 fn from_fn_mut(x: &mut Container) -> Option<&mut bool> {
173 match result_with_ref_mut(x) {
174 //~^ manual_ok_err
175 Ok(panel) => Some(panel),
176 Err(_) => None,
177 }
178 }
179}
src/tools/clippy/tests/ui/manual_ok_err.stderr+31-1
......@@ -111,5 +111,35 @@ LL + "1".parse::<u8>().ok()
111111LL ~ };
112112 |
113113
114error: aborting due to 9 previous errors
114error: manual implementation of `ok`
115 --> tests/ui/manual_ok_err.rs:152:9
116 |
117LL | / match &x.field {
118LL | |
119LL | | Ok(panel) => Some(panel),
120LL | | Err(_) => None,
121LL | | }
122 | |_________^ help: replace with: `(&x.field).as_ref().ok()`
123
124error: manual implementation of `ok`
125 --> tests/ui/manual_ok_err.rs:161:9
126 |
127LL | / match result_with_ref() {
128LL | |
129LL | | Ok(panel) => Some(panel),
130LL | | Err(_) => None,
131LL | | }
132 | |_________^ help: replace with: `result_with_ref().as_ref().ok()`
133
134error: manual implementation of `ok`
135 --> tests/ui/manual_ok_err.rs:173:9
136 |
137LL | / match result_with_ref_mut(x) {
138LL | |
139LL | | Ok(panel) => Some(panel),
140LL | | Err(_) => None,
141LL | | }
142 | |_________^ help: replace with: `result_with_ref_mut(x).as_mut().ok()`
143
144error: aborting due to 12 previous errors
115145
src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.fixed+1-1
......@@ -25,7 +25,7 @@ const fn can_be_const() {
2525 0u64.method();
2626}
2727
28// False negative, see FIXME comment in `clipy_utils::qualify_min_const`
28// False negative, see FIXME comment in `clippy_utils::qualify_min_const_fn`
2929fn could_be_const_but_does_not_trigger<T>(t: T)
3030where
3131 T: const ConstTrait,
src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.rs+1-1
......@@ -25,7 +25,7 @@ fn can_be_const() {
2525 0u64.method();
2626}
2727
28// False negative, see FIXME comment in `clipy_utils::qualify_min_const`
28// False negative, see FIXME comment in `clippy_utils::qualify_min_const_fn`
2929fn could_be_const_but_does_not_trigger<T>(t: T)
3030where
3131 T: const ConstTrait,
src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed+57
......@@ -221,3 +221,60 @@ const fn mut_add(x: &mut i32) {
221221 //~^ missing_const_for_fn
222222 *x += 1;
223223}
224
225mod issue_15079 {
226 pub trait Trait {}
227
228 pub struct Struct<T: Trait> {
229 _t: Option<T>,
230 }
231
232 impl<T: Trait> Struct<T> {
233 #[clippy::msrv = "1.60"]
234 pub fn new_1_60() -> Self {
235 Self { _t: None }
236 }
237
238 #[clippy::msrv = "1.61"]
239 pub const fn new_1_61() -> Self {
240 //~^ missing_const_for_fn
241 Self { _t: None }
242 }
243 }
244
245 pub struct S2<T> {
246 _t: Option<T>,
247 }
248
249 impl<T> S2<T> {
250 #[clippy::msrv = "1.60"]
251 pub const fn new_1_60() -> Self {
252 //~^ missing_const_for_fn
253 Self { _t: None }
254 }
255
256 #[clippy::msrv = "1.61"]
257 pub const fn new_1_61() -> Self {
258 //~^ missing_const_for_fn
259 Self { _t: None }
260 }
261 }
262
263 pub struct S3<T: ?Sized + 'static> {
264 _t: Option<&'static T>,
265 }
266
267 impl<T: ?Sized + 'static> S3<T> {
268 #[clippy::msrv = "1.60"]
269 pub const fn new_1_60() -> Self {
270 //~^ missing_const_for_fn
271 Self { _t: None }
272 }
273
274 #[clippy::msrv = "1.61"]
275 pub const fn new_1_61() -> Self {
276 //~^ missing_const_for_fn
277 Self { _t: None }
278 }
279 }
280}
src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs+57
......@@ -221,3 +221,60 @@ fn mut_add(x: &mut i32) {
221221 //~^ missing_const_for_fn
222222 *x += 1;
223223}
224
225mod issue_15079 {
226 pub trait Trait {}
227
228 pub struct Struct<T: Trait> {
229 _t: Option<T>,
230 }
231
232 impl<T: Trait> Struct<T> {
233 #[clippy::msrv = "1.60"]
234 pub fn new_1_60() -> Self {
235 Self { _t: None }
236 }
237
238 #[clippy::msrv = "1.61"]
239 pub fn new_1_61() -> Self {
240 //~^ missing_const_for_fn
241 Self { _t: None }
242 }
243 }
244
245 pub struct S2<T> {
246 _t: Option<T>,
247 }
248
249 impl<T> S2<T> {
250 #[clippy::msrv = "1.60"]
251 pub fn new_1_60() -> Self {
252 //~^ missing_const_for_fn
253 Self { _t: None }
254 }
255
256 #[clippy::msrv = "1.61"]
257 pub fn new_1_61() -> Self {
258 //~^ missing_const_for_fn
259 Self { _t: None }
260 }
261 }
262
263 pub struct S3<T: ?Sized + 'static> {
264 _t: Option<&'static T>,
265 }
266
267 impl<T: ?Sized + 'static> S3<T> {
268 #[clippy::msrv = "1.60"]
269 pub fn new_1_60() -> Self {
270 //~^ missing_const_for_fn
271 Self { _t: None }
272 }
273
274 #[clippy::msrv = "1.61"]
275 pub fn new_1_61() -> Self {
276 //~^ missing_const_for_fn
277 Self { _t: None }
278 }
279 }
280}
src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr+71-1
......@@ -332,5 +332,75 @@ help: make the function `const`
332332LL | const fn mut_add(x: &mut i32) {
333333 | +++++
334334
335error: aborting due to 25 previous errors
335error: this could be a `const fn`
336 --> tests/ui/missing_const_for_fn/could_be_const.rs:239:9
337 |
338LL | / pub fn new_1_61() -> Self {
339LL | |
340LL | | Self { _t: None }
341LL | | }
342 | |_________^
343 |
344help: make the function `const`
345 |
346LL | pub const fn new_1_61() -> Self {
347 | +++++
348
349error: this could be a `const fn`
350 --> tests/ui/missing_const_for_fn/could_be_const.rs:251:9
351 |
352LL | / pub fn new_1_60() -> Self {
353LL | |
354LL | | Self { _t: None }
355LL | | }
356 | |_________^
357 |
358help: make the function `const`
359 |
360LL | pub const fn new_1_60() -> Self {
361 | +++++
362
363error: this could be a `const fn`
364 --> tests/ui/missing_const_for_fn/could_be_const.rs:257:9
365 |
366LL | / pub fn new_1_61() -> Self {
367LL | |
368LL | | Self { _t: None }
369LL | | }
370 | |_________^
371 |
372help: make the function `const`
373 |
374LL | pub const fn new_1_61() -> Self {
375 | +++++
376
377error: this could be a `const fn`
378 --> tests/ui/missing_const_for_fn/could_be_const.rs:269:9
379 |
380LL | / pub fn new_1_60() -> Self {
381LL | |
382LL | | Self { _t: None }
383LL | | }
384 | |_________^
385 |
386help: make the function `const`
387 |
388LL | pub const fn new_1_60() -> Self {
389 | +++++
390
391error: this could be a `const fn`
392 --> tests/ui/missing_const_for_fn/could_be_const.rs:275:9
393 |
394LL | / pub fn new_1_61() -> Self {
395LL | |
396LL | | Self { _t: None }
397LL | | }
398 | |_________^
399 |
400help: make the function `const`
401 |
402LL | pub const fn new_1_61() -> Self {
403 | +++++
404
405error: aborting due to 30 previous errors
336406
src/tools/clippy/tests/ui/nonminimal_bool.stderr+2-2
......@@ -179,7 +179,7 @@ error: inequality checks against true can be replaced by a negation
179179 --> tests/ui/nonminimal_bool.rs:186:8
180180 |
181181LL | if !b != true {}
182 | ^^^^^^^^^^ help: try simplifying it as shown: `!(!b)`
182 | ^^^^^^^^^^ help: try simplifying it as shown: `!!b`
183183
184184error: this boolean expression can be simplified
185185 --> tests/ui/nonminimal_bool.rs:189:8
......@@ -209,7 +209,7 @@ error: inequality checks against true can be replaced by a negation
209209 --> tests/ui/nonminimal_bool.rs:193:8
210210 |
211211LL | if true != !b {}
212 | ^^^^^^^^^^ help: try simplifying it as shown: `!(!b)`
212 | ^^^^^^^^^^ help: try simplifying it as shown: `!!b`
213213
214214error: this boolean expression can be simplified
215215 --> tests/ui/nonminimal_bool.rs:196:8
src/tools/clippy/tests/ui/or_fun_call.fixed+30
......@@ -5,6 +5,7 @@
55 clippy::uninlined_format_args,
66 clippy::unnecessary_wraps,
77 clippy::unnecessary_literal_unwrap,
8 clippy::unnecessary_result_map_or_else,
89 clippy::useless_vec
910)]
1011
......@@ -409,4 +410,33 @@ fn fn_call_in_nested_expr() {
409410 //~^ or_fun_call
410411}
411412
413mod result_map_or {
414 fn g() -> i32 {
415 3
416 }
417
418 fn f(n: i32) -> i32 {
419 n
420 }
421
422 fn test_map_or() {
423 let x: Result<i32, ()> = Ok(4);
424 let _ = x.map_or_else(|_| g(), |v| v);
425 //~^ or_fun_call
426 let _ = x.map_or_else(|_| g(), f);
427 //~^ or_fun_call
428 let _ = x.map_or(0, f);
429 }
430}
431
432fn test_option_get_or_insert() {
433 // assume that this is slow call
434 fn g() -> u8 {
435 99
436 }
437 let mut x = Some(42_u8);
438 let _ = x.get_or_insert_with(g);
439 //~^ or_fun_call
440}
441
412442fn main() {}
src/tools/clippy/tests/ui/or_fun_call.rs+30
......@@ -5,6 +5,7 @@
55 clippy::uninlined_format_args,
66 clippy::unnecessary_wraps,
77 clippy::unnecessary_literal_unwrap,
8 clippy::unnecessary_result_map_or_else,
89 clippy::useless_vec
910)]
1011
......@@ -409,4 +410,33 @@ fn fn_call_in_nested_expr() {
409410 //~^ or_fun_call
410411}
411412
413mod result_map_or {
414 fn g() -> i32 {
415 3
416 }
417
418 fn f(n: i32) -> i32 {
419 n
420 }
421
422 fn test_map_or() {
423 let x: Result<i32, ()> = Ok(4);
424 let _ = x.map_or(g(), |v| v);
425 //~^ or_fun_call
426 let _ = x.map_or(g(), f);
427 //~^ or_fun_call
428 let _ = x.map_or(0, f);
429 }
430}
431
432fn test_option_get_or_insert() {
433 // assume that this is slow call
434 fn g() -> u8 {
435 99
436 }
437 let mut x = Some(42_u8);
438 let _ = x.get_or_insert(g());
439 //~^ or_fun_call
440}
441
412442fn main() {}
src/tools/clippy/tests/ui/or_fun_call.stderr+57-39
......@@ -1,5 +1,5 @@
11error: function call inside of `unwrap_or`
2 --> tests/ui/or_fun_call.rs:52:22
2 --> tests/ui/or_fun_call.rs:53:22
33 |
44LL | with_constructor.unwrap_or(make());
55 | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(make)`
......@@ -8,7 +8,7 @@ LL | with_constructor.unwrap_or(make());
88 = help: to override `-D warnings` add `#[allow(clippy::or_fun_call)]`
99
1010error: use of `unwrap_or` to construct default value
11 --> tests/ui/or_fun_call.rs:56:14
11 --> tests/ui/or_fun_call.rs:57:14
1212 |
1313LL | with_new.unwrap_or(Vec::new());
1414 | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
......@@ -17,199 +17,199 @@ LL | with_new.unwrap_or(Vec::new());
1717 = help: to override `-D warnings` add `#[allow(clippy::unwrap_or_default)]`
1818
1919error: function call inside of `unwrap_or`
20 --> tests/ui/or_fun_call.rs:60:21
20 --> tests/ui/or_fun_call.rs:61:21
2121 |
2222LL | with_const_args.unwrap_or(Vec::with_capacity(12));
2323 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Vec::with_capacity(12))`
2424
2525error: function call inside of `unwrap_or`
26 --> tests/ui/or_fun_call.rs:64:14
26 --> tests/ui/or_fun_call.rs:65:14
2727 |
2828LL | with_err.unwrap_or(make());
2929 | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| make())`
3030
3131error: function call inside of `unwrap_or`
32 --> tests/ui/or_fun_call.rs:68:19
32 --> tests/ui/or_fun_call.rs:69:19
3333 |
3434LL | with_err_args.unwrap_or(Vec::with_capacity(12));
3535 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| Vec::with_capacity(12))`
3636
3737error: use of `unwrap_or` to construct default value
38 --> tests/ui/or_fun_call.rs:72:24
38 --> tests/ui/or_fun_call.rs:73:24
3939 |
4040LL | with_default_trait.unwrap_or(Default::default());
4141 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
4242
4343error: use of `unwrap_or` to construct default value
44 --> tests/ui/or_fun_call.rs:76:23
44 --> tests/ui/or_fun_call.rs:77:23
4545 |
4646LL | with_default_type.unwrap_or(u64::default());
4747 | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
4848
4949error: function call inside of `unwrap_or`
50 --> tests/ui/or_fun_call.rs:80:18
50 --> tests/ui/or_fun_call.rs:81:18
5151 |
5252LL | self_default.unwrap_or(<FakeDefault>::default());
5353 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(<FakeDefault>::default)`
5454
5555error: use of `unwrap_or` to construct default value
56 --> tests/ui/or_fun_call.rs:84:18
56 --> tests/ui/or_fun_call.rs:85:18
5757 |
5858LL | real_default.unwrap_or(<FakeDefault as Default>::default());
5959 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
6060
6161error: use of `unwrap_or` to construct default value
62 --> tests/ui/or_fun_call.rs:88:14
62 --> tests/ui/or_fun_call.rs:89:14
6363 |
6464LL | with_vec.unwrap_or(vec![]);
6565 | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
6666
6767error: function call inside of `unwrap_or`
68 --> tests/ui/or_fun_call.rs:92:21
68 --> tests/ui/or_fun_call.rs:93:21
6969 |
7070LL | without_default.unwrap_or(Foo::new());
7171 | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)`
7272
7373error: use of `or_insert` to construct default value
74 --> tests/ui/or_fun_call.rs:96:19
74 --> tests/ui/or_fun_call.rs:97:19
7575 |
7676LL | map.entry(42).or_insert(String::new());
7777 | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()`
7878
7979error: use of `or_insert` to construct default value
80 --> tests/ui/or_fun_call.rs:100:23
80 --> tests/ui/or_fun_call.rs:101:23
8181 |
8282LL | map_vec.entry(42).or_insert(vec![]);
8383 | ^^^^^^^^^^^^^^^^^ help: try: `or_default()`
8484
8585error: use of `or_insert` to construct default value
86 --> tests/ui/or_fun_call.rs:104:21
86 --> tests/ui/or_fun_call.rs:105:21
8787 |
8888LL | btree.entry(42).or_insert(String::new());
8989 | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()`
9090
9191error: use of `or_insert` to construct default value
92 --> tests/ui/or_fun_call.rs:108:25
92 --> tests/ui/or_fun_call.rs:109:25
9393 |
9494LL | btree_vec.entry(42).or_insert(vec![]);
9595 | ^^^^^^^^^^^^^^^^^ help: try: `or_default()`
9696
9797error: use of `unwrap_or` to construct default value
98 --> tests/ui/or_fun_call.rs:112:21
98 --> tests/ui/or_fun_call.rs:113:21
9999 |
100100LL | let _ = stringy.unwrap_or(String::new());
101101 | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
102102
103103error: function call inside of `ok_or`
104 --> tests/ui/or_fun_call.rs:117:17
104 --> tests/ui/or_fun_call.rs:118:17
105105 |
106106LL | let _ = opt.ok_or(format!("{} world.", hello));
107107 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))`
108108
109109error: function call inside of `unwrap_or`
110 --> tests/ui/or_fun_call.rs:122:21
110 --> tests/ui/or_fun_call.rs:123:21
111111 |
112112LL | let _ = Some(1).unwrap_or(map[&1]);
113113 | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])`
114114
115115error: function call inside of `unwrap_or`
116 --> tests/ui/or_fun_call.rs:125:21
116 --> tests/ui/or_fun_call.rs:126:21
117117 |
118118LL | let _ = Some(1).unwrap_or(map[&1]);
119119 | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])`
120120
121121error: function call inside of `or`
122 --> tests/ui/or_fun_call.rs:150:35
122 --> tests/ui/or_fun_call.rs:151:35
123123 |
124124LL | let _ = Some("a".to_string()).or(Some("b".to_string()));
125125 | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))`
126126
127127error: function call inside of `unwrap_or`
128 --> tests/ui/or_fun_call.rs:193:18
128 --> tests/ui/or_fun_call.rs:194:18
129129 |
130130LL | None.unwrap_or(ptr_to_ref(s));
131131 | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))`
132132
133133error: function call inside of `unwrap_or`
134 --> tests/ui/or_fun_call.rs:201:14
134 --> tests/ui/or_fun_call.rs:202:14
135135 |
136136LL | None.unwrap_or(unsafe { ptr_to_ref(s) });
137137 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
138138
139139error: function call inside of `unwrap_or`
140 --> tests/ui/or_fun_call.rs:204:14
140 --> tests/ui/or_fun_call.rs:205:14
141141 |
142142LL | None.unwrap_or( unsafe { ptr_to_ref(s) } );
143143 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
144144
145145error: function call inside of `map_or`
146 --> tests/ui/or_fun_call.rs:280:25
146 --> tests/ui/or_fun_call.rs:281:25
147147 |
148148LL | let _ = Some(4).map_or(g(), |v| v);
149149 | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)`
150150
151151error: function call inside of `map_or`
152 --> tests/ui/or_fun_call.rs:282:25
152 --> tests/ui/or_fun_call.rs:283:25
153153 |
154154LL | let _ = Some(4).map_or(g(), f);
155155 | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)`
156156
157157error: use of `unwrap_or_else` to construct default value
158 --> tests/ui/or_fun_call.rs:314:18
158 --> tests/ui/or_fun_call.rs:315:18
159159 |
160160LL | with_new.unwrap_or_else(Vec::new);
161161 | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
162162
163163error: use of `unwrap_or_else` to construct default value
164 --> tests/ui/or_fun_call.rs:318:28
164 --> tests/ui/or_fun_call.rs:319:28
165165 |
166166LL | with_default_trait.unwrap_or_else(Default::default);
167167 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
168168
169169error: use of `unwrap_or_else` to construct default value
170 --> tests/ui/or_fun_call.rs:322:27
170 --> tests/ui/or_fun_call.rs:323:27
171171 |
172172LL | with_default_type.unwrap_or_else(u64::default);
173173 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
174174
175175error: use of `unwrap_or_else` to construct default value
176 --> tests/ui/or_fun_call.rs:326:22
176 --> tests/ui/or_fun_call.rs:327:22
177177 |
178178LL | real_default.unwrap_or_else(<FakeDefault as Default>::default);
179179 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
180180
181181error: use of `or_insert_with` to construct default value
182 --> tests/ui/or_fun_call.rs:330:23
182 --> tests/ui/or_fun_call.rs:331:23
183183 |
184184LL | map.entry(42).or_insert_with(String::new);
185185 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()`
186186
187187error: use of `or_insert_with` to construct default value
188 --> tests/ui/or_fun_call.rs:334:25
188 --> tests/ui/or_fun_call.rs:335:25
189189 |
190190LL | btree.entry(42).or_insert_with(String::new);
191191 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()`
192192
193193error: use of `unwrap_or_else` to construct default value
194 --> tests/ui/or_fun_call.rs:338:25
194 --> tests/ui/or_fun_call.rs:339:25
195195 |
196196LL | let _ = stringy.unwrap_or_else(String::new);
197197 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
198198
199199error: function call inside of `unwrap_or`
200 --> tests/ui/or_fun_call.rs:380:17
200 --> tests/ui/or_fun_call.rs:381:17
201201 |
202202LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)`
203203 | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)`
204204
205205error: function call inside of `unwrap_or`
206 --> tests/ui/or_fun_call.rs:385:17
206 --> tests/ui/or_fun_call.rs:386:17
207207 |
208208LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)`
209209 | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)`
210210
211211error: function call inside of `unwrap_or`
212 --> tests/ui/or_fun_call.rs:390:17
212 --> tests/ui/or_fun_call.rs:391:17
213213 |
214214LL | let _ = opt.unwrap_or({
215215 | _________________^
......@@ -229,22 +229,40 @@ LL ~ });
229229 |
230230
231231error: function call inside of `map_or`
232 --> tests/ui/or_fun_call.rs:396:17
232 --> tests/ui/or_fun_call.rs:397:17
233233 |
234234LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)`
235235 | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)`
236236
237237error: use of `unwrap_or` to construct default value
238 --> tests/ui/or_fun_call.rs:401:17
238 --> tests/ui/or_fun_call.rs:402:17
239239 |
240240LL | let _ = opt.unwrap_or({ i32::default() });
241241 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
242242
243243error: function call inside of `unwrap_or`
244 --> tests/ui/or_fun_call.rs:408:21
244 --> tests/ui/or_fun_call.rs:409:21
245245 |
246246LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() });
247247 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })`
248248
249error: aborting due to 38 previous errors
249error: function call inside of `map_or`
250 --> tests/ui/or_fun_call.rs:424:19
251 |
252LL | let _ = x.map_or(g(), |v| v);
253 | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)`
254
255error: function call inside of `map_or`
256 --> tests/ui/or_fun_call.rs:426:19
257 |
258LL | let _ = x.map_or(g(), f);
259 | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)`
260
261error: function call inside of `get_or_insert`
262 --> tests/ui/or_fun_call.rs:438:15
263 |
264LL | let _ = x.get_or_insert(g());
265 | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)`
266
267error: aborting due to 41 previous errors
250268
src/tools/clippy/tests/ui/question_mark.fixed+12
......@@ -453,3 +453,15 @@ fn const_in_pattern(x: Option<(i32, i32)>) -> Option<()> {
453453
454454 None
455455}
456
457fn issue_13642(x: Option<i32>) -> Option<()> {
458 let Some(x) = x else {
459 #[cfg(false)]
460 panic!();
461
462 #[cfg(true)]
463 return None;
464 };
465
466 None
467}
src/tools/clippy/tests/ui/question_mark.rs+12
......@@ -549,3 +549,15 @@ fn const_in_pattern(x: Option<(i32, i32)>) -> Option<()> {
549549
550550 None
551551}
552
553fn issue_13642(x: Option<i32>) -> Option<()> {
554 let Some(x) = x else {
555 #[cfg(false)]
556 panic!();
557
558 #[cfg(true)]
559 return None;
560 };
561
562 None
563}
src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.rs+13
......@@ -21,3 +21,16 @@ fn main() {
2121 let _: String = format!("{:?}", os_str); //~ unnecessary_debug_formatting
2222 let _: String = format!("{:?}", os_string); //~ unnecessary_debug_formatting
2323}
24
25#[clippy::msrv = "1.86"]
26fn msrv_1_86() {
27 let os_str = OsStr::new("test");
28 println!("{:?}", os_str);
29}
30
31#[clippy::msrv = "1.87"]
32fn msrv_1_87() {
33 let os_str = OsStr::new("test");
34 println!("{:?}", os_str);
35 //~^ unnecessary_debug_formatting
36}
src/tools/clippy/tests/ui/unnecessary_os_str_debug_formatting.stderr+10-1
......@@ -54,5 +54,14 @@ LL | let _: String = format!("{:?}", os_string);
5454 = help: use `Display` formatting and change this to `os_string.display()`
5555 = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed
5656
57error: aborting due to 6 previous errors
57error: unnecessary `Debug` formatting in `println!` args
58 --> tests/ui/unnecessary_os_str_debug_formatting.rs:34:22
59 |
60LL | println!("{:?}", os_str);
61 | ^^^^^^
62 |
63 = help: use `Display` formatting and change this to `os_str.display()`
64 = note: switching to `Display` formatting will change how the value is shown; escaped characters will no longer be escaped and surrounding quotes will be removed
65
66error: aborting due to 7 previous errors
5867
src/tools/clippy/tests/ui/wildcard_enum_match_arm.fixed+29
......@@ -90,6 +90,21 @@ fn main() {
9090 _ => {},
9191 }
9292
93 {
94 pub enum Enum {
95 A,
96 B,
97 C(u8),
98 D(u8, u8),
99 E { e: u8 },
100 };
101 match Enum::A {
102 Enum::A => (),
103 Enum::B | Enum::C(_) | Enum::D(..) | Enum::E { .. } => (),
104 //~^ wildcard_enum_match_arm
105 }
106 }
107
93108 {
94109 #![allow(clippy::manual_non_exhaustive)]
95110 pub enum Enum {
......@@ -105,3 +120,17 @@ fn main() {
105120 }
106121 }
107122}
123
124fn issue15091() {
125 enum Foo {
126 A,
127 B,
128 C,
129 }
130
131 match Foo::A {
132 Foo::A => {},
133 r#type @ Foo::B | r#type @ Foo::C => {},
134 //~^ wildcard_enum_match_arm
135 }
136}
src/tools/clippy/tests/ui/wildcard_enum_match_arm.rs+29
......@@ -90,6 +90,21 @@ fn main() {
9090 _ => {},
9191 }
9292
93 {
94 pub enum Enum {
95 A,
96 B,
97 C(u8),
98 D(u8, u8),
99 E { e: u8 },
100 };
101 match Enum::A {
102 Enum::A => (),
103 _ => (),
104 //~^ wildcard_enum_match_arm
105 }
106 }
107
93108 {
94109 #![allow(clippy::manual_non_exhaustive)]
95110 pub enum Enum {
......@@ -105,3 +120,17 @@ fn main() {
105120 }
106121 }
107122}
123
124fn issue15091() {
125 enum Foo {
126 A,
127 B,
128 C,
129 }
130
131 match Foo::A {
132 Foo::A => {},
133 r#type => {},
134 //~^ wildcard_enum_match_arm
135 }
136}
src/tools/clippy/tests/ui/wildcard_enum_match_arm.stderr+13-1
......@@ -37,8 +37,20 @@ LL | _ => {},
3737error: wildcard match will also match any future added variants
3838 --> tests/ui/wildcard_enum_match_arm.rs:103:13
3939 |
40LL | _ => (),
41 | ^ help: try: `Enum::B | Enum::C(_) | Enum::D(..) | Enum::E { .. }`
42
43error: wildcard match will also match any future added variants
44 --> tests/ui/wildcard_enum_match_arm.rs:118:13
45 |
4046LL | _ => (),
4147 | ^ help: try: `Enum::B | Enum::__Private`
4248
43error: aborting due to 6 previous errors
49error: wildcard match will also match any future added variants
50 --> tests/ui/wildcard_enum_match_arm.rs:133:9
51 |
52LL | r#type => {},
53 | ^^^^^^ help: try: `r#type @ Foo::B | r#type @ Foo::C`
54
55error: aborting due to 8 previous errors
4456
src/tools/clippy/tests/versioncheck.rs+1
......@@ -27,6 +27,7 @@ fn consistent_clippy_crate_versions() {
2727 "clippy_config/Cargo.toml",
2828 "clippy_lints/Cargo.toml",
2929 "clippy_utils/Cargo.toml",
30 "declare_clippy_lint/Cargo.toml",
3031 ];
3132
3233 for path in paths {
src/tools/clippy/triagebot.toml+3
......@@ -17,6 +17,9 @@ allow-unauthenticated = [
1717
1818[issue-links]
1919
20[mentions."clippy_lints/src/doc"]
21cc = ["@notriddle"]
22
2023# Prevents mentions in commits to avoid users being spammed
2124[no-mentions]
2225
src/tools/clippy/util/versions.py+3-3
......@@ -6,11 +6,11 @@ import os
66import sys
77
88def key(v):
9 if v == "master":
10 return sys.maxsize
119 if v == "stable":
12 return sys.maxsize - 1
10 return sys.maxsize
1311 if v == "beta":
12 return sys.maxsize - 1
13 if v == "master":
1414 return sys.maxsize - 2
1515 if v == "pre-1.29.0":
1616 return -1